Commit 0818ce00 by YuleiLan

模版管理。

parent 41ec6a93
Showing with 3266 additions and 13 deletions
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
], ],
"dependencies": { "dependencies": {
"@riophae/vue-treeselect": "0.4.0", "@riophae/vue-treeselect": "0.4.0",
"ace-builds": "^1.4.12",
"axios": "0.18.1", "axios": "0.18.1",
"clipboard": "2.0.4", "clipboard": "2.0.4",
"codemirror": "5.45.0", "codemirror": "5.45.0",
...@@ -46,6 +47,7 @@ ...@@ -46,6 +47,7 @@
"echarts": "4.2.1", "echarts": "4.2.1",
"element-ui": "2.11.1", "element-ui": "2.11.1",
"file-saver": "2.0.1", "file-saver": "2.0.1",
"form-making": "^1.2.8",
"fuse.js": "3.4.4", "fuse.js": "3.4.4",
"js-cookie": "2.2.0", "js-cookie": "2.2.0",
"jsonlint": "1.6.3", "jsonlint": "1.6.3",
...@@ -54,14 +56,19 @@ ...@@ -54,14 +56,19 @@
"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",
"qiniu-js": "^3.0.3",
"screenfull": "4.2.0", "screenfull": "4.2.0",
"showdown": "^1.9.1", "showdown": "^1.9.1",
"solarlunar": "^2.0.7", "solarlunar": "^2.0.7",
"sortablejs": "1.8.4", "sortablejs": "1.8.4",
"tui-editor": "1.3.3", "tui-editor": "1.3.3",
"viewerjs": "^1.6.1",
"vue": "2.6.10", "vue": "2.6.10",
"vue-codemirror-lite": "^1.0.4",
"vue-count-to": "1.0.13", "vue-count-to": "1.0.13",
"vue-cropper": "^0.5.0", "vue-cropper": "^0.5.0",
"vue-i18n": "^5.0.3",
"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",
......
<template>
<div id="app">
<div class="fm-header">
<img class="fm-logo" src="./assets/logo.png">
<div class="fm-title" @click="handleHome">{{ $t('header.title') }}</div>
<iframe style="vertical-align: middle;margin-top:10px;margin-left: 10px;" src="https://ghbtns.com/github-btn.html?user=GavinZhulei&repo=vue-form-making&type=star&count=true" frameborder="0" scrolling="0" width="160px" height="30px" />
<div class="fm-link">
<a target="_blank" href="http://form.xiaoyaoji.cn/pricing">{{ $t('header.pricing') }}</a>
<a target="_blank" href="http://docs.form.xiaoyaoji.cn">{{ $t('header.document') }}</a>
<a v-if="$lang == 'zh-CN'" target="_blank" href="http://docs.form.xiaoyaoji.cn/zh/other/course.html">学习课程</a>
<a target="_blank" href="https://github.com/GavinZhuLei/vue-form-making">GitHub</a>
<div class="action-item">
<el-dropdown trigger="click" @command="handleLangCommand">
<span class="el-dropdown-link">
{{ $route.params.lang == 'zh-CN' ? '简体中文' : 'English' }}<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="zh-CN">简体中文</el-dropdown-item>
<el-dropdown-item command="en-US">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<a class="ad" href="http://form.xiaoyaoji.cn" target="_blank">{{ $t('header.advanced') }}</a>
<a v-if="$lang == 'zh-CN'" class="ad" href="http://www.xiaoyaoji.cn" target="_blank">小幺鸡接口工具</a>
</div>
</div>
<div class="fm-container"><router-view /></div>
</div>
</template>
<script>
import Vue from 'vue'
export default {
name: 'App',
methods: {
handleHome() {
this.$router.push({ path: '/' })
},
handleLangCommand(command) {
this.$router.replace({ name: this.$route.name, params: { lang: command }})
}
}
}
</script>
<style lang="scss">
.fm-header{
height: 50px;
box-shadow: 0 2px 10px rgba(70,160,252, 0.6);
padding: 0 10px;
background-image: linear-gradient(to right,#1278f6,#00b4aa);
position: relative;
.fm-logo{
height: 26px;
vertical-align: middle;
}
.fm-title{
display: inline-block;
line-height: 50px;
vertical-align: middle;
color: #fff;
font-size: 20px;
font-weight: 600;
opacity: 0.8;
margin-left: 6px;
cursor: pointer;
}
.fm-link{
height: 50px;
float: right;
a{
color: #fff;
text-decoration: none;
font-size: 14px;
line-height: 50px;
font-weight: 500;
margin-left: 15px;
&:hover{
opacity: 0.8;
}
&.ad{
color: #f5dab1;
}
}
.action-item{
display: inline-block;
margin-left: 15px;
.el-dropdown{
// font-size: 16px;
// font-weight: 500;
}
.el-dropdown-link{
cursor: pointer;
color: #fff;
&:hover{
opacity: 0.8;
}
}
&.action-item-user{
.el-dropdown-link{
color: #f5dab1;
}
}
}
}
}
.fm-container{
height: calc(100% - 50px);
}
*, :after, :before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html,body{
height: 100%;
}
#app {
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
min-height: 100%;
height: 100%;
}
</style>
<template>
<el-dialog
:id="id"
ref="elDialog"
class="cus-dialog-container"
:title="title"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
append-to-body
center
:width="width"
>
<span v-if="show">
<slot />
</span>
<span
v-if="action"
slot="footer"
v-loading="loading"
class="dialog-footer"
:element-loading-text="loadingText"
>
<slot name="action">
<el-button @click="close">{{ $t('fm.actions.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ $t('fm.actions.confirm') }}</el-button>
</slot>
</span>
</el-dialog>
</template>
<script>
export default {
props: {
visible: Boolean,
loadingText: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
width: {
type: String,
default: '600px'
},
form: {
type: Boolean,
default: true
},
action: {
type: Boolean,
default: true
}
},
data() {
return {
loading: false,
dialogVisible: this.visible,
id: 'dialog_' + new Date().getTime(),
showForm: false
}
},
computed: {
show() {
if (this.form) {
return this.showForm
} else {
return true
}
}
},
watch: {
dialogVisible(val) {
if (!val) {
this.loading = false
this.$emit('on-close')
setTimeout(() => {
this.showForm = false
}, 300)
} else {
this.showForm = true
}
},
visible(val) {
this.dialogVisible = val
}
},
mounted() {
},
methods: {
close() {
this.dialogVisible = false
},
submit() {
this.loading = true
this.$emit('on-submit')
},
end() {
this.loading = false
}
}
}
</script>
<style lang="scss">
.cus-dialog-container{
.el-dialog__footer{
margin: 0 20px;
// border-top: 1px dashed #ccc;
padding: 15px 0 16px;
text-align: center;
position: relative;
.dialog-footer{
display: block;
.circular{
display: inline-block;
vertical-align: middle;
margin-right: 5px;
width: 24px;
height: 24px;
}
.el-loading-text{
display: inline-block;
vertical-align: middle;
}
.el-loading-spinner{
margin-top: -12px;
}
}
}
}
</style>
<template>
<div class="form-config-container">
<el-form label-position="top">
<el-form-item :label="$t('fm.config.form.labelPosition.title')">
<el-radio-group v-model="data.labelPosition">
<el-radio-button label="left">{{ $t('fm.config.form.labelPosition.left') }}</el-radio-button>
<el-radio-button label="right">{{ $t('fm.config.form.labelPosition.right') }}</el-radio-button>
<el-radio-button label="top">{{ $t('fm.config.form.labelPosition.top') }}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('fm.config.form.labelWidth')">
<el-input-number v-model="data.labelWidth" :min="0" :max="200" :step="10" />
</el-form-item>
<el-form-item :label="$t('fm.config.form.size')">
<el-radio-group v-model="data.size">
<el-radio-button label="medium">medium</el-radio-button>
<el-radio-button label="small">small</el-radio-button>
<el-radio-button label="mini">mini</el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
/* eslint-disable */
props: ['data']
}
</script>
<template>
<div>
<el-form
ref="generateForm"
label-suffix=":"
:size="data.config.size"
:model="models"
:rules="rules"
:label-position="data.config.labelPosition"
:label-width="data.config.labelWidth + 'px'"
>
<template v-for="item in data.list">
<template v-if="item.type == 'grid'">
<el-row
:key="item.key"
type="flex"
:gutter="item.options.gutter ? item.options.gutter : 0"
:justify="item.options.justify"
:align="item.options.align"
>
<el-col v-for="(col, colIndex) in item.columns" :key="colIndex" :span="col.span">
<template v-for="citem in col.list">
<el-form-item v-if="citem.type=='blank'" :key="citem.key" :label="citem.name" :prop="citem.model">
<slot :name="citem.model" :model="models" />
</el-form-item>
<genetate-form-item
v-else
:key="citem.key"
:models.sync="models"
:remote="remote"
:rules="rules"
:widget="citem"
@input-change="onInputChange"
:data="data"
/>
</template>
</el-col>
</el-row>
</template>
<template v-else-if="item.type == 'blank'">
<el-form-item :key="item.key" :label="item.name" :prop="item.model">
<slot :name="item.model" :model="models" />
</el-form-item>
</template>
<template v-else>
<genetate-form-item
:key="item.key"
:models.sync="models"
:rules="rules"
:widget="item"
:remote="remote"
:data="data"
@input-change="onInputChange"
/>
</template>
</template>
</el-form>
</div>
</template>
<script>
import GenetateFormItem from './GenerateFormItem'
export default {
name: 'FmGenerateForm',
components: {
GenetateFormItem
},
/* eslint-disable */
props: ['data', 'remote', 'value', 'insite'],
data() {
return {
models: {},
rules: {}
}
},
watch: {
data: {
deep: true,
handler(val) {
this.generateModle(val.list)
}
},
value: {
deep: true,
handler(val) {
this.models = { ...this.models, ...val }
}
}
},
created() {
this.generateModle(this.data.list)
},
mounted() {
},
methods: {
generateModle(genList) {
for (let i = 0; i < genList.length; i++) {
if (genList[i].type === 'grid') {
genList[i].columns.forEach(item => {
this.generateModle(item.list)
})
} else {
if (this.value && Object.keys(this.value).indexOf(genList[i].model) >= 0) {
this.models[genList[i].model] = this.value[genList[i].model]
} else {
if (genList[i].type === 'blank') {
this.$set(this.models, genList[i].model, genList[i].options.defaultType === 'String' ? '' : (genList[i].options.defaultType === 'Object' ? {} : []))
} else {
this.models[genList[i].model] = genList[i].options.defaultValue
}
}
if (this.rules[genList[i].model]) {
this.rules[genList[i].model] = [...this.rules[genList[i].model], ...genList[i].rules.map(item => {
if (item.pattern) {
return { ...item, pattern: eval(item.pattern) }
} else {
return { ...item }
}
})]
} else {
this.rules[genList[i].model] = [...genList[i].rules.map(item => {
if (item.pattern) {
return { ...item, pattern: eval(item.pattern) }
} else {
return { ...item }
}
})]
}
}
}
},
getData() {
return new Promise((resolve, reject) => {
this.$refs.generateForm.validate(valid => {
if (valid) {
resolve(this.models)
} else {
reject(new Error(this.$t('fm.message.validError')).message)
}
})
})
},
reset() {
this.$refs.generateForm.resetFields()
},
onInputChange(value, field) {
this.$emit('on-change', field, value, this.models)
},
refresh() {
}
}
}
</script>
<style lang="scss">
// @import '../styles/cover.scss';
</style>
<template>
<el-form-item
:label-width="widget.type==='divider' || (widget.type==='text' && widget.options.textLabelStatus===false)?'0px':data.config.labelWidth + 'px'"
:label="widget.type==='divider' || (widget.type==='text' && widget.options.textLabelStatus===false)?'':widget.name"
:prop="widget.model"
>
<template v-if="widget.type == 'input'">
<el-input
v-if="widget.options.dataType == 'number' || widget.options.dataType == 'integer' || widget.options.dataType == 'float'"
v-model.number="dataModel"
:type="widget.options.dataType"
:placeholder="widget.options.placeholder"
:style="{width: widget.options.width}"
:disabled="widget.options.disabled"
/>
<el-input
v-else
v-model="dataModel"
:type="widget.options.dataType"
:disabled="widget.options.disabled"
:placeholder="widget.options.placeholder"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type == 'textarea'">
<el-input
v-model="dataModel"
type="textarea"
:rows="5"
:disabled="widget.options.disabled"
:placeholder="widget.options.placeholder"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type == 'number'">
<el-input-number
v-model="dataModel"
:style="{width: widget.options.width}"
:step="widget.options.step"
controls-position="right"
:disabled="widget.options.disabled"
/>
</template>
<template v-if="widget.type == 'radio'">
<el-radio-group
v-model="dataModel"
:style="{width: widget.options.width}"
:disabled="widget.options.disabled"
>
<el-radio
v-for="(item, index) in (widget.options.remote ? widget.options.remoteOptions : widget.options.options)"
:key="index"
:style="{display: widget.options.inline ? 'inline-block' : 'block'}"
:label="item.value"
>
<template v-if="widget.options.remote">{{ item.label }}</template>
<template v-else>{{ widget.options.showLabel ? item.label : item.value }}</template>
</el-radio>
</el-radio-group>
</template>
<template v-if="widget.type == 'checkbox'">
<el-checkbox-group
v-model="dataModel"
:style="{width: widget.options.width}"
:disabled="widget.options.disabled"
>
<el-checkbox
v-for="(item, index) in (widget.options.remote ? widget.options.remoteOptions : widget.options.options)"
:key="index"
:style="{display: widget.options.inline ? 'inline-block' : 'block'}"
:label="item.value"
>
<template v-if="widget.options.remote">{{ item.label }}</template>
<template v-else>{{ widget.options.showLabel ? item.label : item.value }}</template>
</el-checkbox>
</el-checkbox-group>
</template>
<template v-if="widget.type == 'time'">
<el-time-picker
v-model="dataModel"
:is-range="widget.options.isRange"
:placeholder="widget.options.placeholder"
:start-placeholder="widget.options.startPlaceholder"
:end-placeholder="widget.options.endPlaceholder"
:readonly="widget.options.readonly"
:disabled="widget.options.disabled"
:editable="widget.options.editable"
:clearable="widget.options.clearable"
:arrow-control="widget.options.arrowControl"
:value-format="widget.options.format"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type=='date'">
<el-date-picker
v-model="dataModel"
:type="widget.options.type"
:placeholder="widget.options.placeholder"
:start-placeholder="widget.options.startPlaceholder"
:end-placeholder="widget.options.endPlaceholder"
:readonly="widget.options.readonly"
:disabled="widget.options.disabled"
:editable="widget.options.editable"
:clearable="widget.options.clearable"
:value-format="widget.options.timestamp ? 'timestamp' : widget.options.format"
:format="widget.options.format"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type =='rate'">
<el-rate
v-model="dataModel"
:max="widget.options.max"
:disabled="widget.options.disabled"
:allow-half="widget.options.allowHalf"
/>
</template>
<template v-if="widget.type == 'color'">
<el-color-picker
v-model="dataModel"
:disabled="widget.options.disabled"
:show-alpha="widget.options.showAlpha"
/>
</template>
<template v-if="widget.type == 'select'">
<el-select
v-model="dataModel"
:disabled="widget.options.disabled"
:multiple="widget.options.multiple"
:clearable="widget.options.clearable"
:placeholder="widget.options.placeholder"
:style="{width: widget.options.width}"
:filterable="widget.options.filterable"
>
<el-option v-for="item in (widget.options.remote ? widget.options.remoteOptions : widget.options.options)" :key="item.value" :value="item.value" :label="widget.options.showLabel || widget.options.remote?item.label:item.value" />
</el-select>
</template>
<template v-if="widget.type=='switch'">
<el-switch
v-model="dataModel"
:disabled="widget.options.disabled"
/>
</template>
<template v-if="widget.type=='slider'">
<el-slider
v-model="dataModel"
:min="widget.options.min"
:max="widget.options.max"
:disabled="widget.options.disabled"
:step="widget.options.step"
:show-input="widget.options.showInput"
:range="widget.options.range"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type=='imgupload'">
<fm-upload
v-model="dataModel"
:disabled="widget.options.disabled"
:style="{'width': widget.options.width}"
:width="widget.options.size.width"
:height="widget.options.size.height"
:token="widget.options.token"
:domain="widget.options.domain"
:multiple="widget.options.multiple"
:length="widget.options.length"
:is-qiniu="widget.options.isQiniu"
:is-delete="widget.options.isDelete"
:min="widget.options.min"
:is-edit="widget.options.isEdit"
:action="widget.options.action"
/>
</template>
<template v-if="widget.type == 'editor'">
<vue-editor
v-model="dataModel"
:style="{width: widget.options.width}"
/>
</template>
<template v-if="widget.type === 'cascader'">
<el-cascader
v-model="dataModel"
:disabled="widget.options.disabled"
:clearable="widget.options.clearable"
:placeholder="widget.options.placeholder"
:style="{width: widget.options.width}"
:options="widget.options.remoteOptions"
/>
</template>
<template v-if="widget.type === 'text'">
<span
:style="{
'font-size': widget.options.font_size,
'font-family': widget.options.font_family,
'font-weight': widget.options.font_weight,
'color': widget.options.font_color
}"
>
{{ widget.options.defaultValue }}
</span>
</template>
<template v-if="widget.type === 'divider'">
<el-divider
:direction="widget.options.direction"
:content-position="widget.options.content_position"
>
<span
:style="{
'font-size': widget.options.font_size,
'font-family': widget.options.font_family,
'font-weight': widget.options.font_weight,
'color': widget.options.font_color
}"
>
{{ widget.options.defaultValue }}
</span>
</el-divider>
</template>
</el-form-item>
</template>
<script>
import FmUpload from './Upload'
export default {
components: {
FmUpload
},
/* eslint-disable */
props: ['widget', 'models', 'rules', 'remote', 'data'],
data() {
return {
dataModel: this.models[this.widget.model]
}
},
watch: {
dataModel: {
deep: true,
handler(val) {
this.models[this.widget.model] = val
this.$emit('update:models', {
...this.models,
[this.widget.model]: val
})
this.$emit('input-change', val, this.widget.model)
}
},
models: {
deep: true,
handler(val) {
this.dataModel = val[this.widget.model]
}
}
},
created() {
if (this.widget.options.remote && this.remote[this.widget.options.remoteFunc]) {
this.remote[this.widget.options.remoteFunc]((data) => {
this.widget.options.remoteOptions = data.map(item => {
return {
value: item[this.widget.options.props.value],
label: item[this.widget.options.props.label],
children: item[this.widget.options.props.children]
}
})
})
}
if (this.widget.type === 'imgupload' && this.widget.options.isQiniu) {
this.remote[this.widget.options.tokenFunc]((data) => {
this.widget.options.token = data
})
}
},
methods: {
}
}
</script>
<template>
<div class="widget-form-container">
<div v-if="data.list.length == 0" class="form-empty">{{ $t('fm.description.containerEmpty') }}</div>
<el-form :size="data.config.size" label-suffix=":" :label-position="data.config.labelPosition" :label-width="data.config.labelWidth + 'px'">
<draggable
v-model="data.list"
class=""
v-bind="{group:'people', ghostClass: 'ghost',animation: 200, handle: '.drag-widget'}"
@end="handleMoveEnd"
@add="handleWidgetAdd"
>
<transition-group name="fade" tag="div" class="widget-form-list">
<template v-for="(element, index) in data.list">
<template v-if="element.type == 'grid'">
<el-row
v-if="element && element.key"
:key="element.key"
class="widget-col widget-view"
type="flex"
:class="{active: selectWidget.key == element.key}"
:gutter="element.options.gutter ? element.options.gutter : 0"
:justify="element.options.justify"
:align="element.options.align"
@click.native="handleSelectWidget(index)"
>
<el-col v-for="(col, colIndex) in element.columns" :key="colIndex" :span="col.span ? col.span : 0">
<draggable
v-model="col.list"
:no-transition-on-drag="true"
v-bind="{group:'people', ghostClass: 'ghost',animation: 200, handle: '.drag-widget'}"
@end="handleMoveEnd"
@add="handleWidgetColAdd($event, element, colIndex)"
>
<transition-group name="fade" tag="div" class="widget-col-list">
<widget-form-item
v-for="(el, i) in col.list"
v-if="el.key"
:key="el.key"
:element="el"
:select.sync="selectWidget"
:index="i"
:data="col"
:data-config="data"
/>
</transition-group>
</draggable>
</el-col>
<div v-if="selectWidget.key == element.key" class="widget-view-action widget-col-action">
<i class="iconfont icon-trash" @click.stop="handleWidgetDelete(index)" />
</div>
<div v-if="selectWidget.key == element.key" class="widget-view-drag widget-col-drag">
<i class="iconfont icon-drag drag-widget" />
</div>
</el-row>
</template>
<template v-else>
<widget-form-item
v-if="element && element.key"
:key="element.key"
:element="element"
:select.sync="selectWidget"
:index="index"
:data-config="data"
:data="data"
/>
</template>
</template>
</transition-group>
</draggable>
</el-form>
</div>
</template>
<script>
import Draggable from 'vuedraggable'
import WidgetFormItem from './WidgetFormItem'
export default {
components: {
Draggable,
WidgetFormItem
},
/* eslint-disable */
props: ['data', 'select'],
data() {
return {
selectWidget: this.select
}
},
watch: {
select(val) {
this.selectWidget = val
},
selectWidget: {
handler(val) {
this.$emit('update:select', val)
},
deep: true
}
},
mounted() {
document.body.ondrop = function(event) {
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
if (isFirefox) {
event.preventDefault()
event.stopPropagation()
}
}
},
methods: {
handleMoveEnd({ newIndex, oldIndex }) {
},
handleSelectWidget(index) {
this.selectWidget = this.data.list[index]
},
handleWidgetAdd(evt) {
const newIndex = evt.newIndex
const to = evt.to
// 为拖拽到容器的元素添加唯一 key
const key = Date.parse(new Date()) + '_' + Math.ceil(Math.random() * 99999)
this.$set(this.data.list, newIndex, {
...this.data.list[newIndex],
options: {
...this.data.list[newIndex].options,
remoteFunc: 'func_' + key
},
key,
// 绑定键值
model: this.data.list[newIndex].type + '_' + key,
rules: []
})
if (this.data.list[newIndex].type === 'radio' || this.data.list[newIndex].type === 'checkbox' || this.data.list[newIndex].type === 'select') {
this.$set(this.data.list, newIndex, {
...this.data.list[newIndex],
options: {
...this.data.list[newIndex].options,
options: this.data.list[newIndex].options.options.map(item => ({
...item
}))
}
})
}
if (this.data.list[newIndex].type === 'grid') {
this.$set(this.data.list, newIndex, {
...this.data.list[newIndex],
columns: this.data.list[newIndex].columns.map(item => ({ ...item }))
})
}
this.selectWidget = this.data.list[newIndex]
},
handleWidgetColAdd($event, row, colIndex) {
const newIndex = $event.newIndex
const oldIndex = $event.oldIndex
const item = $event.item
// 防止布局元素的嵌套拖拽
if (item.className.indexOf('data-grid') >= 0) {
// 如果是列表中拖拽的元素需要还原到原来位置
item.tagName === 'DIV' && this.data.list.splice(oldIndex, 0, row.columns[colIndex].list[newIndex])
row.columns[colIndex].list.splice(newIndex, 1)
return false
}
const key = Date.parse(new Date()) + '_' + Math.ceil(Math.random() * 99999)
this.$set(row.columns[colIndex].list, newIndex, {
...row.columns[colIndex].list[newIndex],
options: {
...row.columns[colIndex].list[newIndex].options,
remoteFunc: 'func_' + key
},
key,
// 绑定键值
model: row.columns[colIndex].list[newIndex].type + '_' + key,
rules: []
})
if (row.columns[colIndex].list[newIndex].type === 'radio' || row.columns[colIndex].list[newIndex].type === 'checkbox' || row.columns[colIndex].list[newIndex].type === 'select') {
this.$set(row.columns[colIndex].list, newIndex, {
...row.columns[colIndex].list[newIndex],
options: {
...row.columns[colIndex].list[newIndex].options,
options: row.columns[colIndex].list[newIndex].options.options.map(item => ({
...item
}))
}
})
}
this.selectWidget = row.columns[colIndex].list[newIndex]
},
handleWidgetDelete(index) {
if (this.data.list.length - 1 === index) {
if (index === 0) {
this.selectWidget = {}
} else {
this.selectWidget = this.data.list[index - 1]
}
} else {
this.selectWidget = this.data.list[index + 1]
}
this.$nextTick(() => {
this.data.list.splice(index, 1)
})
}
}
}
</script>
<template>
<el-form-item
v-if="element && element.key"
:label-width="element.type==='divider' || (element.type==='text' && element.options.textLabelStatus===false)?'0px':dataConfig.config.labelWidth + 'px'"
class="widget-view "
:class="{active: selectWidget.key === element.key, 'is_req': element.options.required}"
:label="element.type==='divider' || (element.type==='text' && element.options.textLabelStatus===false)?'':element.name"
@click.native.stop="handleSelectWidget(index)"
>
<template v-if="element.type == 'input'">
<el-input
v-model="element.options.defaultValue"
:style="{width: element.options.width}"
:placeholder="element.options.placeholder"
:disabled="element.options.disabled"
/>
</template>
<template v-if="element.type == 'textarea'">
<el-input
v-model="element.options.defaultValue"
type="textarea"
:rows="5"
:style="{width: element.options.width}"
:disabled="element.options.disabled"
:placeholder="element.options.placeholder"
/>
</template>
<template v-if="element.type == 'number'">
<el-input-number
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
:controls-position="element.options.controlsPosition"
:style="{width: element.options.width}"
/>
</template>
<template v-if="element.type == 'radio'">
<el-radio-group
v-model="element.options.defaultValue"
:style="{width: element.options.width}"
:disabled="element.options.disabled"
>
<el-radio
v-for="(item, index2) in element.options.options"
:key="item.value + index2"
:style="{display: element.options.inline ? 'inline-block' : 'block'}"
:label="item.value"
>
{{ element.options.showLabel ? item.label : item.value }}
</el-radio>
</el-radio-group>
</template>
<template v-if="element.type == 'checkbox'">
<el-checkbox-group
v-model="element.options.defaultValue"
:style="{width: element.options.width}"
:disabled="element.options.disabled"
>
<el-checkbox
v-for="(item, index1) in element.options.options"
:key="item.value + index1"
:style="{display: element.options.inline ? 'inline-block' : 'block'}"
:label="item.value"
>
{{ element.options.showLabel ? item.label : item.value }}
</el-checkbox>
</el-checkbox-group>
</template>
<template v-if="element.type == 'time'">
<el-time-picker
v-model="element.options.defaultValue"
:is-range="element.options.isRange"
:placeholder="element.options.placeholder"
:start-placeholder="element.options.startPlaceholder"
:end-placeholder="element.options.endPlaceholder"
:readonly="element.options.readonly"
:disabled="element.options.disabled"
:editable="element.options.editable"
:clearable="element.options.clearable"
:arrow-control="element.options.arrowControl"
:style="{width: element.options.width}"
/>
</template>
<template v-if="element.type == 'date'">
<el-date-picker
v-model="element.options.defaultValue"
:type="element.options.type"
:is-range="element.options.isRange"
:placeholder="element.options.placeholder"
:start-placeholder="element.options.startPlaceholder"
:end-placeholder="element.options.endPlaceholder"
:readonly="element.options.readonly"
:disabled="element.options.disabled"
:editable="element.options.editable"
:clearable="element.options.clearable"
:style="{width: element.options.width}"
/>
</template>
<template v-if="element.type == 'rate'">
<el-rate
v-model="element.options.defaultValue"
:max="element.options.max"
:disabled="element.options.disabled"
:allow-half="element.options.allowHalf"
/>
</template>
<template v-if="element.type == 'color'">
<el-color-picker
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
:show-alpha="element.options.showAlpha"
/>
</template>
<template v-if="element.type == 'select'">
<el-select
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
:multiple="element.options.multiple"
:clearable="element.options.clearable"
:placeholder="element.options.placeholder"
:style="{width: element.options.width}"
>
<el-option v-for="item in element.options.options" :key="item.value" :value="item.value" :label="element.options.showLabel?item.label:item.value" />
</el-select>
</template>
<template v-if="element.type=='switch'">
<el-switch
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
/>
</template>
<template v-if="element.type=='slider'">
<el-slider
v-model="element.options.defaultValue"
:min="element.options.min"
:max="element.options.max"
:disabled="element.options.disabled"
:step="element.options.step"
:show-input="element.options.showInput"
:range="element.options.range"
:style="{width: element.options.width}"
/>
</template>
<template v-if="element.type=='imgupload'">
<fm-upload
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
:style="{'width': element.options.width}"
:width="element.options.size.width"
:height="element.options.size.height"
token="xxx"
domain="xxx"
/>
</template>
<template v-if="element.type == 'cascader'">
<el-cascader
v-model="element.options.defaultValue"
:disabled="element.options.disabled"
:clearable="element.options.clearable"
:placeholder="element.options.placeholder"
:style="{width: element.options.width}"
:options="element.options.remoteOptions"
/>
</template>
<template v-if="element.type == 'editor'">
<vue-editor
v-model="element.options.defaultValue"
:style="{width: element.options.width}"
/>
</template>
<template v-if="element.type=='blank'">
<div style="height: 50px;color: #999;background: #eee;line-height:50px;text-align:center;">{{ $t('fm.components.fields.blank') }}</div>
</template>
<template v-if="element.type === 'text'">
<span
:style="{
'font-size': element.options.font_size,
'font-family': element.options.font_family,
'font-weight': element.options.font_weight,
'color': element.options.font_color
}"
>
{{ element.options.defaultValue }}
</span>
</template>
<template v-if="element.type === 'divider'">
<el-divider
:direction="element.options.direction"
:content-position="element.options.content_position"
>
<span
:style="{
'font-size': element.options.font_size,
'font-family': element.options.font_family,
'font-weight': element.options.font_weight,
'color': element.options.font_color
}"
>
{{ element.options.defaultValue }}
</span>
</el-divider>
</template>
<div v-if="selectWidget.key == element.key" class="widget-view-action">
<i class="iconfont icon-icon_clone" @click.stop="handleWidgetClone(index)" />
<i class="iconfont icon-trash" @click.stop="handleWidgetDelete(index)" />
</div>
<div v-if="selectWidget.key == element.key" class="widget-view-drag">
<i class="iconfont icon-drag drag-widget" />
</div>
</el-form-item>
</template>
<script>
import FmUpload from './Upload'
export default {
components: {
FmUpload
},
/* eslint-disable */
props: ['element', 'select', 'index', 'data', 'dataConfig'],
data() {
return {
selectWidget: this.select
}
},
watch: {
select(val) {
this.selectWidget = val
},
selectWidget: {
handler(val) {
this.$emit('update:select', val)
},
deep: true
}
},
mounted() {
},
methods: {
handleSelectWidget(index) {
this.selectWidget = this.data.list[index]
},
handleWidgetDelete(index) {
if (this.data.list.length - 1 === index) {
if (index === 0) {
this.selectWidget = {}
} else {
this.selectWidget = this.data.list[index - 1]
}
} else {
this.selectWidget = this.data.list[index + 1]
}
this.$nextTick(() => {
this.data.list.splice(index, 1)
})
},
handleWidgetClone(index) {
let cloneData = {
...this.data.list[index],
options: { ...this.data.list[index].options },
key: Date.parse(new Date()) + '_' + Math.ceil(Math.random() * 99999)
}
if (this.data.list[index].type === 'radio' || this.data.list[index].type === 'checkbox' || this.data.list[index].type === 'select') {
cloneData = {
...cloneData,
options: {
...cloneData.options,
options: cloneData.options.options.map(item => ({ ...item }))
}
}
}
this.data.list.splice(index, 0, cloneData)
this.$nextTick(() => {
this.selectWidget = this.data.list[index + 1]
})
}
}
}
</script>
export const basicComponents = [
{
type: 'input',
icon: 'icon-input',
options: {
width: '100%',
defaultValue: '',
required: false,
dataType: 'string',
pattern: '',
placeholder: '',
disabled: false
}
},
{
type: 'textarea',
icon: 'icon-diy-com-textarea',
options: {
width: '100%',
defaultValue: '',
required: false,
disabled: false,
pattern: '',
placeholder: ''
}
},
{
type: 'number',
icon: 'icon-number',
options: {
width: '',
required: false,
defaultValue: 0,
min: '',
max: '',
step: 1,
disabled: false,
controlsPosition: ''
}
},
{
type: 'radio',
icon: 'icon-radio-active',
options: {
inline: false,
defaultValue: '',
showLabel: false,
options: [
{
value: 'Option 1',
label: 'Option 1'
},
{
value: 'Option 2',
label: 'Option 2'
},
{
value: 'Option 3',
label: 'Option 3'
}
],
required: false,
width: '',
remote: false,
remoteOptions: [],
props: {
value: 'value',
label: 'label'
},
remoteFunc: '',
disabled: false
}
},
{
type: 'checkbox',
icon: 'icon-check-box',
options: {
inline: false,
defaultValue: [],
showLabel: false,
options: [
{
value: 'Option 1'
},
{
value: 'Option 2'
},
{
value: 'Option 3'
}
],
required: false,
width: '',
remote: false,
remoteOptions: [],
props: {
value: 'value',
label: 'label'
},
remoteFunc: '',
disabled: false
}
},
{
type: 'time',
icon: 'icon-time',
options: {
defaultValue: '21:19:56',
readonly: false,
disabled: false,
editable: true,
clearable: true,
placeholder: '',
startPlaceholder: '',
endPlaceholder: '',
isRange: false,
arrowControl: true,
format: 'HH:mm:ss',
required: false,
width: ''
}
},
{
type: 'date',
icon: 'icon-date',
options: {
defaultValue: '',
readonly: false,
disabled: false,
editable: true,
clearable: true,
placeholder: '',
startPlaceholder: '',
endPlaceholder: '',
type: 'date',
format: 'yyyy-MM-dd',
timestamp: false,
required: false,
width: ''
}
},
{
type: 'rate',
icon: 'icon-pingfen1',
options: {
defaultValue: null,
max: 5,
disabled: false,
allowHalf: false,
required: false
}
},
{
type: 'color',
icon: 'icon-color',
options: {
defaultValue: '',
disabled: false,
showAlpha: false,
required: false
}
},
{
type: 'select',
icon: 'icon-select',
options: {
defaultValue: '',
multiple: false,
disabled: false,
clearable: false,
placeholder: '',
required: false,
showLabel: false,
width: '',
options: [
{
value: 'Option 1'
},
{
value: 'Option 2'
}, {
value: 'Option 3'
}
],
remote: false,
filterable: false,
remoteOptions: [],
props: {
value: 'value',
label: 'label'
},
remoteFunc: ''
}
},
{
type: 'switch',
icon: 'icon-switch',
options: {
defaultValue: false,
required: false,
disabled: false
}
},
{
type: 'slider',
icon: 'icon-slider',
options: {
defaultValue: 0,
disabled: false,
required: false,
min: 0,
max: 100,
step: 1,
showInput: false,
range: false,
width: ''
}
},
{
type: 'text',
icon: 'icon-wenzishezhi-',
options: {
font_size: '15px', // 字体大小
font_color: '#606266', // 字体颜色
font_weight: '500', // 粗体
font_family: '', // 字体属性
defaultValue: 'This is a text',
textLabelStatus: true,
customClass: ''
}
}
]
export const advanceComponents = [
{
type: 'blank',
icon: 'icon-zidingyishuju',
options: {
defaultType: 'String'
}
},
{
type: 'imgupload',
icon: 'icon-tupian',
options: {
defaultValue: [],
size: {
width: 100,
height: 100
},
width: '',
tokenFunc: 'funcGetToken',
token: '',
domain: 'http://pfp81ptt6.bkt.clouddn.com/',
disabled: false,
length: 8,
multiple: false,
isQiniu: false,
isDelete: false,
min: 0,
isEdit: false,
action: 'https://jsonplaceholder.typicode.com/photos/'
}
},
{
type: 'editor',
icon: 'icon-fuwenbenkuang',
options: {
defaultValue: '',
width: ''
}
},
{
type: 'cascader',
icon: 'icon-jilianxuanze',
options: {
defaultValue: [],
width: '',
placeholder: '',
disabled: false,
clearable: false,
remote: true,
remoteOptions: [],
props: {
value: 'value',
label: 'label',
children: 'children'
},
remoteFunc: ''
}
}
]
export const layoutComponents = [
{
type: 'grid',
icon: 'icon-grid-',
columns: [
{
span: 12,
list: []
},
{
span: 12,
list: []
}
],
options: {
gutter: 0,
justify: 'start',
align: 'top'
}
},
{
type: 'divider',
icon: 'icon-input',
options: {
defaultValue: '分割线', // 字体大小
font_size: '15px', // 字体大小
font_color: '#606266', // 字体颜色
font_weight: '500', // 粗体
font_family: '', // 字体属性
direction: 'horizontal', // horizontal / vertical 设置分割线方向
content_position: 'center' // left / right / center 设置分割线文案的位置
}
}
]
function findRemoteFunc(list, funcList, tokenFuncList, blankList) {
for (let i = 0; i < list.length; i++) {
if (list[i].type === 'grid') {
list[i].columns.forEach(item => {
findRemoteFunc(item.list, funcList, tokenFuncList, blankList)
})
} else {
if (list[i].type === 'blank') {
if (list[i].model) {
blankList.push({
name: list[i].model,
label: list[i].name
})
}
} else if (list[i].type === 'imgupload') {
if (list[i].options.tokenFunc) {
tokenFuncList.push({
func: list[i].options.tokenFunc,
label: list[i].name,
model: list[i].model
})
}
} else {
if (list[i].options.remote && list[i].options.remoteFunc) {
funcList.push({
func: list[i].options.remoteFunc,
label: list[i].name,
model: list[i].model
})
}
}
}
}
}
export default function(data) {
const funcList = []
const tokenFuncList = []
const blankList = []
findRemoteFunc(JSON.parse(data).list, funcList, tokenFuncList, blankList)
let funcTemplate = ''
let blankTemplate = ''
for (let i = 0; i < funcList.length; i++) {
funcTemplate += `
${funcList[i].func} (resolve) {
// ${funcList[i].label} ${funcList[i].model}
// Call callback function once get the data from remote server
// resolve(data)
},
`
}
for (let i = 0; i < tokenFuncList.length; i++) {
funcTemplate += `
${tokenFuncList[i].func} (resolve) {
// ${tokenFuncList[i].label} ${tokenFuncList[i].model}
// Call callback function once get the token
// resolve(token)
},
`
}
for (let i = 0; i < blankList.length; i++) {
blankTemplate += `
<template slot="${blankList[i].name}" slot-scope="scope">
<!-- ${blankList[i].label} -->
<!-- use v-model="scope.model.${blankList[i].name}" to bind data -->
</template>
`
}
return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<link rel="stylesheet" href="https://unpkg.com/form-making/dist/FormMaking.css">
</head>
<body>
<div id="app">
<fm-generate-form :data="jsonData" :remote="remoteFuncs" :value="editData" ref="generateForm">
${blankTemplate}
</fm-generate-form>
<el-button type="primary" @click="handleSubmit">提交</el-button>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/form-making/dist/FormMaking.umd.js"></script>
<script>
new Vue({
el: '#app',
data: {
jsonData: ${data},
editData: {},
remoteFuncs: {
${funcTemplate}
}
},
methods: {
handleSubmit () {
this.$refs.generateForm.getData().then(data => {
// data check success
// data - form data
}).catch(e => {
// data check failed
})
}
}
})
</script>
</body>
</html>`
}
<template>
<fm-making-form ref="makingForm" upload preview generate-code generate-json clearable>
<template slot="action" />
</fm-making-form>
</template>
<script>
export default {
mounted() {
}
}
</script>
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
import VueI18n from 'vue-i18n'
import 'normalize.css/normalize.css'
import MakingForm from './components/Container.vue'
import GenerateForm from './components/GenerateForm.vue'
import enUS from './lang/en-US'
import zhCN from './lang/zh-CN'
import './iconfont/iconfont.css'
import './styles/cover.scss'
import './styles/index.scss'
const loadLang = function(Vue, lang, locale, i18n) {
if (locale) {
locale('en-US', { ...locale('en-US'), ...enUS })
locale('zh-CN', { ...locale('zh-CN'), ...zhCN })
Vue.config.lang = lang
} else if (i18n) {
i18n.setLocaleMessage('en-US', { ...i18n.messages['en-US'], ...enUS })
i18n.setLocaleMessage('zh-CN', { ...i18n.messages['zh-CN'], ...zhCN })
i18n.locale = lang
} else {
Vue.use(VueI18n)
Vue.locale('en-US', { ...Vue.locale('en-US'), ...enUS })
Vue.locale('zh-CN', { ...Vue.locale('zh-CN'), ...zhCN })
Vue.config.lang = lang
}
}
MakingForm.install = function(Vue, opts = {
lang: 'zh-CN',
locale: null,
i18n: null
}) {
loadLang(Vue, opts.lang, opts.locale, opts.i18n)
Vue.component(MakingForm.name, MakingForm)
}
GenerateForm.install = function(Vue, opts = {
lang: 'zh-CN',
locale: null,
i18n: null
}) {
loadLang(Vue, opts.lang, opts.locale, opts.i18n)
Vue.component(GenerateForm.name, GenerateForm)
}
const components = [
MakingForm,
GenerateForm
]
const install = function(Vue, opts = {
lang: 'zh-CN',
locale: null,
i18n: null
}) {
loadLang(Vue, opts.lang, opts.locale, opts.i18n)
components.forEach(component => {
Vue.component(component.name, component)
})
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export {
install,
MakingForm,
GenerateForm
}
export default {
install,
MakingForm,
GenerateForm
}
import Vue from 'vue'
import GenerateForm from './components/GenerateForm.vue'
import './styles/cover.scss'
import './styles/index.scss'
GenerateForm.install = function(Vue) {
Vue.component(GenerateForm.name, GenerateForm)
}
const components = [
GenerateForm
]
const install = function(Vue, opts = {}) {
components.forEach(component => {
Vue.component(component.name, component)
})
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export {
install,
GenerateForm
}
export default {
install,
GenerateForm
}
export default {
fm: {
components: {
fields: {
input: 'Input',
textarea: 'Textarea',
number: 'Number',
radio: 'Radio',
checkbox: 'Checkbox',
time: 'Time',
date: 'Date',
rate: 'Rate',
color: 'Color',
select: 'Select',
switch: 'Switch',
slider: 'Slider',
text: 'Text',
blank: 'Custom',
fileupload: 'File',
imgupload: 'Image',
editor: 'Editor',
cascader: 'Cascader',
table: 'Sub-table',
grid: 'Grid',
tabs: 'Tabs',
divider: 'Divider'
},
basic: {
title: 'Basic Component'
},
advance: {
title: 'Advance Component'
},
layout: {
title: 'Layout'
}
},
description: {
containerEmpty: 'You can drag and drop the item from the left to add components',
configEmpty: 'Please add a component',
tableEmpty: 'You can drag and drop the item from the left to add components',
uploadJsonInfo: 'There is the format of JSON below,you can overwrite it with you own JSON code'
},
message: {
copySuccess: 'Copy Successed',
validError: 'Form data validation failed'
},
actions: {
import: 'Import JSON',
clear: 'Clear',
preview: 'Preview',
json: 'Generate JSON',
code: 'Generate Code',
getData: 'Get Data',
reset: 'Reset',
copyData: 'Copy Data',
cancel: 'Cancel',
confirm: 'Confirm',
addOption: 'Add Option',
addColumn: 'Add Column',
addTab: 'Add Tab',
upload: 'Upload',
add: 'Add'
},
config: {
form: {
title: 'Form Attribute',
labelPosition: {
title: 'Label Position',
left: 'Left',
right: 'Right',
top: 'Top'
},
labelWidth: 'Label Width',
size: 'Size',
customClass: 'Custom Class'
},
widget: {
title: 'Component Attribute',
model: 'ID',
name: 'Name',
width: 'Width',
height: 'Height',
size: 'Size',
labelWidth: 'Label Width',
custom: 'Custom',
placeholder: 'Placeholder',
layout: 'Layout',
block: 'Block',
inline: 'Inline',
contentPosition: 'Content Position',
center: 'Center',
showInput: 'Display Input Box',
min: 'Minimum',
max: 'Maximum',
step: 'Step',
multiple: 'Multiple',
filterable: 'Searchable',
allowHalf: 'Allow Half',
showAlpha: 'Support transparency options',
showLabel: 'Show lable',
option: 'Option',
staticData: 'Static Data',
remoteData: 'Remote Date',
remoteFunc: 'Remote Function',
value: 'Value',
label: 'Label',
childrenOption: 'Sub-Option',
defaultValue: 'Default Value',
showType: 'Display type',
isRange: 'Range Time',
isTimestamp: 'Get time stamp',
startPlaceholder: 'Placeholder of start time',
endPlaceholder: 'Placeholder of end time',
format: 'Format',
limit: 'Maximum Upload Count',
isQiniu: 'Upload with Qiniu Cloud',
tokenFunc: 'A funchtin to get Qiniu Uptoken',
imageAction: 'Picture upload address',
tip: 'Text Prompt',
action: 'Upload Address',
defaultType: 'Data Type',
string: 'String',
object: 'Object',
array: 'Array',
number: 'Number',
boolean: 'Boolean',
integer: 'Integer',
float: 'Float',
url: 'URL',
email: 'E-mail',
hex: 'Hexadecimal',
gutter: 'Grid Spacing',
columnOption: 'Column Configuration',
span: 'Grid spans',
justify: 'Horizontal Arrangement',
justifyStart: 'Start',
justifyEnd: 'End',
justifyCenter: 'Center',
justifySpaceAround: 'Space Around',
justifySpaceBetween: 'Space Between',
align: 'Vertical Arrangement',
alignTop: 'Top',
alignMiddle: 'Middle',
alignBottom: 'Bottom',
type: 'Type',
default: 'Default',
card: 'Tabs',
borderCard: 'Border-Card',
tabPosition: 'Tab Position',
top: 'Tob',
left: 'Left',
right: 'Right',
bottom: 'Bottom',
tabOption: 'Label Configuration',
tabName: 'Tab Name',
customClass: 'Custom Class',
attribute: 'Attribute Action',
dataBind: 'Data Binding',
hidden: 'Hidden',
readonly: 'Read Only',
disabled: 'Disabled',
editable: 'Text box is editable',
clearable: 'Display Clear Button',
arrowControl: 'Use the arrow for time selection',
isDelete: 'Deletable',
isEdit: 'Editable',
showPassword: 'Display Password',
validate: 'Validation',
required: 'Required',
patternPlaceholder: 'Fill in the regular expressions',
newOption: 'New Option',
tab: 'Tab',
validatorRequired: 'Required',
validatorType: 'Invaild format',
validatorPattern: 'Unmatched pattern'
}
},
upload: {
preview: 'preview',
edit: 'replace',
delete: 'delete'
}
}
}
export default {
fm: {
components: {
fields: {
input: '单行文本',
textarea: '多行文本',
number: '计数器',
radio: '单选框组',
checkbox: '多选框组',
time: '时间选择器',
date: '日期选择器',
rate: '评分',
color: '颜色选择器',
select: '下拉选择框',
switch: '开关',
slider: '滑块',
text: '文字',
blank: '自定义区域',
fileupload: '文件',
imgupload: '图片',
editor: '编辑器',
cascader: '级联选择器',
table: '子表单',
grid: '栅格布局',
tabs: '标签页',
divider: '分割线'
},
basic: {
title: '基础字段'
},
advance: {
title: '高级字段'
},
layout: {
title: '布局字段'
}
},
description: {
containerEmpty: '从左侧拖拽来添加字段',
configEmpty: '请添加字段',
tableEmpty: '从左侧拖拽来添加字段',
uploadJsonInfo: 'JSON格式如下,直接复制生成的json覆盖此处代码点击确定即可'
},
message: {
copySuccess: '复制成功',
validError: '表单数据校验失败'
},
actions: {
import: '导入JSON',
clear: '清空',
preview: '预览',
json: '生成JSON',
code: '生成代码',
getData: '获取数据',
reset: '重置',
copyData: '复制数据',
cancel: '取 消',
confirm: '确 定',
addOption: '添加选项',
addColumn: '添加列',
addTab: '添加标签',
upload: '点击上传',
add: '添加'
},
config: {
form: {
title: '表单属性',
labelPosition: {
title: '标签对齐方式',
left: '左对齐',
right: '右对齐',
top: '顶部对齐'
},
labelWidth: '表单标签宽度',
size: '组件尺寸',
customClass: '自定义Class'
},
widget: {
title: '字段属性',
model: '字段标识',
name: '标题',
width: '宽度',
height: '高度',
size: '大小',
labelWidth: '标签宽度',
custom: '自定义',
placeholder: '占位内容',
layout: '布局方式',
block: '块级',
inline: '行内',
contentPosition: '文案位置',
center: '居中',
showInput: '显示输入框',
min: '最小值',
max: '最大值',
step: '步长',
multiple: '是否多选',
filterable: '是否可搜索',
allowHalf: '允许半选',
showAlpha: '支持透明度选择',
showLabel: '是否显示标签',
option: '选项',
staticData: '静态数据',
remoteData: '远端数据',
remoteFunc: '远端方法',
value: '值',
label: '标签',
childrenOption: '子选项',
defaultValue: '默认值',
showType: '显示类型',
isRange: '是否为范围选择',
isTimestamp: '是否获取时间戳',
startPlaceholder: '开始时间占位内容',
endPlaceholder: '结束时间占位内容',
format: '格式',
limit: '最大上传数',
isQiniu: '使用七牛上传',
tokenFunc: '获取七牛Token方法',
imageAction: '图片上传地址',
tip: '提示说明文字',
action: '上传地址',
defaultType: '绑定数据类型',
string: '字符串',
object: '对象',
array: '数组',
number: '数字',
boolean: '布尔值',
integer: '整数',
float: '浮点数',
url: 'URL地址',
email: '邮箱地址',
hex: '十六进制',
gutter: '栅格间隔',
columnOption: '列配置项',
span: '栅格值',
justify: '水平排列方式',
justifyStart: '左对齐',
justifyEnd: '右对齐',
justifyCenter: '居中',
justifySpaceAround: '两侧间隔相等',
justifySpaceBetween: '两端对齐',
align: '垂直排列方式',
alignTop: '顶部对齐',
alignMiddle: '居中',
alignBottom: '底部对齐',
type: '风格类型',
default: '默认',
card: '选项卡',
borderCard: '卡片化',
tabPosition: '选项卡位置',
top: '顶部',
left: '左侧',
right: '右侧',
bottom: '底部',
tabOption: '标签配置项',
tabName: '标签名称',
customClass: '自定义Class',
attribute: '操作属性',
dataBind: '数据绑定',
hidden: '隐藏',
readonly: '完全只读',
disabled: '禁用',
editable: '文本框可输入',
clearable: '显示清除按钮',
arrowControl: '使用箭头进行时间选择',
isDelete: '删除',
isEdit: '编辑',
showPassword: '显示密码',
validate: '校验',
required: '必填',
patternPlaceholder: '填写正则表达式',
newOption: '新选项',
tab: '标签页',
validatorRequired: '必须填写',
validatorType: '格式不正确',
validatorPattern: '格式不匹配'
}
},
upload: {
preview: '预览',
edit: '替换',
delete: '删除'
}
}
}
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui'
import VueI18n from 'vue-i18n'
import VueEditor from 'vue2-editor'
import 'element-ui/lib/theme-chalk/index.css'
import enLocale from 'element-ui/lib/locale/lang/en'
import zhLocale from 'element-ui/lib/locale/lang/zh-CN'
Vue.use(VueI18n)
Vue.use(VueEditor)
const messages = {
'en-US': {
header: {
title: 'FormMaking',
document: 'Docs',
pricing: 'Pricing',
advanced: 'Advanced'
}
},
'zh-CN': {
header: {
title: '表单设计器',
document: '使用文档',
pricing: '商业授权',
advanced: '高级版本'
}
}
}
Vue.locale('en-US', { ...enLocale, ...messages['en-US'] })
Vue.locale('zh-CN', { ...zhLocale, ...messages['zh-CN'] })
Vue.config.lang = 'zh-CN'
Vue.use(ElementUI, { size: 'small' })
// import 'form-making/dist/FormMaking.css'
// import FormMaking from 'form-making'
import FormMaking from './index'
Vue.use(FormMaking)
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
<template>
<router-view />
</template>
<script>
import Vue from 'vue'
export default {
watch: {
'$route.params.lang': function(val) {
this.loadLanguage()
}
},
created() {
this.loadLanguage()
},
methods: {
loadLanguage() {
if (this.$route.params.lang == 'zh-CN') {
Vue.config.lang = 'zh-CN'
localStorage.setItem('language', 'zh-CN')
} else if (this.$route.params.lang == 'en-US') {
Vue.config.lang = 'en-US'
localStorage.setItem('language', 'en-US')
} else {
this.$router.replace({ name: this.$route.name, params: { lang: navigator.language == 'zh-CN' ? 'zh-CN' : 'en-US' }})
}
}
}
}
</script>
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../demo/Home.vue'
import LanguageView from './LanguageView.vue'
Vue.use(Router)
const language = localStorage.getItem('language') || (navigator.language == 'zh-CN' ? 'zh-CN' : 'en-US')
export default new Router({
routes: [
{
path: '/',
redirect: to => ({ name: 'index', params: { lang: language }})
},
{
path: '/:lang',
// name: 'lang',
component: LanguageView,
children: [
{
path: '',
name: 'index',
component: Home
}
]
}
]
})
.el-radio+.el-radio{
margin-left: 0 !important;
}
.el-radio{
margin-right: 30px;
}
.el-checkbox+.el-checkbox{
margin-left: 0 !important;
}
.el-checkbox{
margin-right: 30px;
}
.el-form-item--small{
.el-radio{
line-height: 32px !important;
}
.el-rate{
margin-top: 6px;
}
}
.el-form-item--mini{
.el-radio{
line-height: 28px !important;
}
.el-rate{
margin-top: 4px;
}
}
.el-form-item--medium{
.el-radio{
line-height: 36px !important;
}
.el-rate{
margin-top: 8px;
}
}
\ No newline at end of file
export const loadJs = (url) => {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.type = 'text/javascript'
document.body.appendChild(script)
script.onload = () => {
resolve()
}
})
}
export const loadCss = (url) => {
return new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
document.head.appendChild(link)
link.onload = () => {
resolve()
}
})
}
export const generateUUID = () => {
var d = new Date().getTime()
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random() * 16) % 16 | 0
d = Math.floor(d / 16)
return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16)
})
return uuid
}
import axios from 'axios'
const request = axios.create({
withCredentials: false
})
request.interceptors.request.use(
config => {
return config
},
error => {
return Promise.reject(new Error(error).message)
}
)
request.interceptors.response.use(
response => {
return response.data
},
error => {
return Promise.reject(new Error(error).message)
}
)
export default request
...@@ -53,10 +53,6 @@ ...@@ -53,10 +53,6 @@
margin-left: 20px; margin-left: 20px;
} }
.el-dialog {
margin-top: 6vh !important;
}
.el-table .el-table__header-wrapper th { .el-table .el-table__header-wrapper th {
word-break: break-word; word-break: break-word;
background-color: #f8f8f9; background-color: #f8f8f9;
......
<template> <template>
<div> <div class="app-container">
this is template manager. <el-card class="box-card">
<el-form ref="listQuery" :model="listQuery" :inline="true">
<el-form-item label="分类名称">
<el-input
v-model="listQuery.name"
placeholder="请输入分类名称"
clearable
size="small"
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">搜索</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
v-permisaction="['process:admin:classify:add']"
type="primary"
icon="el-icon-plus"
size="mini"
@click="handleCreate"
>新增</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-button
v-permisaction="['system:sysrole:edit']"
type="success"
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
>编辑</el-button>
</el-col>
<el-col :span="1.5">
<el-button
v-permisaction="['system:sysrole:remove']"
type="danger"
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
>删除</el-button>
</el-col> -->
</el-row>
<el-table v-loading="loading" border :data="classifyList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="ID" prop="id" width="120" />
<el-table-column label="名称" prop="name" :show-overflow-tooltip="true" width="150" />
<el-table-column label="创建者" prop="create_name" :show-overflow-tooltip="true" width="150" />
<el-table-column label="创建时间" align="center" prop="create_time" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.create_time) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
v-permisaction="['process:admin:classify:edit']"
size="mini"
type="text"
icon="el-icon-edit"
@click="handleEdit(scope.row)"
>编辑</el-button>
<el-button
v-permisaction="['process:admin:classify:delete']"
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageIndex"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<el-dialog :title="dialogFormVisibleName===1?'新建模版':'编辑模版'" :visible.sync="open" :fullscreen="true" style="margin-top: 0">
<div class="tpl-create-content">
<el-form ref="ruleForm" :model="ruleForm" :rules="rules" label-width="100px">
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="ruleForm.remarks" type="textarea" />
</el-form-item>
<el-form-item label="模版" prop="form_structure">
<div style="border-radius: 4px; border: 1px solid #ccc; overflow:hidden">
<fm-making-form
ref="makingform"
style="height: 600px;"
preview
clearable
upload
generate-code
generate-json
:advance-fields="['blank', 'editor', 'cascader']"
>
<template slot="action" />
</fm-making-form>
</div>
</el-form-item>
</el-form>
<div style="text-align: center">
<el-button type="primary" @click="dialogFormVisibleName===1?submitForm('ruleForm'):editForm('ruleForm')">提交</el-button>
<el-button @click="open = false">取 消</el-button>
</div>
</div>
</el-dialog>
</el-card>
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue'
import {
createClassify,
classifyList,
updateClassify,
deleteClassify
} from '@/api/process/admin/classify'
// 表单设计
import {
GenerateForm,
MakingForm
} from '@/components/VueFormMaking'
import '@/components/VueFormMaking/styles/FormMaking.css'
import ace from 'ace-builds'
import 'ace-builds/webpack-resolver'
Vue.use(ace)
Vue.use(GenerateForm)
Vue.use(MakingForm)
export default { export default {
name: 'Role',
data() { data() {
return { return {
dialogFormVisibleName: 1,
queryParams: {},
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 总条数
total: 0,
// 是否显示弹出层
open: false,
// 查询参数
classifyList: [],
listQuery: {
page: 1,
per_page: 10
},
ruleForm: {
id: undefined,
name: ''
},
rules: {
name: [
{ required: true, message: '请输入流程分类', trigger: 'blur' }
]
}
} }
}, },
created() {
this.getList()
},
methods: { methods: {
/** 查询角色列表 */
getList() {
this.loading = true
this.listQuery.page = this.queryParams.pageIndex
this.listQuery.per_page = this.queryParams.pageSize
classifyList(this.listQuery).then(response => {
this.classifyList = response.data.data
this.queryParams.pageIndex = response.data.page
this.queryParams.pageSize = response.data.per_page
this.total = response.data.total_count
this.loading = false
})
},
handleCreate() {
this.ruleForm = {
id: undefined,
name: ''
}
this.dialogFormVisibleName = 1
this.open = true
},
handleEdit(row) {
this.ruleForm.id = row.id
this.ruleForm.name = row.name
this.open = true
this.dialogFormVisibleName = 2
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
createClassify(this.ruleForm).then(response => {
if (response !== undefined) {
this.getList()
this.$message({
type: 'success',
message: '分类已增加!'
})
this.open = false
}
})
}
})
},
editForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
updateClassify(this.ruleForm).then(response => {
if (response !== undefined) {
this.getList()
this.$message({
type: 'success',
message: '分类已更新!'
})
this.open = false
}
})
}
})
},
handleQuery() {
this.queryParams.pageIndex = 1
this.queryParams.pageSize = 10
this.getList()
},
handleDelete(row) {
this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteClassify({
classifyId: row.id
}).then(response => {
if (response !== undefined) {
this.getList()
this.$message({
type: 'success',
message: '分类已删除!'
})
}
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
},
handleSelectionChange() {},
handleSave(values) {
var jsonValue = JSON.parse(values)
if (jsonValue.list.length > 0) {
this.ruleForm.form_structure = values
} else {
this.ruleForm.form_structure = ''
}
}
} }
} }
</script> </script>
<style scoped>
</style>
...@@ -6,7 +6,7 @@ function resolve(dir) { ...@@ -6,7 +6,7 @@ function resolve(dir) {
return path.join(__dirname, dir) return path.join(__dirname, dir)
} }
const name = defaultSettings.title || 'vue Element Admin' // page title const name = defaultSettings.title || 'ferry' // page title
// If your port is set to 80, // If your port is set to 80,
// use administrator privileges to execute the command line. // use administrator privileges to execute the command line.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment