组件拆分重构,属性设置拆分重构。

develop
vdpAdmin 2021-10-14 15:20:09 +08:00
parent d94f5b1596
commit 77779eefa9
182 changed files with 8855 additions and 3581 deletions

View File

@ -1,6 +1,6 @@
{
"name": "variant-form",
"version": "2.0.1",
"version": "2.1.0",
"private": false,
"scripts": {
"serve": "vue-cli-service serve --open src/main.js",

View File

@ -1,7 +1,7 @@
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com/
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
@ -40,6 +40,8 @@ export function createDesigner(vueInstance) {
formWidget: null, //表单设计容器
cssClassList: [], //自定义样式列表
historyData: {
index: -1, //index: 0,
maxStep: 20,
@ -528,7 +530,7 @@ export function createDesigner(vueInstance) {
})
return newTable
} else {
} else { //其他容器组件不支持clone操作
return null
}
},
@ -683,6 +685,14 @@ export function createDesigner(vueInstance) {
this.vueInstance.$on(evtName, (data) => callback(data))
},
setCssClassList(cssClassList) {
this.cssClassList = cssClassList
},
getCssClassList() {
return this.cssClassList
},
registerFormWidget(formWidget) {
this.formWidget = formWidget
},

View File

@ -1,408 +0,0 @@
<template>
<div class="container-wrapper">
<el-row v-if="widget.type === 'grid'" :key="widget.id" :gutter="widget.options.gutter" class="grid-container"
:class="[selected ? 'selected' : '', customClass]"
@click.native.stop="selectWidget(widget)">
<template v-for="(colWidget, colIdx) in widget.cols">
<grid-col-widget :widget="colWidget" :designer="designer" :key="colWidget.id" :parent-list="widget.cols"
:index-of-parent-list="colIdx" :parent-widget="widget"></grid-col-widget>
</template>
</el-row>
<div v-else-if="widget.type === 'table'" :key="widget.id" class="table-container"
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
<table class="table-layout">
<tbody>
<tr v-for="(row, rowIdx) in widget.rows" :key="row.id">
<template v-for="(colWidget, colIdx) in row.cols">
<table-cell-widget v-if="!colWidget.merged" :widget="colWidget" :designer="designer"
:key="colWidget.id" :parent-list="widget.cols"
:row-index="rowIdx" :row-length="widget.rows.length"
:col-index="colIdx" :col-length="row.cols.length"
:col-array="row.cols" :row-array="widget.rows"
:parent-widget="widget"></table-cell-widget>
</template>
</tr>
</tbody>
</table>
</div>
<div v-else-if="widget.type === 'tab'" :key="widget.id" class="tab-container"
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
<el-tabs :type="widget.displayType" v-model="activeTab" @tab-click="onTabClick">
<el-tab-pane v-for="(tab, index) in widget.tabs" :key="index" :label="tab.options.label" :name="tab.options.name"
@click.native.stop="selectWidget(widget)">
<draggable :list="tab.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
handle=".drag-handler"
@add="(evt) => onContainerDragAdd(evt, tab.widgetList)"
@update="onContainerDragUpdate" :move="checkContainerMove">
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in tab.widgetList">
<template v-if="'container' === subWidget.category">
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="tab.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></container-widget>
</template>
<template v-else>
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="tab.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></field-widget>
</template>
</template>
</transition-group>
</draggable>
</el-tab-pane>
</el-tabs>
</div>
<div v-else-if="widget.type === 'section'" :key="widget.id" class="section-container"
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
<draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost', animation: 200}"
handle=".drag-handler"
@add="(evt) => onContainerDragAdd(evt, widget.widgetList)"
@update="onContainerDragUpdate" :move="checkContainerMove">
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in widget.widgetList">
<template v-if="'container' === subWidget.category">
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></container-widget>
</template>
<template v-else>
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></field-widget>
</template>
</template>
</transition-group>
</draggable>
</div>
<div class="container-action" v-if="designer.selectedId === widget.id && !widget.internal">
<i class="el-icon-back" :title="i18nt('designer.hint.selectParentWidget')"
@click.stop="selectParentWidget(widget)"></i>
<i class="el-icon-top" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveUpWidget')"
@click.stop="moveUpWidget()"></i>
<i class="el-icon-bottom" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveDownWidget')"
@click.stop="moveDownWidget()"></i>
<i v-if="widget.type === 'table'" class="iconfont icon-insertrow" :title="i18nt('designer.hint.insertRow')"
@click.stop="insertTableRow(widget)"></i>
<i v-if="widget.type === 'table'" class="iconfont icon-insertcolumn" :title="i18nt('designer.hint.insertColumn')"
@click.stop="insertTableCol(widget)"></i>
<i class="el-icon-copy-document" v-if="(widget.type === 'grid') || (widget.type === 'table')"
:title="i18nt('designer.hint.cloneWidget')" @click.stop="cloneContainer(widget)"></i>
<i class="el-icon-delete" :title="i18nt('designer.hint.remove')" @click.stop="removeWidget"></i>
</div>
<div class="drag-handler" v-if="designer.selectedId === widget.id && !widget.internal">
<i class="el-icon-rank" :title="i18nt('designer.hint.dragHandler')"></i>
<i>{{i18nt('designer.widgetLabel.' + widget.type)}}</i>
</div>
</div>
</template>
<script>
import Draggable from 'vuedraggable'
import FieldWidget from "@/components/form-designer/form-widget/field-widget";
import GridColWidget from "@/components/form-designer/form-widget/grid-col-widget";
import TableCellWidget from "@/components/form-designer/form-widget/table-cell-widget";
import VTabPane from "@/components/form-designer/form-widget/tab-pane";
import i18n from "@/utils/i18n";
export default {
name: "container-widget",
componentName: 'ContainerWidget',
mixins: [i18n],
components: {
Draggable,
FieldWidget,
GridColWidget,
TableCellWidget,
VTabPane,
},
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
//
},
data() {
return {
activeTab: 'tab1',
//
}
},
computed: {
selected() {
return this.widget.id === this.designer.selectedId
},
customClass() {
return this.widget.options.customClass || ''
},
},
watch: {
//
},
mounted() {
//
},
methods: {
insertTableRow(widget) {
this.designer.insertTableRow(widget)
this.designer.emitHistoryChange()
},
insertTableCol(widget) {
this.designer.insertTableCol(widget)
this.designer.emitHistoryChange()
},
onContainerDragAdd(evt, subList) {
const newIndex = evt.newIndex
if (!!subList[newIndex]) {
this.designer.setSelected( subList[newIndex] )
}
this.designer.emitHistoryChange()
},
onContainerDragUpdate() {
this.designer.emitHistoryChange()
},
checkContainerMove(evt) {
return this.designer.checkWidgetMove(evt)
},
selectWidget(widget) {
this.designer.setSelected(widget)
},
selectParentWidget() {
if (this.parentWidget) {
this.designer.setSelected(this.parentWidget)
} else {
this.designer.clearSelected()
}
},
moveUpWidget() {
this.designer.moveUpWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
moveDownWidget() {
this.designer.moveDownWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
cloneContainer(widget) {
if (!!this.parentList) {
let newCon = this.designer.cloneContainer(widget)
this.parentList.splice(this.indexOfParentList + 1, 0, newCon)
this.designer.setSelected(newCon)
this.designer.emitHistoryChange()
}
},
removeWidget() {
if (!!this.parentList) {
let nextSelected = null
if (this.parentList.length === 1) {
if (!!this.parentWidget) {
nextSelected = this.parentWidget
}
} else if (this.parentList.length === (1 + this.indexOfParentList)) {
nextSelected = this.parentList[this.indexOfParentList - 1]
} else {
nextSelected = this.parentList[this.indexOfParentList + 1]
}
this.$nextTick(() => {
this.parentList.splice(this.indexOfParentList, 1)
//if (!!nextSelected) {
this.designer.setSelected(nextSelected)
//}
this.designer.emitHistoryChange()
})
}
},
onSubFormDragAdd(evt, subList) {
const newIndex = evt.newIndex
if (!!subList[newIndex]) {
this.designer.setSelected( subList[newIndex] )
}
this.designer.emitHistoryChange()
console.log('test', 'onSubFormDragAdd')
this.designer.emitEvent('field-selected', this.widget)
},
onSubFormDragEnd(evt) {
console.log('sub form drag end: ', evt)
},
onTabClick(evt) {
console.log('onTabClick', evt)
let paneName = evt.name
this.widget.tabs.forEach((tp) => {
tp.options.active = tp.options.name === paneName;
})
},
}
}
</script>
<style lang="scss" scoped>
.container-wrapper {
position: relative;
margin-bottom: 5px;
.container-action{
position: absolute;
//bottom: -30px;
bottom: 0;
right: -2px;
height: 28px;
line-height: 28px;
background: $--color-primary;
z-index: 999;
i {
font-size: 14px;
color: #fff;
margin: 0 5px;
cursor: pointer;
}
}
.drag-handler {
position: absolute;
top: -2px;
//bottom: -24px; /* */
left: -6px;
height: 22px;
line-height: 22px;
background: $--color-primary;
z-index: 9;
i {
font-size: 14px;
font-style: normal;
color: #fff;
margin: 4px;
cursor: move;
}
}
}
.el-row.grid-container {
min-height: 50px;
//line-height: 48px;
//padding: 6px;
outline: 1px dashed #336699;
.form-widget-list {
min-height: 28px;
}
}
div.table-container {
padding: 5px;
border: 1px dashed #336699;
box-sizing: border-box;
table.table-layout {
width: 100%;
text-align: center;
//border: 1px solid #c8ebfb;
border-collapse: collapse;
table-layout: fixed;
::v-deep td {
height: 48px;
border: 1px dashed #336699;
padding: 3px;
display: table-cell;
}
.form-widget-list {
border: 1px dashed #336699;
min-height: 36px;
}
}
}
.tab-container {
//padding: 5px;
margin: 2px;
.form-widget-list {
min-height: 28px;
}
}
.grid-container.selected, .table-container.selected, .tab-container.selected,
.sub-form-container.selected, .section-container.selected, .grid-cell.selected {
outline: 2px solid $--color-primary !important;
}
//.el-tabs.selected {
// outline: 2px solid $&#45;&#45;color-primary;
//}
.section-container {
border: 1px dashed #336699;
border-radius: 5px;
margin: 6px 0;
padding: 6px;
::v-deep .form-widget-list {
min-height: 28px;
}
}
.sub-form-container {
//width: 100%;
padding: 8px;
border: 1px dashed #336699;
::v-deep .sub-form-table {
min-height: 68px;
div.sub-form-table-column {
display: inline-block;
//width: 200px;
}
}
::v-deep .ghost{
content: '';
font-size: 0;
//height: 3px;
height: 74px;
width: 1px;
box-sizing: border-box;
display: inline-block;
background: $--color-primary;
border: 2px solid $--color-primary;
outline-width: 0;
padding: 0;
margin: 0;
overflow: hidden;
}
}
</style>

View File

@ -0,0 +1,100 @@
<!--
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
-->
<template>
<div class="container-wrapper">
<slot></slot>
<div class="container-action" v-if="designer.selectedId === widget.id && !widget.internal">
<i class="el-icon-back" :title="i18nt('designer.hint.selectParentWidget')"
@click.stop="selectParentWidget(widget)"></i>
<i class="el-icon-top" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveUpWidget')"
@click.stop="moveUpWidget()"></i>
<i class="el-icon-bottom" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveDownWidget')"
@click.stop="moveDownWidget()"></i>
<i v-if="widget.type === 'table'" class="iconfont icon-insertrow" :title="i18nt('designer.hint.insertRow')"
@click.stop="insertTableRow(widget)"></i>
<i v-if="widget.type === 'table'" class="iconfont icon-insertcolumn" :title="i18nt('designer.hint.insertColumn')"
@click.stop="insertTableCol(widget)"></i>
<i class="el-icon-copy-document" v-if="(widget.type === 'grid') || (widget.type === 'table')"
:title="i18nt('designer.hint.cloneWidget')" @click.stop="cloneContainer(widget)"></i>
<i class="el-icon-delete" :title="i18nt('designer.hint.remove')" @click.stop="removeWidget"></i>
</div>
<div class="drag-handler" v-if="designer.selectedId === widget.id && !widget.internal">
<i class="el-icon-rank" :title="i18nt('designer.hint.dragHandler')"></i>
<i>{{i18nt('designer.widgetLabel.' + widget.type)}}</i>
</div>
</div>
</template>
<script>
import i18n from "@/utils/i18n";
import containerMixin from "@/components/form-designer/form-widget/container-widget/containerMixin";
export default {
name: "container-wrapper",
mixins: [i18n, containerMixin],
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
},
}
</script>
<style lang="scss" scoped>
.container-wrapper {
position: relative;
margin-bottom: 5px;
.container-action{
position: absolute;
//bottom: -30px;
bottom: 0;
right: -2px;
height: 28px;
line-height: 28px;
background: $--color-primary;
z-index: 999;
i {
font-size: 14px;
color: #fff;
margin: 0 5px;
cursor: pointer;
}
}
.drag-handler {
position: absolute;
top: -2px;
//bottom: -24px; /* */
left: -6px;
height: 22px;
line-height: 22px;
background: $--color-primary;
z-index: 9;
i {
font-size: 14px;
font-style: normal;
color: #fff;
margin: 4px;
cursor: move;
}
}
}
</style>

View File

@ -0,0 +1,89 @@
export default {
methods: {
insertTableRow(widget) {
this.designer.insertTableRow(widget)
this.designer.emitHistoryChange()
},
insertTableCol(widget) {
this.designer.insertTableCol(widget)
this.designer.emitHistoryChange()
},
onContainerDragAdd(evt, subList) {
const newIndex = evt.newIndex
if (!!subList[newIndex]) {
this.designer.setSelected( subList[newIndex] )
}
this.designer.emitHistoryChange()
},
onContainerDragUpdate() {
this.designer.emitHistoryChange()
},
checkContainerMove(evt) {
return this.designer.checkWidgetMove(evt)
},
selectWidget(widget) {
this.designer.setSelected(widget)
},
selectParentWidget() {
if (this.parentWidget) {
this.designer.setSelected(this.parentWidget)
} else {
this.designer.clearSelected()
}
},
moveUpWidget() {
this.designer.moveUpWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
moveDownWidget() {
this.designer.moveDownWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
cloneContainer(widget) {
if (!!this.parentList) {
let newCon = this.designer.cloneContainer(widget)
this.parentList.splice(this.indexOfParentList + 1, 0, newCon)
this.designer.setSelected(newCon)
this.designer.emitHistoryChange()
}
},
removeWidget() {
if (!!this.parentList) {
let nextSelected = null
if (this.parentList.length === 1) {
if (!!this.parentWidget) {
nextSelected = this.parentWidget
}
} else if (this.parentList.length === (1 + this.indexOfParentList)) {
nextSelected = this.parentList[this.indexOfParentList - 1]
} else {
nextSelected = this.parentList[this.indexOfParentList + 1]
}
this.$nextTick(() => {
this.parentList.splice(this.indexOfParentList, 1)
//if (!!nextSelected) {
this.designer.setSelected(nextSelected)
//}
this.designer.emitHistoryChange()
})
}
},
}
}

View File

@ -9,12 +9,12 @@
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in widget.widgetList">
<template v-if="'container' === subWidget.category">
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></container-widget>
<component :is="subWidget.type + '-widget'" :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></component>
</template>
<template v-else>
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></field-widget>
<component :is="subWidget.type + '-widget'" :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></component>
</template>
</template>
</transition-group>
@ -39,9 +39,9 @@
<script>
import Draggable from 'vuedraggable'
//import ContainerWidget from "@/components/form-designer/form-widget/container-widget";
import FieldWidget from "@/components/form-designer/form-widget/field-widget";
import i18n from "@/utils/i18n";
import ContainerComponents from '@/components/form-designer/form-widget/container-widget/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
export default {
name: "GridColWidget",
@ -49,8 +49,8 @@
mixins: [i18n],
components: {
Draggable,
//'container-widget': ContainerWidget, /* 使 */
FieldWidget,
...ContainerComponents,
...FieldComponents,
},
props: {
widget: Object,

View File

@ -0,0 +1,88 @@
<!--
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
-->
<template>
<container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList"
:index-of-parent-list="indexOfParentList">
<el-row :key="widget.id" :gutter="widget.options.gutter" class="grid-container"
:class="[selected ? 'selected' : '', customClass]"
@click.native.stop="selectWidget(widget)">
<template v-for="(colWidget, colIdx) in widget.cols">
<grid-col-widget :widget="colWidget" :designer="designer" :key="colWidget.id" :parent-list="widget.cols"
:index-of-parent-list="colIdx" :parent-widget="widget"></grid-col-widget>
</template>
</el-row>
</container-wrapper>
</template>
<script>
import i18n from "@/utils/i18n"
import GridColWidget from "@/components/form-designer/form-widget/container-widget/grid-col-widget"
import containerMixin from "@/components/form-designer/form-widget/container-widget/containerMixin";
import ContainerWrapper from "@/components/form-designer/form-widget/container-widget/container-wrapper";
export default {
name: "grid-widget",
componentName: 'ContainerWidget',
mixins: [i18n, containerMixin],
components: {
ContainerWrapper,
GridColWidget
},
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
},
computed: {
selected() {
return this.widget.id === this.designer.selectedId
},
customClass() {
return this.widget.options.customClass || ''
},
},
watch: {
//
},
mounted() {
//
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.el-row.grid-container {
min-height: 50px;
//line-height: 48px;
//padding: 6px;
outline: 1px dashed #336699;
.form-widget-list {
min-height: 28px;
}
}
.grid-container.selected, .grid-cell.selected {
outline: 2px solid $--color-primary !important;
}
</style>

View File

@ -0,0 +1,10 @@
const requireComponent = require.context('./', false, /\w+\.vue$/)
let comps = {}
requireComponent.keys().map(fileName => {
let comp = requireComponent(fileName).default;
comps[comp.name] = comp
})
export default comps;

View File

@ -0,0 +1,121 @@
<!--
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
-->
<template>
<container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList"
:index-of-parent-list="indexOfParentList">
<div :key="widget.id" class="tab-container"
:class="{'selected': selected}" @click.stop="selectWidget(widget)">
<el-tabs :type="widget.displayType" v-model="activeTab" @tab-click="onTabClick">
<el-tab-pane v-for="(tab, index) in widget.tabs" :key="index" :label="tab.options.label" :name="tab.options.name"
@click.native.stop="selectWidget(widget)">
<draggable :list="tab.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
handle=".drag-handler"
@add="(evt) => onContainerDragAdd(evt, tab.widgetList)"
@update="onContainerDragUpdate" :move="checkContainerMove">
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in tab.widgetList">
<template v-if="'container' === subWidget.category">
<component :is="subWidget.type + '-widget'" :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="tab.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></component>
</template>
<template v-else>
<component :is="subWidget.type + '-widget'" :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="tab.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></component>
</template>
</template>
</transition-group>
</draggable>
</el-tab-pane>
</el-tabs>
</div>
</container-wrapper>
</template>
<script>
import Draggable from 'vuedraggable'
import i18n from "@/utils/i18n"
import containerMixin from "@/components/form-designer/form-widget/container-widget/containerMixin"
import ContainerWrapper from "@/components/form-designer/form-widget/container-widget/container-wrapper"
import ContainerComponents from '@/components/form-designer/form-widget/container-widget/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
export default {
name: "tab-widget",
componentName: 'ContainerWidget',
mixins: [i18n, containerMixin],
components: {
ContainerWrapper,
Draggable,
...ContainerComponents,
...FieldComponents,
},
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
},
data() {
return {
activeTab: 'tab1',
//
}
},
computed: {
selected() {
return this.widget.id === this.designer.selectedId
},
customClass() {
return this.widget.options.customClass || ''
},
},
watch: {
//
},
mounted() {
//
},
methods: {
onTabClick(evt) {
console.log('onTabClick', evt)
let paneName = evt.name
this.widget.tabs.forEach((tp) => {
tp.options.active = tp.options.name === paneName;
})
},
}
}
</script>
<style lang="scss" scoped>
.tab-container {
//padding: 5px;
margin: 2px;
.form-widget-list {
min-height: 28px;
}
}
.tab-container.selected {
outline: 2px solid $--color-primary !important;
}
</style>

View File

@ -10,12 +10,12 @@
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in widget.widgetList">
<template v-if="'container' === subWidget.category">
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></container-widget>
<component :is="subWidget.type + '-widget'" :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></component>
</template>
<template v-else>
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></field-widget>
<component :is="subWidget.type + '-widget'" :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></component>
</template>
</template>
</transition-group>
@ -53,8 +53,9 @@
<script>
import Draggable from 'vuedraggable'
import FieldWidget from "@/components/form-designer/form-widget/field-widget";
import i18n from "@/utils/i18n";
import i18n from "@/utils/i18n"
import ContainerComponents from '@/components/form-designer/form-widget/container-widget/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
export default {
name: "TableCellWidget",
@ -62,7 +63,8 @@
mixins: [i18n],
components: {
Draggable,
FieldWidget,
...ContainerComponents,
...FieldComponents,
},
props: {
widget: Object,

View File

@ -0,0 +1,111 @@
<!--
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
-->
<template>
<container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList"
:index-of-parent-list="indexOfParentList">
<div :key="widget.id" class="table-container"
:class="[selected ? 'selected' : '', customClass]" @click.stop="selectWidget(widget)">
<table class="table-layout">
<tbody>
<tr v-for="(row, rowIdx) in widget.rows" :key="row.id">
<template v-for="(colWidget, colIdx) in row.cols">
<table-cell-widget v-if="!colWidget.merged" :widget="colWidget" :designer="designer"
:key="colWidget.id" :parent-list="widget.cols"
:row-index="rowIdx" :row-length="widget.rows.length"
:col-index="colIdx" :col-length="row.cols.length"
:col-array="row.cols" :row-array="widget.rows"
:parent-widget="widget"></table-cell-widget>
</template>
</tr>
</tbody>
</table>
</div>
</container-wrapper>
</template>
<script>
import i18n from "@/utils/i18n"
import containerMixin from "@/components/form-designer/form-widget/container-widget/containerMixin"
import ContainerWrapper from "@/components/form-designer/form-widget/container-widget/container-wrapper"
import TableCellWidget from "@/components/form-designer/form-widget/container-widget/table-cell-widget"
export default {
name: "table-widget",
componentName: 'ContainerWidget',
mixins: [i18n, containerMixin],
components: {
ContainerWrapper,
TableCellWidget,
},
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
},
computed: {
selected() {
return this.widget.id === this.designer.selectedId
},
customClass() {
return this.widget.options.customClass || ''
},
},
watch: {
//
},
mounted() {
//
},
methods: {
}
}
</script>
<style lang="scss" scoped>
div.table-container {
padding: 5px;
border: 1px dashed #336699;
box-sizing: border-box;
table.table-layout {
width: 100%;
text-align: center;
//border: 1px solid #c8ebfb;
border-collapse: collapse;
table-layout: fixed;
::v-deep td {
height: 48px;
border: 1px dashed #336699;
padding: 3px;
display: table-cell;
}
.form-widget-list {
border: 1px dashed #336699;
min-height: 36px;
}
}
}
.table-container.selected {
outline: 2px solid $--color-primary !important;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,87 @@
<template>
<static-content-wrapper :designer="designer" :field="field" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-button ref="fieldEditor" :type="field.options.type"
:plain="field.options.plain" :round="field.options.round"
:circle="field.options.circle" :icon="field.options.icon"
:disabled="field.options.disabled"
@click.native="handleButtonWidgetClick">
{{field.options.label}}</el-button>
</static-content-wrapper>
</template>
<script>
import StaticContentWrapper from './static-content-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "button-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
StaticContentWrapper,
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.registerToRefList()
this.initEventHandler()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* static-content-wrapper *//
</style>

View File

@ -0,0 +1,104 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-cascader ref="fieldEditor" :options="field.options.optionItems" v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled"
:size="field.options.size"
:clearable="field.options.clearable"
:filterable="field.options.filterable"
:placeholder="field.options.placeholder || i18nt('render.hint.selectPlaceholder')"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-cascader>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "cascader-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initOptionItems()
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-checkbox-group ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled" :size="field.options.size"
@change="handleChangeEvent">
<template v-if="!!field.options.buttonStyle">
<el-checkbox-button v-for="(item, index) in field.options.optionItems" :key="index" :label="item.value"
:disabled="item.disabled" :border="field.options.border"
:style="{display: field.options.displayStyle}">{{item.label}}</el-checkbox-button>
</template>
<template v-else>
<el-checkbox v-for="(item, index) in field.options.optionItems" :key="index" :label="item.value"
:disabled="item.disabled" :border="field.options.border"
:style="{display: field.options.displayStyle}">{{item.label}}</el-checkbox>
</template>
</el-checkbox-group>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "checkbox-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initOptionItems()
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
</style>

View File

@ -0,0 +1,99 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-color-picker ref="fieldEditor" v-model="fieldModel"
:size="field.options.size"
:disabled="field.options.disabled"
@change="handleChangeEvent">
</el-color-picker>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "color-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-date-picker ref="fieldEditor" :type="field.options.type" v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled" :readonly="field.options.readonly"
:size="field.options.size"
:clearable="field.options.clearable" :editable="field.options.editable"
:format="field.options.format" :value-format="field.options.valueFormat"
:start-placeholder="field.options.startPlaceholder || i18nt('render.hint.startDatePlaceholder')"
:end-placeholder="field.options.endPlaceholder || i18nt('render.hint.endDatePlaceholder')"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-date-picker>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "date-range-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-date-picker ref="fieldEditor" :type="field.options.type" v-model="fieldModel" class="full-width-input"
:readonly="field.options.readonly" :disabled="field.options.disabled"
:size="field.options.size"
:clearable="field.options.clearable" :editable="field.options.editable"
:format="field.options.format" :value-format="field.options.valueFormat"
:placeholder="field.options.placeholder || i18nt('render.hint.datePlaceholder')"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-date-picker>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "date-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,83 @@
<template>
<static-content-wrapper :designer="designer" :field="field" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-divider ref="fieldEditor" direction="horizontal" :content-position="field.options.contentPosition">
{{field.options.label}}</el-divider>
</static-content-wrapper>
</template>
<script>
import StaticContentWrapper from './static-content-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "divider-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
StaticContentWrapper,
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.registerToRefList()
this.initEventHandler()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* static-content-wrapper *//
</style>

View File

@ -0,0 +1,451 @@
import {deepClone} from "@/utils/util";
export default {
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
computed: {
subFormName() {
return !!this.parentWidget ? this.parentWidget.options.name : ''
},
subFormItemFlag() {
return !!this.parentWidget ? this.parentWidget.type === 'sub-form' : false
},
formModel: {
cache: false,
get() {
return this.globalModel.formModel
}
},
},
methods: {
//--------------------- 组件内部方法 begin ------------------//
initFieldModel() {
if (this.field.formItemFlag === false) {
return
}
if (!!this.subFormItemFlag && !this.designState) { //SubForm子表单组件需要特殊处理
let subFormData = this.formModel[this.subFormName]
if (((subFormData === undefined) || (subFormData[this.subFormRowIndex] === undefined) ||
(subFormData[this.subFormRowIndex][this.field.options.name] === undefined)) &&
(this.field.options.defaultValue !== undefined)) {
this.fieldModel = this.field.options.defaultValue
subFormData[this.subFormRowIndex][this.field.options.name] = this.field.options.defaultValue
} else if (subFormData[this.subFormRowIndex][this.field.options.name] === undefined) {
this.fieldModel = null
subFormData[this.subFormRowIndex][this.field.options.name] = null
} else {
this.fieldModel = subFormData[this.subFormRowIndex][this.field.options.name]
}
/* 主动触发子表单内field-widget的onChange事件 */
setTimeout(() => { //延时触发onChange事件, 便于更新计算字段!!
this.handleOnChangeForSubForm(this.fieldModel, this.oldFieldValue, subFormData, this.subFormRowId)
}, 800)
this.oldFieldValue = deepClone(this.fieldModel)
this.initFileList() //处理图片上传、文件上传字段
return
}
if ((this.formModel[this.field.options.name] === undefined) &&
(this.field.options.defaultValue !== undefined)) {
this.fieldModel = this.field.options.defaultValue
} else if (this.formModel[this.field.options.name] === undefined) { //如果formModel为空对象则初始化字段值为null!!
this.formModel[this.field.options.name] = null
} else {
this.fieldModel = this.formModel[this.field.options.name]
}
this.oldFieldValue = deepClone(this.fieldModel)
this.initFileList() //处理图片上传、文件上传字段
},
initFileList() { //初始化上传组件的已上传文件列表
if ( ((this.field.type !== 'picture-upload') && (this.field.type !== 'file-upload')) || (this.designState === true) ) {
return
}
if (!!this.fieldModel) {
if (Array.isArray(this.fieldModel)) {
this.fileList = deepClone(this.fieldModel)
} else {
this.fileList.splice(0, 0, deepClone(this.fieldModel))
}
}
},
initEventHandler() {
this.$on('setFormData', function (newFormData) {
console.log('formModel of globalModel----------', this.globalModel.formModel)
if (!this.subFormItemFlag) {
this.setValue(newFormData[this.field.options.name])
}
})
this.$on('field-value-changed', function (values) {
if (!!this.subFormItemFlag) {
let subFormData = this.formModel[this.subFormName]
this.handleOnChangeForSubForm(values[0], values[1], subFormData, this.subFormRowId)
} else {
this.handleOnChange(values[0], values[1])
}
})
},
handleOnCreated() {
if (!!this.field.options.onCreated) {
let customFunc = new Function(this.field.options.onCreated)
customFunc.call(this)
}
},
handleOnMounted() {
if (!!this.field.options.onMounted) {
let mountFunc = new Function(this.field.options.onMounted)
mountFunc.call(this)
}
},
registerToRefList(oldRefName) {
if ((this.refList !== null) && !!this.field.options.name) {
if (this.subFormItemFlag && !this.designState) { //处理子表单元素(且非设计状态)
if (!!oldRefName) {
delete this.refList[oldRefName + '@row' + this.subFormRowId]
}
this.refList[this.field.options.name + '@row' + this.subFormRowId] = this
} else {
if (!!oldRefName) {
delete this.refList[oldRefName]
}
this.refList[this.field.options.name] = this
}
}
},
unregisterFromRefList() { //销毁组件时注销组件ref
if ((this.refList !== null) && !!this.field.options.name) {
let oldRefName = this.field.options.name
if (this.subFormItemFlag && !this.designState) { //处理子表单元素(且非设计状态)
delete this.refList[oldRefName + '@row' + this.subFormRowId]
} else {
delete this.refList[oldRefName]
}
}
},
initOptionItems() {
if (this.designState) {
return
}
if ((this.field.type === 'radio') || (this.field.type === 'checkbox')
|| (this.field.type === 'select') || (this.field.type === 'cascader')) {
if (!!this.globalOptionData && this.globalOptionData.hasOwnProperty(this.field.options.name)) {
this.loadOptions( this.globalOptionData[this.field.options.name] )
}
}
},
refreshDefaultValue() {
if ((this.designState === true) && (this.field.options.defaultValue !== undefined)) {
this.fieldModel = this.field.options.defaultValue
}
},
buildFieldRules() {
this.rules.splice(0, this.rules.length) //清空已有
if (!!this.field.options.required) {
this.rules.push({
required: true,
message: this.i18nt('render.hint.fieldRequired'),
})
}
if (!!this.field.options.validation) {
let vldName = this.field.options.validation
if (!!FormValidators[vldName]) {
this.rules.push({
validator: FormValidators[vldName],
trigger: ['blur', 'change'],
label: this.field.options.label,
errorMsg: this.field.options.validationHint
})
} else {
this.rules.push({
validator: FormValidators['regExp'],
trigger: ['blur', 'change'],
regExp: vldName,
label: this.field.options.label,
errorMsg: this.field.options.validationHint
})
}
}
if (!!this.field.options.onValidate) {
let customFn = new Function('rule', 'value', 'callback', this.field.options.onValidate)
this.rules.push({
validator: customFn,
trigger: ['blur', 'change'],
label: this.field.options.label
})
}
},
disableOptionOfList(optionList, optionValue) {
if (!!optionList && (optionList.length > 0)) {
optionList.forEach(opt => {
if (opt.value === optionValue) {
opt.disabled = true
}
})
}
},
enableOptionOfList(optionList, optionValue) {
if (!!optionList && (optionList.length > 0)) {
optionList.forEach(opt => {
if (opt.value === optionValue) {
opt.disabled = false
}
})
}
},
//--------------------- 组件内部方法 end ------------------//
//--------------------- 事件处理 begin ------------------//
emitFieldDataChange(newValue, oldValue) {
this.$emit('field-value-changed', [newValue, oldValue])
/* 必须用dispatch向指定父组件派发消息 */
this.dispatch('VFormRender', 'fieldChange',
[this.field.options.name, newValue, oldValue, this.subFormName, this.subFormRowIndex])
},
syncUpdateFormModel(value) {
if (!!this.designState) {
return
}
if (!!this.subFormItemFlag) {
let subFormData = this.formModel[this.subFormName] || [{}]
let subFormDataRow = subFormData[this.subFormRowIndex]
subFormDataRow[this.field.options.name] = value
} else {
this.formModel[this.field.options.name] = value
}
},
handleChangeEvent(value) {
this.syncUpdateFormModel(value)
this.emitFieldDataChange(value, this.oldFieldValue)
//number组件一般不会触发focus事件故此处需要手工赋值oldFieldValue
this.oldFieldValue = deepClone(value) /* oldFieldValue需要在initFieldModel()方法中赋初值!! */
},
handleFocusCustomEvent(event) {
this.oldFieldValue = deepClone(this.fieldModel) //保存修改change之前的值
if (!!this.field.options.onFocus) {
let customFn = new Function('event', this.field.options.onFocus)
customFn.call(this, event)
}
},
handleBlurCustomEvent(event) {
if (!!this.field.options.onBlur) {
let customFn = new Function('event', this.field.options.onBlur)
customFn.call(this, event)
}
},
handleInputCustomEvent(value) {
if (!!this.field.options.onInput) {
let customFn = new Function('value', this.field.options.onInput)
customFn.call(this, value)
}
},
emitAppendButtonClick() {
/* 必须调用mixins中的dispatch方法逐级向父组件发送消息 */
this.dispatch('VFormRender', 'appendButtonClick', [this]);
},
handleOnChange(val, oldVal) { //自定义onChange事件
if (!!this.field.options.onChange) {
let changeFn = new Function('value', 'oldValue', this.field.options.onChange)
changeFn.call(this, val, oldVal)
}
},
handleOnChangeForSubForm(val, oldVal, subFormData, rowId) { //子表单自定义onChange事件
if (!!this.field.options.onChange) {
let changeFn = new Function('value', 'oldValue', 'subFormData', 'rowId', this.field.options.onChange)
changeFn.call(this, val, oldVal, subFormData, rowId)
}
},
handleButtonWidgetClick() {
if (!!this.designState) { //设计状态不触发点击事件
return
}
if (!!this.field.options.onClick) {
let changeFn = new Function(this.field.options.onClick)
changeFn.call(this)
} else {
this.dispatch('VFormRender', 'buttonClick', [this]);
}
},
remoteQuery(keyword) {
if (!!this.field.options.onRemoteQuery) {
let remoteFn = new Function('keyword', this.field.options.onRemoteQuery)
remoteFn.call(this, keyword)
}
},
//--------------------- 事件处理 end ------------------//
//--------------------- 以下为组件支持外部调用的API方法 begin ------------------//
/* 提示:用户可自行扩充这些方法!!! */
getFormRef() { /* 获取VFrom引用必须在VForm组件created之后方可调用 */
return this.refList['v_form_ref']
},
getWidgetRef(widgetName, showError) {
let foundRef = this.refList[widgetName]
if (!foundRef && !!showError) {
this.$message.error(this.i18nt('render.hint.refNotFound') + widgetName)
}
return foundRef
},
getFieldEditor() { //获取内置的el表单组件
return this.$refs['fieldEditor']
},
/*
注意VFormRender的setFormData方法不会触发子表单内field-widget的setValue方法
因为setFormData方法调用后子表单内所有field-widget组件已被清空接收不到setFormData事件
* */
setValue(newValue) {
/* if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
this.fileList = newValue
} else */ if (!!this.field.formItemFlag) {
let oldValue = deepClone(this.fieldModel)
this.fieldModel = newValue
this.initFileList()
this.syncUpdateFormModel(newValue)
this.emitFieldDataChange(newValue, oldValue)
}
},
getValue() {
/* if ((this.field.type === 'picture-upload') || (this.field.type === 'file-upload')) {
return this.fileList
} else */ {
return this.fieldModel
}
},
resetField() {
let defaultValue = this.field.options.defaultValue
this.setValue(defaultValue)
},
setWidgetOption(optionName, optionValue) { //通用组件选项修改API
if (this.field.options.hasOwnProperty(optionName)) {
this.field.options[optionName] = optionValue
//TODO: 是否重新构建组件??有些属性修改后必须重新构建组件才能生效,比如字段校验规则。
}
},
setReadonly(flag) {
this.field.options.readonly = flag
},
setDisabled(flag) {
this.field.options.disabled = flag
},
setAppendButtonVisible(flag) {
this.field.options.appendButton = flag
},
setAppendButtonDisabled(flag) {
this.field.options.appendButtonDisabled = flag
},
setHidden(flag) {
this.field.options.hidden = flag
},
setRequired(flag) {
this.field.options.required = flag
this.buildFieldRules()
},
setLabel(newLabel) {
this.field.options.label = newLabel
},
focus() {
if (!!this.getFieldEditor() && !!this.getFieldEditor().focus) {
this.getFieldEditor().focus()
}
},
clearSelectedOptions() { //清空已选选项
if ((this.field.type !== 'checkbox') && (this.field.type !== 'radio') && (this.field.type !== 'select')) {
return
}
if ((this.field.type === 'checkbox') ||
((this.field.type === 'select') && this.field.options.multiple)) {
this.fieldModel = []
} else {
this.fieldModel = ''
}
},
loadOptions(options) {
this.field.options.optionItems = deepClone(options)
this.clearSelectedOptions() //清空已选选项
},
disableOption(optionValue) {
this.disableOptionOfList(this.field.options.optionItems, optionValue)
},
enableOption(optionValue) {
this.enableOptionOfList(this.field.options.optionItems, optionValue)
},
setUploadHeader(name, value) {
this.$set(this.uploadHeaders, name, value)
},
setUploadData(name, value) {
this.$set(this.uploadData, name, value)
},
setToolbar(customToolbar) {
this.customToolbar = customToolbar
},
//--------------------- 以上为组件支持外部调用的API方法 end ------------------//
}
}

View File

@ -0,0 +1,264 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-upload ref="fieldEditor" :disabled="field.options.disabled"
:style="styleVariables" class="dynamicPseudoAfter"
:action="field.options.uploadURL" :headers="uploadHeaders" :data="uploadData"
:with-credentials="field.options.withCredentials"
:multiple="field.options.multipleSelect" :file-list="fileList"
:show-file-list="field.options.showFileList" :class="{'hideUploadDiv': uploadBtnHidden}"
:limit="field.options.limit" :on-exceed="handleFileExceed" :before-upload="beforeFileUpload"
:on-success="handleFileUpload" :on-error="handelUploadError" :on-remove="handleFileRemove">
<div slot="tip" class="el-upload__tip"
v-if="!!field.options.uploadTip">{{field.options.uploadTip}}</div>
<i slot="default" class="el-icon-plus avatar-uploader-icon"></i>
<div slot="file" slot-scope="{file}" class="upload-file-list">
<span class="upload-file-name" :title="file.name">{{file.name}}</span>
<a :href="file.url" download="">
<i class="el-icon-download file-action" title="i18nt('render.hint.downloadFile')"></i></a>
<i class="el-icon-delete file-action" title="i18nt('render.hint.removeFile')" v-if="!field.options.disabled"
@click="removeUploadFile(file.name)"></i>
</div>
</el-upload>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import {deepClone} from "@/utils/util";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
let selectFileText = "'" + translate('render.hint.selectFile') + "'"
export default {
name: "file-upload-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
uploadHeaders: {},
uploadData: {
key: '', //
//token: '', //token
//policy: '', //policy
//authorization: '', //
},
fileList: [], //
uploadBtnHidden: false,
styleVariables: {
'--select-file-action': selectFileText,
},
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
handleFileExceed() {
//let uploadLimit = this.field.options.limit
this.$message.warning(eval('`' + this.i18nt('render.hint.uploadExceed') + '`'));
},
updateUploadFieldModelAndEmitDataChange() {
let oldValue = deepClone(this.fieldModel)
this.fieldModel = deepClone(this.fileList)
this.syncUpdateFormModel(this.fieldModel)
this.emitFieldDataChange(this.fieldModel, oldValue)
},
beforeFileUpload(file) {
let fileTypeCheckResult = false
let extFileName = file.name.substring(file.name.lastIndexOf('.') + 1)
if (!!this.field.options && !!this.field.options.fileTypes) {
let uploadFileTypes = this.field.options.fileTypes
if (uploadFileTypes.length > 0) {
fileTypeCheckResult = uploadFileTypes.some( (ft) => {
return extFileName.toLowerCase() === ft.toLowerCase()
})
}
}
if (!fileTypeCheckResult) {
this.$message.error(this.i18nt('render.hint.unsupportedFileType') + extFileName)
return false;
}
let fileSizeCheckResult = false
let uploadFileMaxSize = 5 //5MB
if (!!this.field.options && !!this.field.options.fileMaxSize) {
uploadFileMaxSize = this.field.options.fileMaxSize
}
fileSizeCheckResult = file.size / 1024 / 1024 <= uploadFileMaxSize
if (!fileSizeCheckResult) {
this.$message.error(this.i18nt('render.hint.fileSizeExceed') + uploadFileMaxSize + 'MB')
return false;
}
this.uploadData.key = file.name
return this.handleOnBeforeUpload(file)
},
handleFileUpload(res, file, fileList) {
if (!!this.field.options.onUploadSuccess) {
let mountFunc = new Function('result', 'file', 'fileList', this.field.options.onUploadSuccess)
mountFunc.call(this, res, file, fileList)
} else {
if (file.status === 'success') {
this.fileList.push(file)
this.updateUploadFieldModelAndEmitDataChange()
this.uploadBtnHidden = this.fileList.length >= this.field.options.limit
}
}
},
handleFileRemove(file, fileList) {
let foundIdx = -1
this.fileList.forEach((tf,idx) => {
if (tf.name === file.name) {
foundIdx = idx
}
})
this.fileList = fileList
this.updateUploadFieldModelAndEmitDataChange()
this.uploadBtnHidden = fileList.length >= this.field.options.limit
},
removeUploadFile(fileName) {
let foundIdx = -1
this.fileList.forEach((file,idx) => {
if (file.name === fileName) {
foundIdx = idx
}
})
if (foundIdx >= 0) {
this.fileList.splice(foundIdx, 1)
this.updateUploadFieldModelAndEmitDataChange()
this.uploadBtnHidden = this.fileList.length >= this.field.options.limit
}
},
handelUploadError(err, file, fileList) {
if (!!this.field.options.onUploadError) {
let customFn = new Function('error', 'file', 'fileList', this.field.options.onUploadError)
customFn.call(this, err, file, fileList)
} else {
this.$message({
message: this.i18nt('render.hint.uploadError') + err,
duration: 3000,
type: 'error',
})
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
.dynamicPseudoAfter ::v-deep .el-upload.el-upload--text {
color: $--color-primary;
font-size: 12px;
.el-icon-plus:after {
content: var(--select-file-action);
}
}
.hideUploadDiv {
::v-deep div.el-upload--picture-card { /* 隐藏最后的图片上传按钮 */
display: none;
}
::v-deep div.el-upload--text { /* 隐藏最后的文件上传按钮 */
display: none;
}
::v-deep div.el-upload__tip { /* 隐藏最后的文件上传按钮 */
display: none;
}
}
.upload-file-list {
font-size: 12px;
.file-action {
color: $--color-primary;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,331 @@
<!--
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
-->
<template>
<div class="field-wrapper" :class="{'design-time-bottom-margin': !!this.designer}">
<el-form-item v-if="!!field.formItemFlag" :label="label" :label-width="labelWidth + 'px'"
:title="field.options.labelTooltip"
v-show="!field.options.hidden || (designState === true)"
:rules="rules" :prop="getPropName()"
:class="[selected ? 'selected' : '', labelAlign, customClass, field.options.required ? 'required' : '']"
@click.native.stop="selectField(field)">
<span v-if="!!field.options.labelIconClass" slot="label" class="custom-label">
<template v-if="field.options.labelIconPosition === 'front'">
<template v-if="!!field.options.labelTooltip">
<el-tooltip :content="field.options.labelTooltip" effect="light">
<i :class="field.options.labelIconClass"></i></el-tooltip>{{label}}</template>
<template v-else>
<i :class="field.options.labelIconClass"></i>{{label}}</template>
</template>
<template v-else-if="field.options.labelIconPosition === 'rear'">
<template v-if="!!field.options.labelTooltip">
{{label}}<el-tooltip :content="field.options.labelTooltip" effect="light">
<i :class="field.options.labelIconClass"></i></el-tooltip></template>
<template v-else>
{{label}}<i :class="field.options.labelIconClass"></i></template>
</template>
</span>
<slot></slot>
</el-form-item>
<template v-if="!!this.designer">
<div class="field-action" v-if="designer.selectedId === field.id">
<i class="el-icon-back" :title="i18nt('designer.hint.selectParentWidget')" @click.stop="selectParentWidget(field)"></i>
<i class="el-icon-top" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveUpWidget')"
@click.stop="moveUpWidget(field)"></i>
<i class="el-icon-bottom" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveDownWidget')"
@click.stop="moveDownWidget(field)"></i>
<i class="el-icon-delete" :title="i18nt('designer.hint.remove')" @click.stop="removeFieldWidget"></i>
</div>
<div class="drag-handler background-opacity" v-if="designer.selectedId === field.id">
<i class="el-icon-rank" :title="i18nt('designer.hint.dragHandler')"></i>
<i>{{i18nt('designer.widgetLabel.' + field.type)}}</i>
<i v-if="field.options.hidden === true" class="iconfont icon-hide"></i>
</div>
</template>
</div>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "form-item-wrapper",
mixins: [i18n],
props: {
field: Object,
designer: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
rules: Array,
},
inject: ['formConfig'],
computed: {
selected() {
return !!this.designer && this.field.id === this.designer.selectedId
},
label() {
if (!!this.field.options.labelHidden) {
return ''
}
return this.field.options.label
},
labelWidth() {
if (!!this.field.options.labelHidden) {
return 0
}
if (!!this.field.options.labelWidth) {
return this.field.options.labelWidth
}
if (!!this.designer) {
return this.designer.formConfig.labelWidth
} else {
return this.formConfig.labelWidth
}
},
labelAlign() {
if (!!this.field.options.labelAlign) {
return this.field.options.labelAlign
}
if (!!this.designer) {
return this.designer.formConfig.labelAlign || 'label-left-align'
} else {
return this.formConfig.labelAlign || 'label-left-align'
}
},
customClass() {
return !!this.field.options.customClass ? this.field.options.customClass.join(' ') : ''
},
subFormName() {
return !!this.parentWidget ? this.parentWidget.options.name : ''
},
subFormItemFlag() {
return !!this.parentWidget ? this.parentWidget.type === 'sub-form' : false
},
},
created() {
//
},
methods: {
selectField(field) {
if (!!this.designer) {
this.designer.setSelected(field)
this.designer.emitEvent('field-selected', this.parentWidget) //
}
},
selectParentWidget() {
if (this.parentWidget) {
this.designer.setSelected(this.parentWidget)
} else {
this.designer.clearSelected()
}
},
moveUpWidget() {
this.designer.moveUpWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
moveDownWidget() {
this.designer.moveDownWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
removeFieldWidget() {
if (!!this.parentList) {
let nextSelected = null
if (this.parentList.length === 1) {
if (!!this.parentWidget) {
nextSelected = this.parentWidget
}
} else if (this.parentList.length === (1 + this.indexOfParentList)) {
nextSelected = this.parentList[this.indexOfParentList - 1]
} else {
nextSelected = this.parentList[this.indexOfParentList + 1]
}
this.$nextTick(() => {
this.parentList.splice(this.indexOfParentList, 1)
//if (!!nextSelected) {
this.designer.setSelected(nextSelected)
//}
this.designer.emitHistoryChange()
})
}
},
getPropName() {
if (this.subFormItemFlag && !this.designState) {
return this.subFormName + "." + this.subFormRowIndex + "." + this.field.options.name + ""
} else {
return this.field.options.name
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss";
.design-time-bottom-margin {
margin-bottom: 5px;
}
.field-wrapper {
position: relative;
.field-action{
position: absolute;
//bottom: -24px;
bottom: 0;
right: -2px;
height: 22px;
line-height: 22px;
background: $--color-primary;
z-index: 9;
i {
font-size: 14px;
color: #fff;
margin: 0 5px;
cursor: pointer;
}
}
.drag-handler {
position: absolute;
top: 0;
//bottom: -22px; /* */
left: -1px;
height: 20px;
line-height: 20px;
//background: $--color-primary;
z-index: 9;
i {
font-size: 12px;
font-style: normal;
color: #fff;
margin: 4px;
cursor: move;
}
&:hover {
//opacity: 1;
background: $--color-primary;
}
}
}
.el-form-item {
//margin-bottom: 0 !important;
//margin-bottom: 6px;
//margin-top: 2px;
position: relative;
::v-deep .el-form-item__label {
white-space: nowrap;
text-overflow: ellipsis;
}
::v-deep .el-form-item__content {
//position: unset; /* TODO: */
}
span.custom-label i {
margin: 0 3px;
}
/* 隐藏Chrome浏览器中el-input数字输入框右侧的上下调整小箭头 */
::v-deep .hide-spin-button input::-webkit-outer-spin-button,
::v-deep .hide-spin-button input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
}
/* 隐藏Firefox浏览器中el-input数字输入框右侧的上下调整小箭头 */
::v-deep .hide-spin-button input[type="number"] {
-moz-appearance: textfield;
}
}
.required ::v-deep .el-form-item__label::before {
content: '*';
color: #F56C6C;
margin-right: 4px;
}
.static-content-item {
min-height: 20px;
display: flex; /* 垂直居中 */
align-items: center; /* 垂直居中 */
::v-deep .el-divider--horizontal {
margin: 0;
}
}
.el-form-item.selected, .static-content-item.selected {
outline: 2px solid $--color-primary;
}
::v-deep .label-left-align .el-form-item__label {
text-align: left;
}
::v-deep .label-center-align .el-form-item__label {
text-align: center;
}
::v-deep .label-right-align .el-form-item__label {
text-align: right;
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<static-content-wrapper :designer="designer" :field="field" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<div ref="fieldEditor" v-html="field.options.htmlContent"></div>
</static-content-wrapper>
</template>
<script>
import StaticContentWrapper from './static-content-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "html-text-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
StaticContentWrapper,
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.registerToRefList()
this.initEventHandler()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* static-content-wrapper *//
</style>

View File

@ -0,0 +1,10 @@
const requireComponent = require.context('./', false, /\w+\.vue$/)
let comps = {}
requireComponent.keys().map(fileName => {
let comp = requireComponent(fileName).default;
comps[comp.name] = comp
})
export default comps;

View File

@ -0,0 +1,112 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-input ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled" :readonly="field.options.readonly"
:size="field.options.size" class="hide-spin-button"
:type="inputType"
:show-password="field.options.showPassword"
:placeholder="field.options.placeholder"
:clearable="field.options.clearable"
:minlength="field.options.minLength" :maxlength="field.options.maxLength"
:show-word-limit="field.options.showWordLimit"
:prefix-icon="field.options.prefixIcon" :suffix-icon="field.options.suffixIcon"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent" @input="handleInputCustomEvent"
@change="handleChangeEvent">
<el-button slot="append" v-if="field.options.appendButton" :disabled="field.options.disabled || field.options.appendButtonDisabled"
:class="field.options.buttonIcon" @click.native="emitAppendButtonClick"></el-button>
</el-input>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "input-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
inputType() {
if (this.field.options.type === 'number') {
return 'text' //inputtypenumberv-model
}
return this.field.options.type
},
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
</style>

View File

@ -0,0 +1,103 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-input-number ref="fieldEditor" v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled"
:size="field.options.size" :controls-position="field.options.controlsPosition"
:placeholder="field.options.placeholder"
:min="field.options.min" :max="field.options.max"
:precision="field.options.precision" :step="field.options.step"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-input-number>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "number-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,219 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-upload ref="fieldEditor" :disabled="field.options.disabled"
:action="field.options.uploadURL" :headers="uploadHeaders" :data="uploadData"
:with-credentials="field.options.withCredentials"
:multiple="field.options.multipleSelect" :file-list="fileList" :show-file-list="field.options.showFileList"
list-type="picture-card" :class="{'hideUploadDiv': uploadBtnHidden}"
:limit="field.options.limit" :on-exceed="handlePictureExceed"
:before-upload="beforePictureUpload"
:on-success="handlePictureUpload" :on-error="handelUploadError" :on-remove="handlePictureRemove">
<div slot="tip" class="el-upload__tip"
v-if="!!field.options.uploadTip">{{field.options.uploadTip}}</div>
<i class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import {deepClone} from "@/utils/util";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "picture-upload-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
uploadHeaders: {},
uploadData: {
key: '', //
//token: '', //token
//policy: '', //policy
//authorization: '', //
},
fileList: [], //
uploadBtnHidden: false,
// styleVariables: {
// '--select-file-action': selectFileText,
// },
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
handlePictureExceed() {
//let uploadLimit = this.field.options.limit
this.$message.warning(eval('`' + this.i18nt('render.hint.uploadExceed') + '`'));
},
updateUploadFieldModelAndEmitDataChange() {
let oldValue = deepClone(this.fieldModel)
this.fieldModel = deepClone(this.fileList)
this.syncUpdateFormModel(this.fieldModel)
this.emitFieldDataChange(this.fieldModel, oldValue)
},
beforePictureUpload(file) {
let fileTypeCheckResult = false
if (!!this.field.options && !!this.field.options.fileTypes) {
let uploadFileTypes = this.field.options.fileTypes
if (uploadFileTypes.length > 0) {
fileTypeCheckResult = uploadFileTypes.some( (ft) => {
return file.type === 'image/' + ft
})
}
}
if (!fileTypeCheckResult) {
this.$message.error(this.i18nt('render.hint.unsupportedFileType') + file.type)
return false;
}
let fileSizeCheckResult = false
let uploadFileMaxSize = 5 //5MB
if (!!this.field.options && !!this.field.options.fileMaxSize) {
uploadFileMaxSize = this.field.options.fileMaxSize
}
fileSizeCheckResult = file.size / 1024 / 1024 <= uploadFileMaxSize
if (!fileSizeCheckResult) {
this.$message.error(this.$('render.hint.fileSizeExceed') + uploadFileMaxSize + 'MB')
return false;
}
this.uploadData.key = file.name
return this.handleOnBeforeUpload(file)
},
handlePictureUpload(res, file, fileList) {
if (!!this.field.options.onUploadSuccess) {
let customFn = new Function('result', 'file', 'fileList', this.field.options.onUploadSuccess)
customFn.call(this, res, file, fileList)
} else {
if (file.status === 'success') {
this.fileList.push(file)
this.updateUploadFieldModelAndEmitDataChange()
this.uploadBtnHidden = this.fileList.length >= this.field.options.limit
}
}
},
handlePictureRemove(file, fileList) {
let foundIdx = -1
this.fileList.forEach((tf,idx) => {
if (tf.name === file.name) {
foundIdx = idx
}
})
this.fileList = fileList
this.updateUploadFieldModelAndEmitDataChange()
this.uploadBtnHidden = fileList.length >= this.field.options.limit
},
handelUploadError(err, file, fileList) {
if (!!this.field.options.onUploadError) {
let customFn = new Function('error', 'file', 'fileList', this.field.options.onUploadError)
customFn.call(this, err, file, fileList)
} else {
this.$message({
message: this.i18nt('render.hint.uploadError') + err,
duration: 3000,
type: 'error',
})
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
.hideUploadDiv {
::v-deep div.el-upload--picture-card { /* 隐藏最后的图片上传按钮 */
display: none;
}
::v-deep div.el-upload--text { /* 隐藏最后的文件上传按钮 */
display: none;
}
::v-deep div.el-upload__tip { /* 隐藏最后的文件上传按钮 */
display: none;
}
}
</style>

View File

@ -0,0 +1,105 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-radio-group ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled" :size="field.options.size"
@change="handleChangeEvent">
<template v-if="!!field.options.buttonStyle">
<el-radio-button v-for="(item, index) in field.options.optionItems" :key="index" :label="item.value"
:disabled="item.disabled" :border="field.options.border"
:style="{display: field.options.displayStyle}">{{item.label}}</el-radio-button>
</template>
<template v-else>
<el-radio v-for="(item, index) in field.options.optionItems" :key="index" :label="item.value"
:disabled="item.disabled" :border="field.options.border"
:style="{display: field.options.displayStyle}">{{item.label}}</el-radio>
</template>
</el-radio-group>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "radio-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initOptionItems()
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
</style>

View File

@ -0,0 +1,102 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-rate ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled"
:max="field.options.max"
:low-threshold="field.options.lowThreshold" :high-threshold="field.options.highThreshold"
:allow-half="field.options.allowHalf"
:show-text="field.options.showText" :show-score="field.options.showScore"
@change="handleChangeEvent">
</el-rate>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "rate-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,121 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<vue-editor ref="fieldEditor" v-model="fieldModel" :editor-toolbar="customToolbar"
:disabled="field.options.disabled" :placeholder="field.options.placeholder"
@text-change="handleRichEditorChangeEvent"
@focus="handleRichEditorFocusEvent" @blur="handleRichEditorBlurEvent">
</vue-editor>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import {deepClone} from "@/utils/util";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "rich-editor-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
VueEditor: resolve => { //
require(['vue2-editor'], ({VueEditor}) => resolve(VueEditor))
}
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
customToolbar: [], //
valueChangedFlag: false, //vue2-editor
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
handleRichEditorChangeEvent() {
this.valueChangedFlag = true
},
handleRichEditorFocusEvent() {
this.oldFieldValue = deepClone(this.fieldModel)
},
handleRichEditorBlurEvent() {
if (this.valueChangedFlag) {
this.emitFieldDataChange(this.fieldModel, this.oldFieldValue)
this.valueChangedFlag = false
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,115 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-select ref="fieldEditor" v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled"
:size="field.options.size"
:clearable="field.options.clearable"
:filterable="field.options.filterable"
:allow-create="field.options.allowCreate"
:default-first-option="allowDefaultFirstOption"
:automatic-dropdown="field.options.automaticDropdown"
:multiple="field.options.multiple" :multiple-limit="field.options.multipleLimit"
:placeholder="field.options.placeholder || i18nt('render.hint.selectPlaceholder')"
:remote="this.field.options.remote" :remote-method="remoteQuery"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
<el-option v-for="item in field.options.optionItems" :key="item.value" :label="item.label"
:value="item.value" :disabled="item.disabled">
</el-option>
</el-select>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "select-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
allowDefaultFirstOption() {
return (!!this.field.options.filterable && !!this.field.options.allowCreate)
},
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initOptionItems()
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-slider ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled"
:min="field.options.min" :max="field.options.max" :step="field.options.step"
:range="field.options.range" :vertical="field.options.vertical"
@change="handleChangeEvent">
</el-slider>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "slider-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,185 @@
<template>
<div class="field-wrapper" :class="{'design-time-bottom-margin': !!this.designer}">
<div class="static-content-item" v-show="!field.options.hidden || (designState === true)"
:class="{'selected': selected}" @click.stop="selectField(field)">
<slot></slot>
</div>
<template v-if="!!this.designer">
<div class="field-action" v-if="designer.selectedId === field.id">
<i class="el-icon-back" :title="i18nt('designer.hint.selectParentWidget')" @click.stop="selectParentWidget(field)"></i>
<i class="el-icon-top" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveUpWidget')"
@click.stop="moveUpWidget(field)"></i>
<i class="el-icon-bottom" v-if="!!parentList && (parentList.length > 1)" :title="i18nt('designer.hint.moveDownWidget')"
@click.stop="moveDownWidget(field)"></i>
<i class="el-icon-delete" :title="i18nt('designer.hint.remove')" @click.stop="removeFieldWidget"></i>
</div>
<div class="drag-handler background-opacity" v-if="designer.selectedId === field.id">
<i class="el-icon-rank" :title="i18nt('designer.hint.dragHandler')"></i>
<i>{{i18nt('designer.widgetLabel.' + field.type)}}</i>
<i v-if="field.options.hidden === true" class="iconfont icon-hide"></i>
</div>
</template>
</div>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "static-content-wrapper",
mixins: [i18n],
props: {
field: Object,
designer: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
computed: {
selected() {
return !!this.designer && this.field.id === this.designer.selectedId
},
},
methods: {
selectField(field) {
if (!!this.designer) {
this.designer.setSelected(field)
this.designer.emitEvent('field-selected', this.parentWidget) //
}
},
selectParentWidget() {
if (this.parentWidget) {
this.designer.setSelected(this.parentWidget)
} else {
this.designer.clearSelected()
}
},
moveUpWidget() {
this.designer.moveUpWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
moveDownWidget() {
this.designer.moveDownWidget(this.parentList, this.indexOfParentList)
this.designer.emitHistoryChange()
},
removeFieldWidget() {
if (!!this.parentList) {
let nextSelected = null
if (this.parentList.length === 1) {
if (!!this.parentWidget) {
nextSelected = this.parentWidget
}
} else if (this.parentList.length === (1 + this.indexOfParentList)) {
nextSelected = this.parentList[this.indexOfParentList - 1]
} else {
nextSelected = this.parentList[this.indexOfParentList + 1]
}
this.$nextTick(() => {
this.parentList.splice(this.indexOfParentList, 1)
//if (!!nextSelected) {
this.designer.setSelected(nextSelected)
//}
this.designer.emitHistoryChange()
})
}
},
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss";
.design-time-bottom-margin {
margin-bottom: 5px;
}
.field-wrapper {
position: relative;
.field-action{
position: absolute;
//bottom: -24px;
bottom: 0;
right: -2px;
height: 22px;
line-height: 22px;
background: $--color-primary;
z-index: 9;
i {
font-size: 14px;
color: #fff;
margin: 0 5px;
cursor: pointer;
}
}
.drag-handler {
position: absolute;
top: 0;
//bottom: -22px; /* */
left: -1px;
height: 20px;
line-height: 20px;
//background: $--color-primary;
z-index: 9;
i {
font-size: 12px;
font-style: normal;
color: #fff;
margin: 4px;
cursor: move;
}
&:hover {
//opacity: 1;
background: $--color-primary;
}
}
}
.static-content-item {
min-height: 20px;
display: flex; /* 垂直居中 */
align-items: center; /* 垂直居中 */
::v-deep .el-divider--horizontal {
margin: 0;
}
}
.el-form-item.selected, .static-content-item.selected {
outline: 2px solid $--color-primary;
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<static-content-wrapper :designer="designer" :field="field" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<div ref="fieldEditor">{{field.options.textContent}}</div>
</static-content-wrapper>
</template>
<script>
import StaticContentWrapper from './static-content-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "static-text-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
StaticContentWrapper,
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.registerToRefList()
this.initEventHandler()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* static-content-wrapper *//
</style>

View File

@ -0,0 +1,101 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-switch ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled"
:active-text="field.options.activeText" :inactive-text="field.options.inactiveText"
:active-color="field.options.activeColor" :inactive-color="field.options.inactiveColor"
:width="field.options.switchWidth"
@change="handleChangeEvent">
</el-switch>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "switch-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,99 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-input type="textarea" ref="fieldEditor" v-model="fieldModel"
:disabled="field.options.disabled" :readonly="field.options.readonly"
:size="field.options.size"
:placeholder="field.options.placeholder" :rows="field.options.rows"
:minlength="field.options.minLength" :maxlength="field.options.maxLength"
:show-word-limit="field.options.showWordLimit"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent" @input="handleInputCustomEvent"
@change="handleChangeEvent">
</el-input>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "textarea-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
</style>

View File

@ -0,0 +1,104 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-time-picker ref="fieldEditor" is-range v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled" :readonly="field.options.readonly"
:size="field.options.size"
:clearable="field.options.clearable" :editable="field.options.editable"
:format="field.options.format" value-format="HH:mm:ss"
:start-placeholder="field.options.startPlaceholder || i18nt('render.hint.startTimePlaceholder')"
:end-placeholder="field.options.endPlaceholder || i18nt('render.hint.endTimePlaceholder')"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-time-picker>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "time-range-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -0,0 +1,103 @@
<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<el-time-picker ref="fieldEditor" v-model="fieldModel" class="full-width-input"
:disabled="field.options.disabled" :readonly="field.options.readonly"
:size="field.options.size"
:clearable="field.options.clearable" :editable="field.options.editable"
:format="field.options.format" value-format="HH:mm:ss"
:placeholder="field.options.placeholder || i18nt('render.hint.timePlaceholder')"
@focus="handleFocusCustomEvent" @blur="handleBlurCustomEvent"
@change="handleChangeEvent">
</el-time-picker>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from 'element-ui/lib/mixins/emitter'
import i18n, {translate} from "@/utils/i18n";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
export default {
name: "time-widget",
componentName: 'FieldWidget', //FieldWidgetbroadcast
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
},
inject: ['refList', 'formConfig', 'globalOptionData', 'globalModel'],
data() {
return {
oldFieldValue: null, //fieldchange
fieldModel: null,
rules: [],
}
},
computed: {
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* mountedcreatedmountedmountedprop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeDestroy() {
this.unregisterFromRefList()
},
methods: {
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; //* form-item-wrapper *//
.full-width-input {
width: 100% !important;
}
</style>

View File

@ -11,12 +11,12 @@
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(widget, index) in designer.widgetList">
<template v-if="'container' === widget.category">
<container-widget :widget="widget" :designer="designer" :key="widget.id" :parent-list="designer.widgetList"
:index-of-parent-list="index" :parent-widget="null"></container-widget>
<component :is="getWidgetName(widget)" :widget="widget" :designer="designer" :key="widget.id" :parent-list="designer.widgetList"
:index-of-parent-list="index" :parent-widget="null"></component>
</template>
<template v-else>
<field-widget :field="widget" :designer="designer" :key="widget.id" :parent-list="designer.widgetList"
:index-of-parent-list="index" :parent-widget="null" :design-state="true"></field-widget>
<component :is="getWidgetName(widget)" :field="widget" :designer="designer" :key="widget.id" :parent-list="designer.widgetList"
:index-of-parent-list="index" :parent-widget="null" :design-state="true"></component>
</template>
</template>
</transition-group>
@ -29,9 +29,9 @@
<script>
import Draggable from 'vuedraggable'
import ContainerWidget from "@/components/form-designer/form-widget/container-widget";
import FieldWidget from "@/components/form-designer/form-widget/field-widget";
import i18n from "@/utils/i18n";
import ContainerComponents from '@/components/form-designer/form-widget/container-widget/index'
import FieldComponents from '@/components/form-designer/form-widget/field-widget/index'
import i18n from "@/utils/i18n"
export default {
name: "VFormWidget",
@ -39,8 +39,9 @@
mixins: [i18n],
components: {
Draggable,
ContainerWidget,
FieldWidget,
...ContainerComponents,
...FieldComponents,
},
props: {
designer: Object,
@ -116,6 +117,10 @@
this.designer.registerFormWidget(this)
},
methods: {
getWidgetName(widget) {
return widget.type + '-widget'
},
disableFirefoxDefaultDrop() {
let isFirefox = (navigator.userAgent.toLowerCase().indexOf("firefox") !== -1)
if (isFirefox) {

View File

@ -1,90 +0,0 @@
<template>
<el-tab-pane :name="'tab1'" :label="widget.label" @click.native.stop="selectWidget(widget)">
<draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"
handle=".drag-handler" @end="(evt) => onTabDragEnd(evt, widget.widgetList)"
@add="(evt) => onTabDragAdd(evt, widget.widgetList)"
@update="onTabDragUpdate" :move="checkContainerMove">
<transition-group name="fade" tag="div" class="form-widget-list">
<template v-for="(subWidget, swIdx) in widget.widgetList">
<template v-if="'container' === subWidget.category">
<container-widget :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget"></container-widget>
</template>
<template v-else>
<field-widget :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList"
:index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></field-widget>
</template>
</template>
</transition-group>
</draggable>
</el-tab-pane>
</template>
<script>
/* 此组件未用上!! */
import Draggable from 'vuedraggable'
import ContainerWidget from "@/components/form-designer/form-widget/container-widget";
import FieldWidget from "@/components/form-designer/form-widget/field-widget";
import i18n from "@/utils/i18n";
export default {
name: "VTabPane",
mixins: [i18n],
components: {
Draggable,
ContainerWidget,
FieldWidget,
},
props: {
widget: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
},
computed: {
selected() {
return this.widget.id === this.designer.selectedId
},
},
methods: {
selectWidget(widget) {
this.designer.setSelected(widget)
},
onTabDragEnd(obj, subList) {
//
},
onTabDragAdd(evt, subList) { //
const newIndex = evt.newIndex
console.log(newIndex)
if (!!subList[newIndex]) {
this.designer.setSelected( subList[newIndex] )
}
this.designer.emitHistoryChange()
},
onTabDragUpdate() {
this.designer.emitHistoryChange()
},
checkContainerMove(evt) {
return this.designer.checkWidgetMove(evt)
},
}
}
</script>
<style lang="scss" scoped>
.el-tab-pane {
::v-deep .form-widget-list {
min-height: 28px;
}
}
</style>

View File

@ -2,7 +2,7 @@
/**
* author: vformAdmin
* email: vdpadmin@163.com
* website: http://www.vform666.com/
* website: http://www.vform666.com
* date: 2021.08.18
* remark: 如果要分发VForm源码需在本文件顶部保留此文件头信息
*/
@ -62,7 +62,7 @@
import VFormWidget from './form-widget/index'
import {createDesigner} from "@/components/form-designer/designer";
import {addWindowResizeHandler, getQueryParam} from "@/utils/util";
import {VARIANT_FORM_VERSION} from "@/utils/config";
import {MOCK_CASE_URL, VARIANT_FORM_VERSION} from "@/utils/config";
import i18n, { changeLocale } from "@/utils/i18n";
export default {
@ -81,6 +81,8 @@
curLangName: '',
vsCodeFlag: false,
caseName: '',
docUrl: 'http://www.vform666.com/document.html',
gitUrl: 'https://github.com/vform666/variant-form',
chatUrl: 'http://www.vform666.com/chat-group.html',
@ -98,6 +100,7 @@
},
created() {
this.vsCodeFlag = getQueryParam('vscode') == 1
this.caseName = getQueryParam('case')
},
mounted() {
this.initLocale()
@ -108,6 +111,8 @@
this.scrollerHeight = window.innerHeight - 56 - 36 + 'px'
})
})
this.loadCase()
},
methods: {
openHome() {
@ -138,6 +143,24 @@
}
},
loadCase() {
if (!this.caseName) {
return
}
axios.get(MOCK_CASE_URL + this.caseName).then(res => {
if (!!res.data.code) {
this.$message.error(this.i18nt('designer.hint.sampleLoadedFail'))
return
}
this.setFormJson(res.data)
this.$message.success(this.i18nt('designer.hint.sampleLoadedSuccess'))
}).catch(error => {
this.$message.error(this.i18nt('designer.hint.sampleLoadedFail') + ':' +error)
})
},
initLocale() {
let curLocale = localStorage.getItem('v_form_locale')
if (!!this.vsCodeFlag) {

View File

@ -0,0 +1,300 @@
<template>
<div>
<el-form :model="formConfig" size="mini" label-position="left" label-width="120px"
class="setting-form" @submit.native.prevent>
<el-collapse v-model="formActiveCollapseNames" class="setting-collapse">
<el-collapse-item name="1" :title="i18nt('designer.setting.basicSetting')">
<el-form-item :label="i18nt('designer.setting.formSize')">
<el-select v-model="formConfig.size">
<el-option v-for="item in formSizes" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.labelPosition')">
<el-radio-group v-model="formConfig.labelPosition" class="radio-group-custom">
<el-radio-button label="left">{{i18nt('designer.setting.leftPosition')}}</el-radio-button>
<el-radio-button label="top">{{i18nt('designer.setting.topPosition')}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.labelAlign')">
<el-radio-group v-model="formConfig.labelAlign" class="radio-group-custom">
<el-radio-button label="label-left-align">{{i18nt('designer.setting.leftAlign')}}</el-radio-button>
<el-radio-button label="label-center-align">{{i18nt('designer.setting.centerAlign')}}</el-radio-button>
<el-radio-button label="label-right-align">{{i18nt('designer.setting.rightAlign')}}</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.labelWidth')">
<el-input-number v-model="formConfig.labelWidth" :min="0" style="width: 100%"></el-input-number>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.formCss')">
<el-button type="info" icon="el-icon-edit" plain round @click="editFormCss">{{i18nt('designer.setting.addCss')}}</el-button>
</el-form-item>
<!-- -->
<el-form-item :label="i18nt('designer.setting.customClass')">
<el-select v-model="formConfig.customClass" multiple filterable allow-create
default-first-option>
<el-option v-for="(item, idx) in cssClassList" :key="idx" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
<!-- -->
<el-form-item :label="i18nt('designer.setting.globalFunctions')">
<el-button type="info" icon="el-icon-edit" plain round @click="editGlobalFunctions">{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
<el-form-item label-width="0">
<el-divider class="custom-divider">{{i18nt('designer.setting.formSFCSetting')}}</el-divider>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.formModelName')">
<el-input type="text" v-model="formConfig.modelName"></el-input>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.formRefName')">
<el-input type="text" v-model="formConfig.refName"></el-input>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.formRulesName')">
<el-input type="text" v-model="formConfig.rulesName"></el-input>
</el-form-item>
</el-collapse-item>
<el-collapse-item name="2" :title="i18nt('designer.setting.eventSetting')">
<el-form-item label="onFormCreated" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editFormEventHandler('onFormCreated')">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
<el-form-item label="onFormMounted" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editFormEventHandler('onFormMounted')">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
<!-- -->
<el-form-item label="onFormDataChange" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editFormEventHandler('onFormDataChange')">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
<!-- -->
<!--
<el-form-item label="onFormValidate">
<el-button type="info" icon="el-icon-edit" plain round @click="editFormEventHandler('onFormValidate')">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
-->
</el-collapse-item>
</el-collapse>
</el-form>
<el-dialog :title="i18nt('designer.setting.editFormEventHandler')" :visible.sync="showFormEventDialogFlag"
v-if="showFormEventDialogFlag" :show-close="true" class="small-padding-dialog" v-dialog-drag
:close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
<el-alert type="info" :closable="false" :title="'form.' + eventParamsMap[curEventName]"></el-alert>
<code-editor :mode="'javascript'" :readonly="false" v-model="formEventHandlerCode"></code-editor>
<el-alert type="info" :closable="false" title="}"></el-alert>
<div slot="footer" class="dialog-footer">
<el-button @click="showFormEventDialogFlag = false">
{{i18nt('designer.hint.cancel')}}</el-button>
<el-button type="primary" @click="saveFormEventHandler">
{{i18nt('designer.hint.confirm')}}</el-button>
</div>
</el-dialog>
<el-dialog :title="i18nt('designer.setting.formCss')" :visible.sync="showEditFormCssDialogFlag"
v-if="showEditFormCssDialogFlag" :show-close="true" class="small-padding-dialog" v-dialog-drag
:close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
<code-editor :mode="'css'" :readonly="false" v-model="formCssCode"></code-editor>
<div slot="footer" class="dialog-footer">
<el-button @click="showEditFormCssDialogFlag = false">
{{i18nt('designer.hint.cancel')}}</el-button>
<el-button type="primary" @click="saveFormCss">
{{i18nt('designer.hint.confirm')}}</el-button>
</div>
</el-dialog>
<el-dialog :title="i18nt('designer.setting.globalFunctions')" :visible.sync="showEditFunctionsDialogFlag"
v-if="showEditFunctionsDialogFlag" :show-close="true" class="small-padding-dialog" v-dialog-drag
:close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true">
<code-editor :mode="'javascript'" :readonly="false" v-model="functionsCode"></code-editor>
<div slot="footer" class="dialog-footer">
<el-button @click="showEditFunctionsDialogFlag = false">
{{i18nt('designer.hint.cancel')}}</el-button>
<el-button type="primary" @click="saveGlobalFunctions">
{{i18nt('designer.hint.confirm')}}</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import i18n from "@/utils/i18n"
import CodeEditor from '@/components/code-editor/index'
import {deepClone, insertCustomCssToHead, insertGlobalFunctionsToHtml} from "@/utils/util"
export default {
name: "form-setting",
mixins: [i18n],
components: {
CodeEditor,
},
props: {
designer: Object,
formConfig: Object,
},
data() {
return {
formActiveCollapseNames: ['1', '2'],
formSizes: [
{label: 'default', value: ''},
{label: 'large', value: 'large'},
{label: 'medium', value: 'medium'},
{label: 'small', value: 'small'},
{label: 'mini', value: 'mini'},
],
showEditFormCssDialogFlag: false,
formCssCode: '',
cssClassList: [],
showEditFunctionsDialogFlag: false,
functionsCode: '',
showFormEventDialogFlag: false,
formEventHandlerCode: '',
curEventName: '',
eventParamsMap: {
'onFormCreated': 'onFormCreated() {',
'onFormMounted': 'onFormMounted() {',
'onFormDataChange': 'onFormDataChange(fieldName, newValue, oldValue, formModel, subFormName, subFormRowIndex) {',
//'onFormValidate': 'onFormValidate() {',
},
}
},
mounted() {
/* SettingPanelFormWidget, FormWidgetformConfig
此处SettingPanel可能无法获取到formConfig.cssCode, 故加个延时函数 */
setTimeout(() => {
this.formCssCode = this.formConfig.cssCode
insertCustomCssToHead(this.formCssCode)
this.extractCssClass()
this.designer.emitEvent('form-css-updated', deepClone(this.cssClassList))
}, 1200)
},
methods: {
editFormCss() {
this.formCssCode = this.designer.formConfig.cssCode
this.showEditFormCssDialogFlag = true
},
extractCssClass() {
let regExp = /\..*{/g
let result = this.formCssCode.match(regExp)
//this.cssClassList.length = 0
this.cssClassList.splice(0, this.cssClassList.length) //splicelength=0
if (!!result && result.length > 0) {
result.forEach((rItem) => {
let classArray = rItem.split(',') //class
if (classArray.length > 0) {
classArray.forEach((cItem) => {
let caItem = cItem.trim()
if (caItem.indexOf('.', 1) !== -1) { //.
let newClass = caItem.substring(caItem.indexOf('.') + 1, caItem.indexOf('.', 1)) //.class
if (!!newClass) {
this.cssClassList.push(newClass.trim())
}
} else if (caItem.indexOf(' ') !== -1) { //
let newClass = caItem.substring(caItem.indexOf('.') + 1, caItem.indexOf(' ')) //.class
if (!!newClass) {
this.cssClassList.push(newClass.trim())
}
} else {
if (caItem.indexOf('{') !== -1) { //{
let newClass = caItem.substring(caItem.indexOf('.') + 1, caItem.indexOf('{'))
this.cssClassList.push( newClass.trim() )
} else {
let newClass = caItem.substring(caItem.indexOf('.') + 1)
this.cssClassList.push( newClass.trim() )
}
}
})
}
})
}
},
saveFormCss() {
this.extractCssClass()
this.designer.formConfig.cssCode = this.formCssCode
insertCustomCssToHead(this.formCssCode)
this.showEditFormCssDialogFlag = false
this.designer.emitEvent('form-css-updated', deepClone(this.cssClassList))
},
editGlobalFunctions() {
this.functionsCode = this.designer.formConfig.functions
this.showEditFunctionsDialogFlag = true
},
saveGlobalFunctions() {
this.designer.formConfig.functions = this.functionsCode
insertGlobalFunctionsToHtml(this.functionsCode)
this.showEditFunctionsDialogFlag = false
},
editFormEventHandler(eventName) {
this.curEventName = eventName
this.formEventHandlerCode = this.formConfig[eventName]
this.showFormEventDialogFlag = true
},
saveFormEventHandler() {
this.formConfig[this.curEventName] = this.formEventHandlerCode
this.showFormEventDialogFlag = false
},
}
}
</script>
<style lang="scss" scoped>
.setting-form {
::v-deep .el-form-item__label {
font-size: 13px;
//text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
::v-deep .el-form-item--mini.el-form-item {
margin-bottom: 6px;
}
.radio-group-custom {
::v-deep .el-radio-button__inner {
padding-left: 12px;
padding-right: 12px;
}
}
.custom-divider.el-divider--horizontal {
margin: 10px 0;
}
}
.setting-collapse {
::v-deep .el-collapse-item__content {
padding-bottom: 6px;
}
::v-deep .el-collapse-item__header {
font-style: italic;
font-weight: bold;
}
}
.small-padding-dialog {
::v-deep .el-dialog__body {
padding: 6px 15px 12px 15px;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,24 @@
<template>
<el-form-item :label="i18nt('designer.setting.allowCreate')">
<el-checkbox v-model="optionModel.allowCreate"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "allowCreate-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,28 @@
<template>
<div>
<el-form-item label-width="0">
<el-divider class="custom-divider">{{i18nt('designer.setting.inputButton')}}</el-divider>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.appendButton')">
<el-checkbox v-model="optionModel.appendButton"></el-checkbox>
</el-form-item>
</div>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "appendButton-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.appendButtonDisabled')">
<el-checkbox v-model="optionModel.appendButtonDisabled"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "appendButtonDisabled-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.automaticDropdown')">
<el-checkbox v-model="optionModel.automaticDropdown"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "automaticDropdown-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.border')">
<el-checkbox v-model="optionModel.border"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "border-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.appendButtonIcon')">
<el-input type="text" v-model="optionModel.buttonIcon"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "buttonIcon-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.buttonStyle')">
<el-checkbox v-model="optionModel.buttonStyle"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "buttonStyle-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.clearable')">
<el-checkbox v-model="optionModel.clearable"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "clearable-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,40 @@
<template>
<div>
<el-form-item :label="i18nt('designer.setting.widgetColumnWidth')" v-show="!!subFormChildWidgetFlag">
<el-input type="text" v-model="optionModel.columnWidth"></el-input>
</el-form-item>
</div>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "columnWidth-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
//subFormChildWidgetFlag: false,
subFormChildWidgetFlag: true,
}
},
created() {
},
mounted() {
this.designer.handleEvent('field-selected', (parentWidget) => {
this.subFormChildWidgetFlag = !!parentWidget && (parentWidget.type === 'sub-form');
//console.log('subFormChildWidgetFlag', this.subFormChildWidgetFlag)
})
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
<template>
<el-form-item :label="i18nt('designer.setting.colSpanTitle')">
<el-input-number v-model.number="optionModel.span" :min="1" :max="24"
style="width: 100%"></el-input-number>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "grid-col-span-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,81 @@
<template>
<div>
<el-form-item label-width="0">
<el-divider class="custom-divider">{{i18nt('designer.setting.columnSetting')}}</el-divider>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.gutter')">
<el-input-number v-model="optionModel.gutter" style="width: 100%"></el-input-number>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.colsOfGrid')"></el-form-item>
<el-form-item label-width="0">
<li v-for="(colItem, colIdx) in selectedWidget.cols" :key="colIdx" class="col-item">
<span class="col-span-title">{{i18nt('designer.setting.colSpanTitle')}}{{colIdx + 1}}</span>
<el-input-number v-model.number="colItem.options.span" :min="1" :max="24"
@change="(newValue, oldValue) => spanChanged(selectedWidget, colItem, colIdx, newValue, oldValue)"
class="cell-span-input"></el-input-number>
<el-button circle plain size="mini" type="danger" @click="deleteCol(selectedWidget, colIdx)"
icon="el-icon-minus" class="col-delete-button"></el-button>
</li>
<div>
<el-button type="text" @click="addNewCol(selectedWidget)">{{i18nt('designer.setting.addColumn')}}</el-button>
</div>
</el-form-item>
</div>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "gutter-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
methods: {
spanChanged(curGrid) {
let spanSum = 0
curGrid.cols.forEach((colItem) => {
spanSum += colItem.options.span
})
if (spanSum > 24) {
//this.$message.info('24')
console.log('列栅格之和超出24')
//TODO:
}
this.designer.saveCurrentHistoryStep()
},
deleteCol(curGrid, colIdx) {
this.designer.deleteColOfGrid(curGrid, colIdx)
this.designer.emitHistoryChange()
},
addNewCol(curGrid) {
this.designer.addNewColOfGrid(curGrid)
this.designer.emitHistoryChange()
},
}
}
</script>
<style lang="scss" scoped>
li.col-item {
list-style: none;
span.col-span-title {
display: inline-block;
font-size: 13px;
width: 120px;
}
.col-delete-button {
margin-left: 6px;
}
}
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.showBlankRow')">
<el-checkbox v-model="optionModel.showBlankRow"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "showBlankRow-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.showRowNumber')">
<el-checkbox v-model="optionModel.showRowNumber"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "showRowNumber-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,36 @@
<template>
<el-form-item :label="i18nt('designer.setting.labelAlign')">
<el-radio-group v-model="optionModel.labelAlign" class="radio-group-custom">
<el-radio-button label="label-left-align">
{{i18nt('designer.setting.leftAlign')}}</el-radio-button>
<el-radio-button label="label-center-align">
{{i18nt('designer.setting.centerAlign')}}</el-radio-button>
<el-radio-button label="label-right-align">
{{i18nt('designer.setting.rightAlign')}}</el-radio-button>
</el-radio-group>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "sub-form-labelAlign-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style lang="scss" scoped>
.radio-group-custom {
::v-deep .el-radio-button__inner {
padding-left: 12px;
padding-right: 12px;
}
}
</style>

View File

@ -0,0 +1,119 @@
<!--
因tabs属性并不包含于options属性之中故只能跟其他options属性之内的属性值合并设置此处选择与customClass合并
-->
<template>
<div>
<el-form-item :label="i18nt('designer.setting.customClass')">
<el-select v-model="optionModel.customClass" multiple filterable allow-create
default-first-option>
<el-option v-for="(item, idx) in cssClassList" :key="idx" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="i18nt('designer.setting.tabPaneSetting')"></el-form-item>
<el-form-item label-width="0" class="panes-setting">
<draggable tag="ul" :list="selectedWidget.tabs"
v-bind="{group:'panesGroup', ghostClass: 'ghost', handle: '.drag-option'}">
<li v-for="(tpItem, tpIdx) in selectedWidget.tabs" :key="tpIdx" class="col-item">
<!-- span style="margin-right: 12px">{{tpIdx + 1}}</span -->
<el-checkbox v-model="tpItem.options.active" disabled @change="(evt) => onTabPaneActiveChange(evt, tpItem)"
style="margin-right: 8px">{{i18nt('designer.setting.paneActive')}}</el-checkbox>
<el-input type="text" v-model="tpItem.options.label" style="width: 155px"></el-input>
<i class="iconfont icon-drag drag-option"></i>
<el-button circle plain size="mini" type="danger" @click="deleteTabPane(selectedWidget, tpIdx)"
icon="el-icon-minus" class="col-delete-button"></el-button>
</li>
<div>
<el-button type="text" @click="addTabPane(selectedWidget)">{{i18nt('designer.setting.addTabPane')}}</el-button>
</div>
</draggable>
</el-form-item>
</div>
</template>
<script>
import i18n from "@/utils/i18n"
import Draggable from 'vuedraggable'
import {deepClone} from "@/utils/util";
export default {
name: "tab-customClass-editor",
componentName: 'PropertyEditor',
mixins: [i18n],
components: {
Draggable,
},
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
cssClassList: [],
}
},
created() {
this.cssClassList = deepClone(this.designer.getCssClassList())
//css
this.designer.handleEvent('form-css-updated', (cssClassList) => {
this.cssClassList = cssClassList
})
},
methods: {
onTabPaneActiveChange(evt, tpItem) {
//TODO: !!!
},
addTabPane(curTabs) {
this.designer.addTabPaneOfTabs(curTabs)
this.designer.emitHistoryChange()
},
deleteTabPane(curTabs, tpIdx) {
if (curTabs.tabs.length === 1) {
this.$message.info(this.i18nt('designer.hint.lastPaneCannotBeDeleted'))
return
}
this.designer.deleteTabPaneOfTabs(curTabs, tpIdx)
this.designer.emitHistoryChange()
},
}
}
</script>
<style lang="scss" scoped>
li.col-item {
list-style: none;
span.col-span-title {
display: inline-block;
font-size: 13px;
width: 120px;
}
.col-delete-button {
margin-left: 6px;
}
}
.panes-setting {
ul {
padding-inline-start: 0;
padding-left: 0; /* 重置IE11默认样式 */
margin: 0;
}
.drag-option {
cursor: move;
}
li.ghost {
background: #fff;
border: 2px dotted $--color-primary;
}
}
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.cellHeight')">
<el-input type="text" v-model="optionModel.cellHeight"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "cellHeight-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.cellWidth')">
<el-input type="text" v-model="optionModel.cellWidth"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "cellWidth-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,40 @@
<template>
<el-form-item :label="i18nt('designer.setting.customClass')">
<el-select v-model="optionModel.customClass" multiple filterable allow-create
default-first-option>
<el-option v-for="(item, idx) in cssClassList" :key="idx" :label="item" :value="item"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
import {deepClone} from "@/utils/util";
export default {
name: "customClass-editor",
componentName: 'PropertyEditor',
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
cssClassList: [],
}
},
created() {
this.cssClassList = deepClone(this.designer.getCssClassList())
//css
this.designer.handleEvent('form-css-updated', (cssClassList) => {
this.cssClassList = cssClassList
})
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,25 @@
<template>
<el-form-item :label="i18nt('designer.setting.defaultValue')">
<el-input v-if="!hasConfig('optionItems')" type="text" v-model="optionModel.defaultValue"
@change="emitDefaultValueChange"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import propertyMixin from "@/components/form-designer/setting-panel/property-editor/propertyMixin"
export default {
name: "defaultValue-editor",
mixins: [i18n, propertyMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.disabled')">
<el-checkbox v-model="optionModel.disabled"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "disabled-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,26 @@
<template>
<el-form-item :label="i18nt('designer.setting.displayStyle')">
<el-radio-group v-model="optionModel.displayStyle">
<el-radio label="inline">{{i18nt('designer.setting.inlineLayout')}}</el-radio>
<el-radio label="block">{{i18nt('designer.setting.blockLayout')}}</el-radio>
</el-radio-group>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "displayStyle-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
<template>
<el-form-item :label="i18nt('designer.setting.editable')">
<el-checkbox v-model="optionModel.editable"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "editable-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.endPlaceholder')">
<el-input type="text" v-model="optionModel.endPlaceholder"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "endPlaceholder-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,19 @@
import emitter from 'element-ui/lib/mixins/emitter'
export default {
mixins: [emitter],
created() {
// this.$on('saveEventHandler', function (params) {
// //this.optionModel[eventName] = handlerCodes
// this.optionModel[params[0]] = params[1]
// })
},
methods: {
editEventHandler(eventName, eventParams) {
this.dispatch('SettingPanel', 'editEventHandler', [eventName, [...eventParams]])
},
}
}

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onBeforeUpload" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onBeforeUpload', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onBeforeUpload-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['file'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onBlur" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onBlur', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onBlur-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['event'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onChange" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onChange', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onChange-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['value', 'oldValue', 'subFormData', 'rowId'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onClick" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onClick', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onClick-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: [],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,31 @@
<template>
<el-form-item label="onCreated" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onCreated', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onCreated-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: [],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onFocus" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onFocus', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onFocus-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['event'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onInput" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onInput', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onInput-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['value'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onMounted" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onMounted', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onMounted-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: [],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onRemoteQuery" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onRemoteQuery', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onRemoteQuery-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['keyword'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onSubFormRowAdd" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onSubFormRowAdd', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onSubFormRowAdd-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['subFormData', 'newRowId'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onSubFormRowChange" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onSubFormRowChange', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onSubFormRowChange-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['subFormData'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onSubFormRowDelete" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onSubFormRowDelete', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onSubFormRowDelete-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['subFormData', 'deletedDataRow'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onSubFormRowInsert" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onSubFormRowInsert', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onSubFormRowInsert-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['subFormData', 'newRowId'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onUploadError" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onUploadError', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onUploadError-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['error', 'file', 'fileList'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onUploadSuccess" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onUploadSuccess', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onUploadSuccess-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['result', 'file', 'fileList'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item label="onValidate" label-width="150px">
<el-button type="info" icon="el-icon-edit" plain round @click="editEventHandler('onValidate', eventParams)">
{{i18nt('designer.setting.addEventHandler')}}</el-button>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import eventMixin from "@/components/form-designer/setting-panel/property-editor/event-handler/eventMixin"
export default {
name: "onValidate-editor",
mixins: [i18n, eventMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
eventParams: ['rule', 'value', 'callback'],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,32 @@
<template>
<el-form-item :label="i18nt('designer.setting.displayType')">
<el-select v-model="optionModel.type">
<el-option label="default" value=""></el-option>
<el-option label="primary" value="primary"></el-option>
<el-option label="success" value="success"></el-option>
<el-option label="warning" value="warning"></el-option>
<el-option label="danger" value="danger"></el-option>
<el-option label="info" value="info"></el-option>
<el-option label="text" value="text"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
import propertyMixin from "@/components/form-designer/setting-panel/property-editor/propertyMixin"
export default {
name: "button-type-editor",
mixins: [i18n, propertyMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.circle')">
<el-checkbox v-model="optionModel.circle"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "circle-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.buttonIcon')">
<el-input type="text" v-model="optionModel.icon"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "icon-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
<template>
<el-form-item :label="i18nt('designer.setting.plain')">
<el-checkbox v-model="optionModel.plain"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "plain-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.round')">
<el-checkbox v-model="optionModel.round"></el-checkbox>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "round-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-color-picker v-model="optionModel.defaultValue" @change="emitDefaultValueChange">
</el-color-picker>
</template>
<script>
import i18n from "@/utils/i18n"
import propertyMixin from "@/components/form-designer/setting-panel/property-editor/propertyMixin"
export default {
name: "color-defaultValue-editor",
mixins: [i18n, propertyMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
<template>
<el-date-picker :type="optionModel.type" v-model="optionModel.defaultValue" @change="emitDefaultValueChange"
:format="optionModel.format" :value-format="optionModel.valueFormat" style="width: 100%">
</el-date-picker>
</template>
<script>
import i18n from "@/utils/i18n"
import propertyMixin from "@/components/form-designer/setting-panel/property-editor/propertyMixin"
export default {
name: "date-range-defaultValue-editor",
mixins: [i18n, propertyMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item :label="i18nt('designer.setting.format')">
<el-select v-model="optionModel.format" filterable allow-create>
<el-option label="yyyy-MM-dd" value="yyyy-MM-dd"></el-option>
<el-option label="yyyy/MM/dd" value="yyyy/MM/dd"></el-option>
<el-option label="yyyy年MM月dd日" value="yyyy年MM月dd日"></el-option>
<el-option label="yyyy-MM-dd HH:mm:ss" value="yyyy-MM-dd HH:mm:ss"></el-option>
<el-option label="yyyy-MM-dd hh:mm:ss" value="yyyy-MM-dd hh:mm:ss"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "date-range-format-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,27 @@
<template>
<el-form-item :label="i18nt('designer.setting.displayType')">
<el-select v-model="optionModel.type">
<el-option label="daterange" value="daterange"></el-option>
<el-option label="datetimerange" value="datetimerange"></el-option>
<el-option label="monthrange" value="monthrange"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "date-range-type-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,26 @@
<template>
<el-form-item :label="i18nt('designer.setting.valueFormat')">
<el-select v-model="optionModel.valueFormat" filterable allow-create>
<el-option label="yyyy-MM-dd" value="yyyy-MM-dd"></el-option>
<el-option label="yyyy-MM-dd HH:mm:ss" value="yyyy-MM-dd HH:mm:ss"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "date-range-valueFormat-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,24 @@
<template>
<el-date-picker :type="optionModel.type" v-model="optionModel.defaultValue" @change="emitDefaultValueChange"
:format="optionModel.format" :value-format="optionModel.valueFormat" style="width: 100%">
</el-date-picker>
</template>
<script>
import i18n from "@/utils/i18n"
import propertyMixin from "@/components/form-designer/setting-panel/property-editor/propertyMixin"
export default {
name: "date-defaultValue-editor",
mixins: [i18n, propertyMixin],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item :label="i18nt('designer.setting.format')">
<el-select v-model="optionModel.format" filterable allow-create>
<el-option label="yyyy-MM-dd" value="yyyy-MM-dd"></el-option>
<el-option label="yyyy/MM/dd" value="yyyy/MM/dd"></el-option>
<el-option label="yyyy年MM月dd日" value="yyyy年MM月dd日"></el-option>
<el-option label="yyyy-MM-dd HH:mm:ss" value="yyyy-MM-dd HH:mm:ss"></el-option>
<el-option label="yyyy-MM-dd hh:mm:ss" value="yyyy-MM-dd hh:mm:ss"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "date-format-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-form-item :label="i18nt('designer.setting.displayType')">
<el-select v-model="optionModel.type">
<el-option label="datetime" value="datetime"></el-option>
<el-option label="date" value="date"></el-option>
<el-option label="dates" value="dates"></el-option>
<el-option label="year" value="year"></el-option>
<el-option label="month" value="month"></el-option>
<el-option label="week" value="week"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n";
export default {
name: "date-type-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,26 @@
<template>
<el-form-item :label="i18nt('designer.setting.valueFormat')">
<el-select v-model="optionModel.valueFormat" filterable allow-create>
<el-option label="yyyy-MM-dd" value="yyyy-MM-dd"></el-option>
<el-option label="yyyy-MM-dd HH:mm:ss" value="yyyy-MM-dd HH:mm:ss"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "date-valueFormat-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,27 @@
<template>
<el-form-item :label="i18nt('designer.setting.contentPosition')">
<el-select v-model="optionModel.contentPosition">
<el-option label="center" value="center"></el-option>
<el-option label="left" value="left"></el-option>
<el-option label="right" value="right"></el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "contentPosition-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,44 @@
<template>
<el-form-item>
<span slot="label">{{i18nt('designer.setting.fileTypes')}}
<el-tooltip effect="light" :content="i18nt('designer.setting.fileTypesHelp')">
<i class="el-icon-info"></i></el-tooltip>
</span>
<el-select multiple allow-create filterable default-first-option
v-model="optionModel.fileTypes" style="width: 100%">
<el-option v-for="(ft, ftIdx) in uploadFileTypes"
:key="ftIdx"
:label="ft.label"
:value="ft.value">
</el-option>
</el-select>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "fileTypes-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
data() {
return {
uploadFileTypes: [
{value: 'doc', label: 'doc'},
{value: 'xls', label: 'xls'},
{value: 'docx', label: 'docx'},
{value: 'xlsx', label: 'xlsx'},
],
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<el-form-item :label="i18nt('designer.setting.htmlContent')">
<el-input v-model="optionModel.htmlContent"></el-input>
</el-form-item>
</template>
<script>
import i18n from "@/utils/i18n"
export default {
name: "htmlContent-editor",
mixins: [i18n],
props: {
designer: Object,
selectedWidget: Object,
optionModel: Object,
},
}
</script>
<style scoped>
</style>

Some files were not shown because too many files have changed in this diff Show More