variant-form/src/components-iview/form-designer/toolbar-panel/index.vue

567 lines
18 KiB
Vue

<template>
<div class="toolbar-container">
<div class="left-toolbar">
<Button type="text" :disabled="undoDisabled" :title="i18nt('designer.toolbar.undoHint')"
@click="undoHistory" style="background-color: transparent;">
<svg-icon icon-class="undo" />
</Button>
<Button type="text" :disabled="redoDisabled" :title="i18nt('designer.toolbar.redoHint')"
@click="redoHistory" style="background-color: transparent;">
<svg-icon icon-class="redo" />
</Button>
<ButtonGroup style="margin-left: 20px">
<Button :type="layoutType === 'PC' ? 'primary': 'default'" @click="changeLayoutType('PC')">
{{i18nt('designer.toolbar.pcLayout')}}
</Button>
<Button :type="layoutType === 'H5' ? 'primary': 'default'" @click="changeLayoutType('H5')">
{{i18nt('designer.toolbar.mobileLayout')}}
</Button>
</ButtonGroup>
</div>
<div class="right-toolbar">
<Button type="text" @click="clearFormWidget"><i class="ivu-icon ivu-icon-ios-trash" />{{i18nt('designer.toolbar.clear')}}</Button>
<Button type="text" @click="previewForm"><i class="ivu-icon ivu-icon-ios-eye-outline" />{{i18nt('designer.toolbar.preview')}}</Button>
<Button type="text" @click="importJson">{{i18nt('designer.toolbar.importJson')}}</Button>
<Button type="text" @click="exportJson">{{i18nt('designer.toolbar.exportJson')}}</Button>
<Button type="text" @click="exportCode">{{i18nt('designer.toolbar.exportCode')}}</Button>
<Button type="text" v-if="false">{{i18nt('designer.toolbar.generateCode')}}</Button>
<Button type="text" @click="generateSFC"><svg-icon icon-class="vue-sfc" />{{i18nt('designer.toolbar.generateSFC')}}</Button>
</div>
<Modal :title="i18nt('designer.toolbar.preview')"
v-model="showPreviewDialogFlag" :closable="true" class="small-padding-dialog" draggable
:mask-closable="false" width="1200"
:fullscreen ="layoutType === 'H5'">
<div v-if="showPreviewDialogFlag">
<div class="form-render-wrapper" :class="[layoutType === 'H5' ? 'h5-layout' : '']">
<VFormRender ref="preForm" :form-json="formJson" :form-data="testFormData"
@appendButtonClick="testOnAppendButtonClick" @buttonClick="testOnButtonClick"
@formChange="handleFormChange" @change="handlerNativeChange"
></VFormRender>
</div>
</div>
<code-editor v-model="testFunc" style="display: none"></code-editor>
<div slot="footer" class="dialog-footer">
<Button type="primary" size="default" @click="getFormData">{{i18nt('designer.hint.getFormData')}}</Button>
<Button type="primary" size="default" @click="resetForm">{{i18nt('designer.hint.resetForm')}}</Button>
<Button type="primary" size="default" @click="setFormDisabled">{{i18nt('designer.hint.disableForm')}}</Button>
<Button type="primary" size="default" @click="setFormEnabled">{{i18nt('designer.hint.enableForm')}}</Button>
<Button type="default" size="default" @click="showPreviewDialogFlag = false">{{i18nt('designer.hint.closePreview')}}</Button>
</div>
</Modal>
<Modal :title="i18nt('designer.toolbar.importJson')"
v-model="showImportJsonDialogFlag" :closable="true" class="small-padding-dialog" width="800" draggable
:mask-closable="false">
<Alert show-icon>{{i18nt('designer.hint.importJsonHint')}}</Alert>
<code-editor v-if="showImportJsonDialogFlag" :mode="'json'" :readonly="false" v-model="importTemplate"></code-editor>
<div slot="footer" class="dialog-footer">
<Button size="default" @click="showImportJsonDialogFlag = false">
{{i18nt('designer.hint.cancel')}}
</Button>
<Button size="default" type="primary" @click="doJsonImport">
{{i18nt('designer.hint.import')}}
</Button>
</div>
</Modal>
<Modal :title="i18nt('designer.toolbar.exportJson')"
v-model="showExportJsonDialogFlag" :closable="true" class="small-padding-dialog" draggable
width="800" :mask-closable="false">
<code-editor v-if="showExportJsonDialogFlag" :mode="'json'" :readonly="true" v-model="jsonContent"></code-editor>
<div slot="footer" class="dialog-footer">
<Button size="default" type="primary" class="copy-json-btn" :data-clipboard-text="jsonRawContent">
{{i18nt('designer.hint.copyJson')}}
</Button>
<Button size="default" type="default" @click="showExportJsonDialogFlag = false">
{{i18nt('designer.hint.closePreview')}}
</Button>
</div>
</Modal>
<Modal :title="i18nt('designer.toolbar.exportCode')"
v-model="showExportCodeDialogFlag" :closable="true" class="small-padding-dialog" draggable
:mask-closable="false"
width="960">
<Tabs type="line" class="no-box-shadow" v-model="activeCodeTab">
<TabPane label="Vue" name="vue">
<code-editor v-if="showExportCodeDialogFlag" :mode="'html'" :readonly="true" v-model="vueCode" :user-worker="false"></code-editor>
</TabPane>
<TabPane label="HTML" name="html">
<code-editor v-if="showExportCodeDialogFlag" :mode="'html'" :readonly="true" v-model="htmlCode" :user-worker="false"></code-editor>
</TabPane>
</Tabs>
<div slot="footer" class="dialog-footer">
<Button size="default" type="primary" class="copy-vue-btn" :data-clipboard-text="vueCode">
{{i18nt('designer.hint.copyVueCode')}}
</Button>
<Button size="default" type="primary" class="copy-html-btn" :data-clipboard-text="htmlCode">
{{i18nt('designer.hint.copyHtmlCode')}}
</Button>
<Button size="default" type="primary" @click="saveVueCode">{{i18nt('designer.hint.saveVueCode')}}</Button>
<Button size="default" type="primary" @click="saveHtmlCode">{{i18nt('designer.hint.saveHtmlCode')}}</Button>
<Button size="default" type="default" @click="showExportCodeDialogFlag = false">
{{i18nt('designer.hint.closePreview')}}
</Button>
</div>
</Modal>
<Modal :title="i18nt('designer.hint.exportFormData')"
v-model="showFormDataDialogFlag" :closable="true" class="dialog-title-light-bg" width="800" draggable
:mask-closable="false">
<div style="border: 1px solid #DCDFE6">
<code-editor v-if="showFormDataDialogFlag" :mode="'json'" :readonly="true" v-model="formDataJson"></code-editor>
</div>
<div slot="footer" class="dialog-footer">
<Button size="default" type="primary" class="copy-form-data-json-btn" :data-clipboard-text="formDataRawJson">
{{i18nt('designer.hint.copyFormData')}}
</Button>
<Button size="default" type="default" @click="showFormDataDialogFlag = false">
{{i18nt('designer.hint.closePreview')}}
</Button>
</div>
</Modal>
<Modal :title="i18nt('designer.toolbar.generateSFC')" width="960"
v-model="showExportSFCDialogFlag" :closable="true" class="dialog-title-light-bg" draggable
:mask-closable="false">
<Tabs type="line" class="no-box-shadow no-padding" v-model="activeSFCTab">
<TabPane label="Vue2" name="vue2">
<code-editor v-if="showExportSFCDialogFlag" :mode="'html'" :readonly="true" v-model="sfcCode" :user-worker="false"></code-editor>
</TabPane>
<TabPane label="Vue3" name="vue3">
<code-editor v-if="showExportSFCDialogFlag" :mode="'html'" :readonly="true" v-model="sfcCodeV3" :user-worker="false"></code-editor>
</TabPane>
</Tabs>
<div slot="footer" class="dialog-footer">
<Button size="default" type="primary" class="copy-vue2-sfc-btn" :data-clipboard-text="sfcCode" @click="copyV2SFC">
{{i18nt('designer.hint.copyVue2SFC')}}</Button>
<Button size="default" type="primary" class="copy-vue3-sfc-btn" :data-clipboard-text="sfcCodeV3" @click="copyV3SFC">
{{i18nt('designer.hint.copyVue3SFC')}}</Button>
<Button size="default" @click="saveV2SFC">{{i18nt('designer.hint.saveVue2SFC')}}</Button>
<Button size="default" @click="saveV3SFC">{{i18nt('designer.hint.saveVue3SFC')}}</Button>
<Button size="default" type="default" @click="showExportSFCDialogFlag = false">
{{i18nt('designer.hint.closePreview')}}</Button>
</div>
</Modal>
<Modal :title="this.i18nt('designer.hint.saveFileTitle')" width="300px"
v-model="showExportSFCFileNameDialogFlag" :closable="true" class="dialog-title-light-bg" draggable
:mask-closable="false">
<Input type="text" size="large" v-model="saveFileName" :placeholder="i18nt('designer.hint.fileNameForSave')" clearable/>
<div slot="footer" class="dialog-footer">
<Button size="default" type="primary" @click="saveFileExec">{{i18nt('designer.hint.confirm')}}</Button>
<Button size="default" type="default" @click="showExportSFCFileNameDialogFlag = false">{{i18nt('designer.hint.closePreview')}}</Button>
</div>
</Modal>
</div>
</template>
<script>
import VFormRender from '@/components-iview/form-render/index'
import CodeEditor from '@/components-iview/code-editor/index'
import Clipboard from 'clipboard'
import {deepClone, copyToClipboard, generateId, getQueryParam} from "@/utils/util";
import i18n from "../../utils/i18n";
import {generateCode} from "../../utils/code-generator";
import {genSFC} from "../../utils/sfc-generator";
import loadBeautifier from "@/utils/beautifierLoader";
import { saveAs } from 'file-saver'
export default {
name: "ToolbarPanel",
mixins: [i18n],
components: {
VFormRender,
//CodeEditor: () => import('@/components/code-editor/index'),
CodeEditor,
Clipboard,
},
props: {
designer: Object
},
mounted() {
if(this.load) this.reloadCode();
},
data() {
return {
showPreviewDialogFlag: false,
showImportJsonDialogFlag: false,
showExportJsonDialogFlag: false,
showExportCodeDialogFlag: false,
showFormDataDialogFlag: false,
showExportSFCDialogFlag: false,
showExportSFCFileNameDialogFlag: false,
saveFileHandler:null,
saveFileName:'',
testFunc: '',
importTemplate: '',
jsonContent: '',
jsonRawContent: '',
formDataJson: '',
formDataRawJson: '',
vueCode: '',
htmlCode: '',
sfcCode: '',
sfcCodeV3: '',
activeCodeTab: 'vue',
activeSFCTab: 'vue2',
testFormData: {
// 'userName': '666888',
// 'productItems': [
// {'pName': 'iPhone12', 'pNum': 10},
// {'pName': 'P50', 'pNum': 16},
// ]
}
}
},
computed: {
formJson() {
return {
widgetList: this.designer.widgetList,
formConfig: this.designer.formConfig
}
},
undoDisabled() {
return !this.designer.undoEnabled()
},
redoDisabled() {
return !this.designer.redoEnabled()
},
layoutType() {
return this.designer.getLayoutType()
},
},
methods: {
undoHistory() {
this.designer.undoHistoryStep()
},
redoHistory() {
this.designer.redoHistoryStep()
},
changeLayoutType(newType) {
this.designer.changeLayoutType(newType)
},
clearFormWidget() {
this.designer.clearDesigner()
},
previewForm() {
this.showPreviewDialogFlag = true
},
saveFileExec(){
this.saveAsFile(this.saveFileHandler[0],this.saveFileHandler[1])
},
saveAsFile(fileContent, defaultFileName) {
let value="";
if (!this.saveFileName) {
value = defaultFileName
}else{
value=this.saveFileName ;
}
if (getQueryParam('vscode') == 1) {
this.vsSaveFile(value, fileContent)
return
}
const fileBlob = new Blob([fileContent], { type: 'text/plain;charset=utf-8' })
saveAs(fileBlob ,value)
},
vsSaveFile(fileName, fileContent) {
const msgObj = {
cmd: 'writeFile',
data: {
fileName,
code: fileContent
}
}
window.parent.postMessage(msgObj, '*')
},
importJson() {
this.importTemplate = JSON.stringify(this.designer.getImportTemplate(), null, ' ')
this.showImportJsonDialogFlag = true
},
doJsonImport() {
try {
let importObj = JSON.parse(this.importTemplate)
this.designer.loadFormJson(importObj)
this.showImportJsonDialogFlag = false
this.$Message.success(this.i18nt('designer.hint.importJsonSuccess'))
this.designer.emitHistoryChange()
} catch (ex) {
this.$Message.error(ex + '')
}
},
exportJson() {
//alert( JSON.stringify(this.designer.widgetList) )
//this.jsonContent = JSON.stringify(this.designer.widgetList, null, ' ')
let widgetList = deepClone(this.designer.widgetList)
let formConfig = deepClone(this.designer.formConfig)
this.jsonContent = JSON.stringify({
widgetList,
formConfig
}, null, ' ')
this.jsonRawContent = JSON.stringify({
widgetList,
formConfig
})
this.showExportJsonDialogFlag = true;
this.$forceUpdate();
this.$nextTick(() => {
let copyClipboard = new Clipboard('.copy-json-btn')
copyClipboard.on('success', e => {
this.$Message.success(this.i18nt('designer.hint.copyJsonSuccess'))
copyClipboard.destroy() // 释放内存
})
copyClipboard.on('error', e => { // 不支持复制
this.$Message.error(this.i18nt('designer.hint.copyJsonFail'))
copyClipboard.destroy()
})
})
},
exportCode() {
this.vueCode = generateCode(this.formJson)
this.htmlCode = generateCode(this.formJson, 'html')
this.showExportCodeDialogFlag = true
this.$nextTick(() => {
let vueClipboard = new Clipboard('.copy-vue-btn')
vueClipboard.on('success', e => {
this.$Message.success(this.i18nt('designer.hint.copyVueCodeSuccess'))
vueClipboard.destroy() // 释放内存
})
vueClipboard.on('error', e => { // 不支持复制
this.$Message.error(this.i18nt('designer.hint.copyVueCodeFail'))
vueClipboard.destroy()
})
let htmlClipboard = new Clipboard('.copy-html-btn')
htmlClipboard.on('success', e => {
this.$Message.success(this.i18nt('designer.hint.copyHtmlCodeSuccess'))
htmlClipboard.destroy() // 释放内存
})
htmlClipboard.on('error', e => { // 不支持复制
this.$Message.error(this.i18nt('designer.hint.copyHtmlCodeFail'))
htmlClipboard.destroy()
})
})
},
copyVueCode(e) {
copyToClipboard(this.vueCode, e,
this.$Message,
this.i18nt('designer.hint.copyVueCodeSuccess'),
this.i18nt('designer.hint.copyVueCodeFail')
)
},
copyHtmlCode(e) {
copyToClipboard(this.htmlCode, e,
this.$Message,
this.i18nt('designer.hint.copyHtmlCodeSuccess'),
this.i18nt('designer.hint.copyHtmlCodeFail')
)
},
saveVueCode() {
this.saveFileHandler=[this.vueCode, `vform${generateId()}.vue`];
this.showExportSFCFileNameDialogFlag=true;
},
saveHtmlCode() {
this.saveFileHandler=[this.htmlCode, `vform${generateId()}.vue`];
this.showExportSFCFileNameDialogFlag=true;
},
generateSFC() {
loadBeautifier(beautifier => {
this.sfcCode = genSFC(this.designer.formConfig, this.designer.widgetList, beautifier)
this.sfcCodeV3 = genSFC(this.designer.formConfig, this.designer.widgetList, beautifier, true)
this.showExportSFCDialogFlag = true
})
},
copyV2SFC(e) {
copyToClipboard(this.sfcCode, e,
this.$Message,
this.i18nt('designer.hint.copySFCSuccess'),
this.i18nt('designer.hint.copySFCFail')
)
},
copyV3SFC(e) {
copyToClipboard(this.sfcCodeV3, e,
this.$Message,
this.i18nt('designer.hint.copySFCSuccess'),
this.i18nt('designer.hint.copySFCFail')
)
},
saveV2SFC() {
this.saveFileHandler=[this.sfcCode, `vformV2-${generateId()}.vue`];
this.showExportSFCFileNameDialogFlag=true;
// this.saveAsFile(this.sfcCode, `vformV2-${generateId()}.vue`)
},
saveV3SFC() {
this.saveFileHandler=[this.sfcCodeV3, `vformV3-${generateId()}.vue`];
this.showExportSFCFileNameDialogFlag=true;
// this.saveAsFile(this.sfcCodeV3, `vformV3-${generateId()}.vue`)
},
getFormData() {
this.$refs['preForm'].getFormData().then(formData => {
//alert( JSON.stringify(formData) )
this.formDataJson = JSON.stringify(formData, null, ' ')
this.formDataRawJson = JSON.stringify(formData)
this.showFormDataDialogFlag = true
this.$nextTick(() => {
let copyClipboard = new Clipboard('.copy-form-data-json-btn')
copyClipboard.on('success', e => {
this.$Message.success(this.i18nt('designer.hint.copyJsonSuccess'))
copyClipboard.destroy() // 释放内存
})
copyClipboard.on('error', e => { // 不支持复制
this.$Message.error(this.i18nt('designer.hint.copyJsonFail'))
copyClipboard.destroy()
})
})
}).catch(error => {
this.$Message.error(error)
})
},
resetForm() {
this.$refs['preForm'].resetForm()
},
setFormDisabled() {
this.$refs['preForm'].disableForm()
},
setFormEnabled() {
this.$refs['preForm'].enableForm()
},
handleFormChange(fieldName, newValue, oldValue, formModel) {
console.log('---formChange start---')
console.log('fieldName', fieldName)
console.log('newValue', newValue)
console.log('oldValue', oldValue)
console.log('formModel', formModel)
console.log('---formChange end---')
},
testOnAppendButtonClick(clickedWidget) {
console.log('test', clickedWidget)
},
testOnButtonClick(button) {
console.log('test', button)
},
handlerNativeChange(args){
}
}
}
</script>
<style lang="scss" scoped>
div.toolbar-container {
//border-bottom: 1px solid #EBEEF5;
}
.left-toolbar {
float: left;
font-size: 16px;
}
.right-toolbar {
float: right;
::v-deep .el-button--text {
font-size: 14px !important;
}
.ivu-icon{
font-size:18px;
}
}
.el-button i {
margin-right: 3px;
}
.small-padding-dialog {
::v-deep .el-dialog__header {
padding-top: 3px;
padding-bottom: 3px;
background: #f1f2f3;
}
::v-deep .el-dialog__body {
padding: 12px 15px 12px 15px;
.el-alert {
padding: 0 10px;
}
}
::v-deep .ace-container {
border: 1px solid #DCDFE6;
}
}
.dialog-title-light-bg {
::v-deep .el-dialog__header {
background: #f1f2f3;
}
}
.no-box-shadow {
box-shadow: none;
}
.form-render-wrapper {
//height: calc(100vh - 142px);
}
.form-render-wrapper.h5-layout {
margin: 0 auto;
width: 420px;
border-radius: 15px;
//border-width: 10px;
background-color:blue;
box-shadow: 0 0 1px 10px #495060;
height: calc(100vh - 142px);
}
</style>