feat: flowable前端改造

This commit is contained in:
tony
2022-12-11 21:08:14 +08:00
parent dcbef01709
commit ae0226a8f4
112 changed files with 12865 additions and 2660 deletions

View File

@@ -0,0 +1,22 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
mounted() {
// 取消开始的loading动画
const preLoader = document.querySelector('#pre-loader')
preLoader.style.display = 'none'
// fix: firefox 下 拖拽 会新打卡一个选项卡
// https://github.com/JakHuang/form-generator/issues/15
document.body.ondrop = event => {
event.preventDefault()
event.stopPropagation()
}
}
}
</script>

View File

@@ -41,7 +41,7 @@
<el-button @click="close">
取消
</el-button>
<el-button type="primary" @click="handleConfirm">
<el-button type="primary" @click="handelConfirm">
确定
</el-button>
</div>
@@ -94,7 +94,7 @@ export default {
close(e) {
this.$emit('update:visible', false)
},
handleConfirm() {
handelConfirm() {
this.$refs.elForm.validate(valid => {
if (!valid) return
this.$emit('confirm', { ...this.formData })
@@ -104,3 +104,7 @@ export default {
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,100 +1,120 @@
<script>
import draggable from 'vuedraggable'
import render from '@/utils/generator/render'
const components = {
itemBtns(h, element, index, parent) {
const { copyItem, deleteItem } = this.$listeners
return [
<span class="drawing-item-copy" title="复制" onClick={event => {
copyItem(element, parent); event.stopPropagation()
}}>
<i class="el-icon-copy-document" />
</span>,
<span class="drawing-item-delete" title="删除" onClick={event => {
deleteItem(index, parent); event.stopPropagation()
}}>
<i class="el-icon-delete" />
</span>
]
}
}
const layouts = {
colFormItem(h, element, index, parent) {
const { activeItem } = this.$listeners
let className = this.activeId === element.formId ? 'drawing-item active-from-item' : 'drawing-item'
if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
return (
<el-col span={element.span} class={className}
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
<el-form-item label-width={element.labelWidth ? `${element.labelWidth}px` : null}
label={element.label} required={element.required}>
<render key={element.renderKey} conf={element} onInput={ event => {
this.$set(element, 'defaultValue', event)
}} />
</el-form-item>
{components.itemBtns.apply(this, arguments)}
</el-col>
)
},
rowFormItem(h, element, index, parent) {
const { activeItem } = this.$listeners
const className = this.activeId === element.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item'
let child = renderChildren.apply(this, arguments)
if (element.type === 'flex') {
child = <el-row type={element.type} justify={element.justify} align={element.align}>
{child}
</el-row>
}
return (
<el-col span={element.span}>
<el-row gutter={element.gutter} class={className}
nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
<span class="component-name">{element.componentName}</span>
<draggable list={element.children} animation={340} group="componentsGroup" class="drag-wrapper">
{child}
</draggable>
{components.itemBtns.apply(this, arguments)}
</el-row>
</el-col>
)
}
}
function renderChildren(h, element, index, parent) {
if (!Array.isArray(element.children)) return null
return element.children.map((el, i) => {
const layout = layouts[el.layout]
if (layout) {
return layout.call(this, h, el, i, element.children)
}
return layoutIsNotFound()
})
}
function layoutIsNotFound() {
throw new Error(`没有与${this.element.layout}匹配的layout`)
}
export default {
components: {
render,
draggable
},
props: [
'element',
'index',
'drawingList',
'activeId',
'formConf'
],
render(h) {
const layout = layouts[this.element.layout]
if (layout) {
return layout.call(this, h, this.element, this.index, this.drawingList)
}
return layoutIsNotFound()
}
}
</script>
<script>
import draggable from 'vuedraggable'
import render from '@/components/render/render'
const components = {
itemBtns(h, currentItem, index, list) {
const { copyItem, deleteItem } = this.$listeners
return [
<span class="drawing-item-copy" title="复制" onClick={event => {
copyItem(currentItem, list); event.stopPropagation()
}}>
<i class="el-icon-copy-document" />
</span>,
<span class="drawing-item-delete" title="删除" onClick={event => {
deleteItem(index, list); event.stopPropagation()
}}>
<i class="el-icon-delete" />
</span>
]
}
}
const layouts = {
colFormItem(h, currentItem, index, list) {
const { activeItem } = this.$listeners
const config = currentItem.__config__
const child = renderChildren.apply(this, arguments)
let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
if (config.showLabel === false) labelWidth = '0'
return (
<el-col span={config.span} class={className}
nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}>
<el-form-item label-width={labelWidth}
label={config.showLabel ? config.label : ''} required={config.required}>
<render key={config.renderKey} conf={currentItem} onInput={ event => {
this.$set(config, 'defaultValue', event)
}}>
{child}
</render>
</el-form-item>
{components.itemBtns.apply(this, arguments)}
</el-col>
)
},
rowFormItem(h, currentItem, index, list) {
const { activeItem } = this.$listeners
const config = currentItem.__config__
const className = this.activeId === config.formId
? 'drawing-row-item active-from-item'
: 'drawing-row-item'
let child = renderChildren.apply(this, arguments)
if (currentItem.type === 'flex') {
child = <el-row type={currentItem.type} justify={currentItem.justify} align={currentItem.align}>
{child}
</el-row>
}
return (
<el-col span={config.span}>
<el-row gutter={config.gutter} class={className}
nativeOnClick={event => { activeItem(currentItem); event.stopPropagation() }}>
<span class="component-name">{config.componentName}</span>
<draggable list={config.children || []} animation={340}
group="componentsGroup" class="drag-wrapper">
{child}
</draggable>
{components.itemBtns.apply(this, arguments)}
</el-row>
</el-col>
)
},
raw(h, currentItem, index, list) {
const config = currentItem.__config__
const child = renderChildren.apply(this, arguments)
return <render key={config.renderKey} conf={currentItem} onInput={ event => {
this.$set(config, 'defaultValue', event)
}}>
{child}
</render>
}
}
function renderChildren(h, currentItem, index, list) {
const config = currentItem.__config__
if (!Array.isArray(config.children)) return null
return config.children.map((el, i) => {
const layout = layouts[el.__config__.layout]
if (layout) {
return layout.call(this, h, el, i, config.children)
}
return layoutIsNotFound.call(this)
})
}
function layoutIsNotFound() {
throw new Error(`没有与${this.currentItem.__config__.layout}匹配的layout`)
}
export default {
components: {
render,
draggable
},
props: [
'currentItem',
'index',
'drawingList',
'activeId',
'formConf'
],
render(h) {
const layout = layouts[this.currentItem.__config__.layout]
if (layout) {
return layout.call(this, h, this.currentItem, this.index, this.drawingList)
}
return layoutIsNotFound.call(this)
}
}
</script>

View File

@@ -0,0 +1,332 @@
<template>
<div>
<el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose">
<div style="height:100%">
<el-row style="height:100%;overflow:auto">
<el-col :md="24" :lg="12" class="left-editor">
<div class="setting" title="资源引用" @click="showResource">
<el-badge :is-dot="!!resources.length" class="item">
<i class="el-icon-setting" />
</el-badge>
</div>
<el-tabs v-model="activeTab" type="card" class="editor-tabs">
<el-tab-pane name="html">
<span slot="label">
<i v-if="activeTab==='html'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
template
</span>
</el-tab-pane>
<el-tab-pane name="js">
<span slot="label">
<i v-if="activeTab==='js'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
script
</span>
</el-tab-pane>
<el-tab-pane name="css">
<span slot="label">
<i v-if="activeTab==='css'" class="el-icon-edit" />
<i v-else class="el-icon-document" />
css
</span>
</el-tab-pane>
</el-tabs>
<div v-show="activeTab==='html'" id="editorHtml" class="tab-editor" />
<div v-show="activeTab==='js'" id="editorJs" class="tab-editor" />
<div v-show="activeTab==='css'" id="editorCss" class="tab-editor" />
</el-col>
<el-col :md="24" :lg="12" class="right-preview">
<div class="action-bar" :style="{'text-align': 'left'}">
<span class="bar-btn" @click="runCode">
<i class="el-icon-refresh" />
刷新
</span>
<span class="bar-btn" @click="exportFile">
<i class="el-icon-download" />
导出vue文件
</span>
<span ref="copyBtn" class="bar-btn copy-btn">
<i class="el-icon-document-copy" />
复制代码
</span>
<span class="bar-btn delete-btn" @click="$emit('update:visible', false)">
<i class="el-icon-circle-close" />
关闭
</span>
</div>
<iframe
v-show="isIframeLoaded"
ref="previewPage"
class="result-wrapper"
frameborder="0"
src="preview.html"
@load="iframeLoad"
/>
<div v-show="!isIframeLoaded" v-loading="true" class="result-wrapper" />
</el-col>
</el-row>
</div>
</el-drawer>
<resource-dialog
:visible.sync="resourceVisible"
:origin-resource="resources"
@save="setResource"
/>
</div>
</template>
<script>
import { parse } from '@babel/parser'
import ClipboardJS from 'clipboard'
import { saveAs } from 'file-saver'
import {
makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/utils/generator/html'
import { makeUpJs } from '@/utils/generator/js'
import { makeUpCss } from '@/utils/generator/css'
import { exportDefault, beautifierConf, titleCase } from '@/utils/index'
import ResourceDialog from './ResourceDialog'
import loadMonaco from '@/utils/loadMonaco'
import loadBeautifier from '@/utils/loadBeautifier'
const editorObj = {
html: null,
js: null,
css: null
}
const mode = {
html: 'html',
js: 'javascript',
css: 'css'
}
let beautifier
let monaco
export default {
components: { ResourceDialog },
props: ['formData', 'generateConf'],
data() {
return {
activeTab: 'html',
htmlCode: '',
jsCode: '',
cssCode: '',
codeFrame: '',
isIframeLoaded: false,
isInitcode: false, // 保证open后两个异步只执行一次runcode
isRefreshCode: false, // 每次打开都需要重新刷新代码
resourceVisible: false,
scripts: [],
links: [],
monaco: null
}
},
computed: {
resources() {
return this.scripts.concat(this.links)
}
},
watch: {},
created() {
},
mounted() {
window.addEventListener('keydown', this.preventDefaultSave)
const clipboard = new ClipboardJS('.copy-btn', {
text: trigger => {
const codeStr = this.generateCode()
this.$notify({
title: '成功',
message: '代码已复制到剪切板,可粘贴。',
type: 'success'
})
return codeStr
}
})
clipboard.on('error', e => {
this.$message.error('代码复制失败')
})
},
beforeDestroy() {
window.removeEventListener('keydown', this.preventDefaultSave)
},
methods: {
preventDefaultSave(e) {
if (e.key === 's' && (e.metaKey || e.ctrlKey)) {
e.preventDefault()
}
},
onOpen() {
const { type } = this.generateConf
this.htmlCode = makeUpHtml(this.formData, type)
this.jsCode = makeUpJs(this.formData, type)
this.cssCode = makeUpCss(this.formData)
loadBeautifier(btf => {
beautifier = btf
this.htmlCode = beautifier.html(this.htmlCode, beautifierConf.html)
this.jsCode = beautifier.js(this.jsCode, beautifierConf.js)
this.cssCode = beautifier.css(this.cssCode, beautifierConf.html)
loadMonaco(val => {
monaco = val
this.setEditorValue('editorHtml', 'html', this.htmlCode)
this.setEditorValue('editorJs', 'js', this.jsCode)
this.setEditorValue('editorCss', 'css', this.cssCode)
if (!this.isInitcode) {
this.isRefreshCode = true
this.isIframeLoaded && (this.isInitcode = true) && this.runCode()
}
})
})
},
onClose() {
this.isInitcode = false
this.isRefreshCode = false
},
iframeLoad() {
if (!this.isInitcode) {
this.isIframeLoaded = true
this.isRefreshCode && (this.isInitcode = true) && this.runCode()
}
},
setEditorValue(id, type, codeStr) {
if (editorObj[type]) {
editorObj[type].setValue(codeStr)
} else {
editorObj[type] = monaco.editor.create(document.getElementById(id), {
value: codeStr,
theme: 'vs-dark',
language: mode[type],
automaticLayout: true
})
}
// ctrl + s 刷新
editorObj[type].onKeyDown(e => {
if (e.keyCode === 49 && (e.metaKey || e.ctrlKey)) {
this.runCode()
}
})
},
runCode() {
const jsCodeStr = editorObj.js.getValue()
try {
const ast = parse(jsCodeStr, { sourceType: 'module' })
const astBody = ast.program.body
if (astBody.length > 1) {
this.$confirm(
'js格式不能识别仅支持修改export default的对象内容',
'提示',
{
type: 'warning'
}
)
return
}
if (astBody[0].type === 'ExportDefaultDeclaration') {
const postData = {
type: 'refreshFrame',
data: {
generateConf: this.generateConf,
html: editorObj.html.getValue(),
js: jsCodeStr.replace(exportDefault, ''),
css: editorObj.css.getValue(),
scripts: this.scripts,
links: this.links
}
}
this.$refs.previewPage.contentWindow.postMessage(
postData,
location.origin
)
} else {
this.$message.error('请使用export default')
}
} catch (err) {
this.$message.error(`js错误${err}`)
console.error(err)
}
},
generateCode() {
const html = vueTemplate(editorObj.html.getValue())
const script = vueScript(editorObj.js.getValue())
const css = cssStyle(editorObj.css.getValue())
return beautifier.html(html + script + css, beautifierConf.html)
},
exportFile() {
this.$prompt('文件名:', '导出文件', {
inputValue: `${+new Date()}.vue`,
closeOnClickModal: false,
inputPlaceholder: '请输入文件名'
}).then(({ value }) => {
if (!value) value = `${+new Date()}.vue`
const codeStr = this.generateCode()
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
saveAs(blob, value)
})
},
showResource() {
this.resourceVisible = true
},
setResource(arr) {
const scripts = []; const
links = []
if (Array.isArray(arr)) {
arr.forEach(item => {
if (item.endsWith('.css')) {
links.push(item)
} else {
scripts.push(item)
}
})
this.scripts = scripts
this.links = links
} else {
this.scripts = []
this.links = []
}
}
}
}
</script>
<style lang="scss" scoped>
@import '@/styles/mixin.scss';
.tab-editor {
position: absolute;
top: 33px;
bottom: 0;
left: 0;
right: 0;
font-size: 14px;
}
.left-editor {
position: relative;
height: 100%;
background: #1e1e1e;
overflow: hidden;
}
.setting{
position: absolute;
right: 15px;
top: 3px;
color: #a9f122;
font-size: 18px;
cursor: pointer;
z-index: 1;
}
.right-preview {
height: 100%;
.result-wrapper {
height: calc(100vh - 33px);
width: 100%;
overflow: auto;
padding: 12px;
box-sizing: border-box;
}
}
@include action-bar;
::v-deep .el-drawer__header {
display: none;
}
</style>

View File

@@ -1,123 +1,123 @@
<template>
<div class="icon-dialog">
<el-dialog
v-bind="$attrs"
width="980px"
:modal-append-to-body="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<div slot="title">
选择图标
<el-input
v-model="key"
size="mini"
:style="{width: '260px'}"
placeholder="请输入图标名称"
prefix-icon="el-icon-search"
clearable
/>
</div>
<ul class="icon-ul">
<li
v-for="icon in iconList"
:key="icon"
:class="active===icon?'active-item':''"
@click="onSelect(icon)"
>
<i :class="icon" />
<div>{{ icon }}</div>
</li>
</ul>
</el-dialog>
</div>
</template>
<script>
import iconList from '@/utils/generator/icon.json'
const originList = iconList.map(name => `el-icon-${name}`)
export default {
inheritAttrs: false,
props: ['current'],
data() {
return {
iconList: originList,
active: null,
key: ''
}
},
watch: {
key(val) {
if (val) {
this.iconList = originList.filter(name => name.indexOf(val) > -1)
} else {
this.iconList = originList
}
}
},
methods: {
onOpen() {
this.active = this.current
this.key = ''
},
onClose() {},
onSelect(icon) {
this.active = icon
this.$emit('select', icon)
this.$emit('update:visible', false)
}
}
}
</script>
<style lang="scss" scoped>
.icon-ul {
margin: 0;
padding: 0;
font-size: 0;
li {
list-style-type: none;
text-align: center;
font-size: 14px;
display: inline-block;
width: 16.66%;
box-sizing: border-box;
height: 108px;
padding: 15px 6px 6px 6px;
cursor: pointer;
overflow: hidden;
&:hover {
background: #f2f2f2;
}
&.active-item{
background: #e1f3fb;
color: #7a6df0
}
> i {
font-size: 30px;
line-height: 50px;
}
}
}
.icon-dialog {
::v-deep .el-dialog {
border-radius: 8px;
margin-bottom: 0;
margin-top: 4vh !important;
display: flex;
flex-direction: column;
max-height: 92vh;
overflow: hidden;
box-sizing: border-box;
.el-dialog__header {
padding-top: 14px;
}
.el-dialog__body {
margin: 0 20px 20px 20px;
padding: 0;
overflow: auto;
}
}
}
</style>
<template>
<div class="icon-dialog">
<el-dialog
v-bind="$attrs"
width="980px"
:modal-append-to-body="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<div slot="title">
选择图标
<el-input
v-model="key"
size="mini"
:style="{width: '260px'}"
placeholder="请输入图标名称"
prefix-icon="el-icon-search"
clearable
/>
</div>
<ul class="icon-ul">
<li
v-for="icon in iconList"
:key="icon"
:class="active===icon?'active-item':''"
@click="onSelect(icon)"
>
<i :class="icon" />
<div>{{ icon }}</div>
</li>
</ul>
</el-dialog>
</div>
</template>
<script>
import iconList from '@/utils/generator/icon.json'
const originList = iconList.map(name => `el-icon-${name}`)
export default {
inheritAttrs: false,
props: ['current'],
data() {
return {
iconList: originList,
active: null,
key: ''
}
},
watch: {
key(val) {
if (val) {
this.iconList = originList.filter(name => name.indexOf(val) > -1)
} else {
this.iconList = originList
}
}
},
methods: {
onOpen() {
this.active = this.current
this.key = ''
},
onClose() {},
onSelect(icon) {
this.active = icon
this.$emit('select', icon)
this.$emit('update:visible', false)
}
}
}
</script>
<style lang="scss" scoped>
.icon-ul {
margin: 0;
padding: 0;
font-size: 0;
li {
list-style-type: none;
text-align: center;
font-size: 14px;
display: inline-block;
width: 16.66%;
box-sizing: border-box;
height: 108px;
padding: 15px 6px 6px 6px;
cursor: pointer;
overflow: hidden;
&:hover {
background: #f2f2f2;
}
&.active-item{
background: #e1f3fb;
color: #7a6df0
}
> i {
font-size: 30px;
line-height: 50px;
}
}
}
.icon-dialog {
::v-deep .el-dialog {
border-radius: 8px;
margin-bottom: 0;
margin-top: 4vh !important;
display: flex;
flex-direction: column;
max-height: 92vh;
overflow: hidden;
box-sizing: border-box;
.el-dialog__header {
padding-top: 14px;
}
.el-dialog__body {
margin: 0 20px 20px 20px;
padding: 0;
overflow: auto;
}
}
}
</style>

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -59,7 +59,7 @@
<div slot="footer">
<el-button
type="primary"
@click="handleConfirm"
@click="handelConfirm"
>
确定
</el-button>
@@ -72,6 +72,9 @@
</template>
<script>
import { isNumberStr } from '@/utils/index'
import { getTreeNodeId, saveTreeNodeId } from '@/utils/db'
const id = getTreeNodeId()
export default {
components: {},
@@ -79,7 +82,7 @@ export default {
props: [],
data() {
return {
id: 100,
id,
formData: {
label: undefined,
value: undefined
@@ -118,6 +121,9 @@ export default {
// eslint-disable-next-line func-names
'formData.value': function (val) {
this.dataType = isNumberStr(val) ? 'number' : 'string'
},
id(val) {
saveTreeNodeId(val)
}
},
created() {},
@@ -133,7 +139,7 @@ export default {
close() {
this.$emit('update:visible', false)
},
handleConfirm() {
handelConfirm() {
this.$refs.elForm.validate(valid => {
if (!valid) return
if (this.dataType === 'number') {
@@ -147,3 +153,6 @@ export default {
}
}
</script>
<style lang="scss" scoped>
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
import Vue from 'vue'
import App from './App.vue'
import router from '@/router'
import '@/styles/index.scss'
import '@/icons'
import axios from 'axios'
import Tinymce from '@/components/tinymce/index.vue'
Vue.component('tinymce', Tinymce)
Vue.config.productionTip = false
Vue.prototype.$axios = axios
new Vue({
router,
render: h => h(App)
}).$mount('#app')

View File

@@ -0,0 +1,61 @@
import Vue from 'vue'
import { loadScriptQueue } from '@/utils/loadScript'
import axios from 'axios'
import Tinymce from '@/components/tinymce/index.vue'
Vue.component('tinymce', Tinymce)
Vue.prototype.$axios = axios
const $previewApp = document.getElementById('previewApp')
const childAttrs = {
file: '',
dialog: ' width="600px" class="dialog-width" v-if="visible" :visible.sync="visible" :modal-append-to-body="false" '
}
window.addEventListener('message', init, false)
function buildLinks(links) {
let strs = ''
links.forEach(url => {
strs += `<link href="${url}" rel="stylesheet">`
})
return strs
}
function init(event) {
if (event.data.type === 'refreshFrame') {
const code = event.data.data
const attrs = childAttrs[code.generateConf.type]
let links = ''
if (Array.isArray(code.links) && code.links.length > 0) {
links = buildLinks(code.links)
}
$previewApp.innerHTML = `${links}<style>${code.css}</style><div id="app"></div>`
if (Array.isArray(code.scripts) && code.scripts.length > 0) {
loadScriptQueue(code.scripts, () => {
newVue(attrs, code.js, code.html)
})
} else {
newVue(attrs, code.js, code.html)
}
}
}
function newVue(attrs, main, html) {
main = eval(`(${main})`)
main.template = `<div>${html}</div>`
new Vue({
components: {
child: main
},
data() {
return {
visible: true
}
},
template: `<div><child ${attrs}/></div>`
}).$mount('#app')
}