将 tool 合并到 infra 模块

This commit is contained in:
YunaiV
2022-03-11 00:39:34 +08:00
parent 716bbb9813
commit 5a2169b688
139 changed files with 1192 additions and 1476 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

@@ -0,0 +1,110 @@
<template>
<div>
<el-dialog
v-bind="$attrs"
width="500px"
:close-on-click-modal="false"
:modal-append-to-body="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<el-row :gutter="15">
<el-form
ref="elForm"
:model="formData"
:rules="rules"
size="medium"
label-width="100px"
>
<el-col :span="24">
<el-form-item label="生成类型" prop="type">
<el-radio-group v-model="formData.type">
<el-radio-button
v-for="(item, index) in typeOptions"
:key="index"
:label="item.value"
:disabled="item.disabled"
>
{{ item.label }}
</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="showFileName" label="文件名" prop="fileName">
<el-input v-model="formData.fileName" placeholder="请输入文件名" clearable />
</el-form-item>
</el-col>
</el-form>
</el-row>
<div slot="footer">
<el-button @click="close">
取消
</el-button>
<el-button type="primary" @click="handelConfirm">
确定
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
inheritAttrs: false,
props: ['showFileName'],
data() {
return {
formData: {
fileName: undefined,
type: 'file'
},
rules: {
fileName: [{
required: true,
message: '请输入文件名',
trigger: 'blur'
}],
type: [{
required: true,
message: '生成类型不能为空',
trigger: 'change'
}]
},
typeOptions: [{
label: '页面',
value: 'file'
}, {
label: '弹窗',
value: 'dialog'
}]
}
},
computed: {
},
watch: {},
mounted() {},
methods: {
onOpen() {
if (this.showFileName) {
this.formData.fileName = `${+new Date()}.vue`
}
},
onClose() {
},
close(e) {
this.$emit('update:visible', false)
},
handelConfirm() {
this.$refs.elForm.validate(valid => {
if (!valid) return
this.$emit('confirm', { ...this.formData })
this.close()
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,120 @@
<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,331 @@
<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 '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/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'
}).catch(() => {});
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

@@ -0,0 +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/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

@@ -0,0 +1,158 @@
<template>
<div>
<el-dialog
v-bind="$attrs"
:close-on-click-modal="false"
:modal-append-to-body="false"
v-on="$listeners"
@open="onOpen"
@close="onClose"
>
<el-row :gutter="0">
<el-form
ref="elForm"
:model="formData"
:rules="rules"
size="small"
label-width="100px"
>
<el-col :span="24">
<el-form-item
label="选项名"
prop="label"
>
<el-input
v-model="formData.label"
placeholder="请输入选项名"
clearable
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item
label="选项值"
prop="value"
>
<el-input
v-model="formData.value"
placeholder="请输入选项值"
clearable
>
<el-select
slot="append"
v-model="dataType"
:style="{width: '100px'}"
>
<el-option
v-for="(item, index) in dataTypeOptions"
:key="index"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
/>
</el-select>
</el-input>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div slot="footer">
<el-button
type="primary"
@click="handelConfirm"
>
确定
</el-button>
<el-button @click="close">
取消
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { isNumberStr } from '@/utils/index'
import { getTreeNodeId, saveTreeNodeId } from '@/utils/db'
const id = getTreeNodeId()
export default {
components: {},
inheritAttrs: false,
props: [],
data() {
return {
id,
formData: {
label: undefined,
value: undefined
},
rules: {
label: [
{
required: true,
message: '请输入选项名',
trigger: 'blur'
}
],
value: [
{
required: true,
message: '请输入选项值',
trigger: 'blur'
}
]
},
dataType: 'string',
dataTypeOptions: [
{
label: '字符串',
value: 'string'
},
{
label: '数字',
value: 'number'
}
]
}
},
computed: {},
watch: {
// eslint-disable-next-line func-names
'formData.value': function (val) {
this.dataType = isNumberStr(val) ? 'number' : 'string'
},
id(val) {
saveTreeNodeId(val)
}
},
created() {},
mounted() {},
methods: {
onOpen() {
this.formData = {
label: undefined,
value: undefined
}
},
onClose() {},
close() {
this.$emit('update:visible', false)
},
handelConfirm() {
this.$refs.elForm.validate(valid => {
if (!valid) return
if (this.dataType === 'number') {
this.formData.value = parseFloat(this.formData.value)
}
this.formData.id = this.id++
this.$emit('commit', this.formData)
this.close()
})
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,469 @@
<template>
<div class="container">
<div class="left-board">
<div class="logo-wrapper">
<div class="logo">
<img :src="logo" alt="logo"> Form Generator
<a class="github" href="https://github.com/JakHuang/form-generator" target="_blank">
<img src="https://github.githubassets.com/pinned-octocat.svg" alt>
</a>
</div>
</div>
<el-scrollbar class="left-scrollbar">
<div class="components-list">
<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
<div class="components-title">
<svg-icon icon-class="component" />
{{ item.title }}
</div>
<draggable
class="components-draggable"
:list="item.list"
:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for="(element, index) in item.list"
:key="index"
class="components-item"
@click="addComponent(element)"
>
<div class="components-body">
<svg-icon :icon-class="element.__config__.tagIcon" />
{{ element.__config__.label }}
</div>
</div>
</draggable>
</div>
</div>
</el-scrollbar>
</div>
<div class="center-board">
<div class="action-bar">
<el-button icon="el-icon-video-play" type="text" @click="run">
运行
</el-button>
<el-button icon="el-icon-view" type="text" @click="showJson">
查看json
</el-button>
<el-button icon="el-icon-download" type="text" @click="download">
导出vue文件
</el-button>
<el-button class="copy-btn-main" icon="el-icon-document-copy" type="text" @click="copy">
复制代码
</el-button>
<el-button class="delete-btn" icon="el-icon-delete" type="text" @click="empty">
清空
</el-button>
</div>
<el-scrollbar class="center-scrollbar">
<el-row class="center-board-row" :gutter="formConf.gutter">
<el-form
:size="formConf.size"
:label-position="formConf.labelPosition"
:disabled="formConf.disabled"
:label-width="formConf.labelWidth + 'px'"
>
<draggable class="drawing-board" :list="drawingList" :animation="340" group="componentsGroup">
<draggable-item
v-for="(item, index) in drawingList"
:key="item.renderKey"
:drawing-list="drawingList"
:current-item="item"
:index="index"
:active-id="activeId"
:form-conf="formConf"
@activeItem="activeFormItem"
@copyItem="drawingItemCopy"
@deleteItem="drawingItemDelete"
/>
</draggable>
<div v-show="!drawingList.length" class="empty-info">
从左侧拖入或点选组件进行表单设计
</div>
</el-form>
</el-row>
</el-scrollbar>
</div>
<right-panel
:active-data="activeData"
:form-conf="formConf"
:show-field="!!drawingList.length"
@tag-change="tagChange"
@fetch-data="fetchData"
/>
<form-drawer
:visible.sync="drawerVisible"
:form-data="formData"
size="100%"
:generate-conf="generateConf"
/>
<json-drawer
size="60%"
:visible.sync="jsonDrawerVisible"
:json-str="JSON.stringify(formData)"
@refresh="refreshJson"
/>
<code-type-dialog
:visible.sync="dialogVisible"
title="选择生成类型"
:show-file-name="showFileName"
@confirm="generate"
/>
<input id="copyNode" type="hidden">
</div>
</template>
<script>
import draggable from 'vuedraggable'
import { debounce } from 'throttle-debounce'
import { saveAs } from 'file-saver'
import ClipboardJS from 'clipboard'
import render from '@/components/render/render'
import FormDrawer from './FormDrawer'
import JsonDrawer from './JsonDrawer'
import RightPanel from './RightPanel'
import {
inputComponents, selectComponents, layoutComponents, formConf
} from '@/components/generator/config'
import {
exportDefault, beautifierConf, isNumberStr, titleCase, deepClone, isObjectObject
} from '@/utils/index'
import {
makeUpHtml, vueTemplate, vueScript, cssStyle
} from '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/generator/css'
import drawingDefalut from '@/components/generator/drawingDefalut'
import logo from '@/assets/logo/logo.png'
import CodeTypeDialog from './CodeTypeDialog'
import DraggableItem from './DraggableItem'
import {
getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf
} from '@/utils/db'
import loadBeautifier from '@/utils/loadBeautifier'
let beautifier
const emptyActiveData = { style: {}, autosize: {} }
let oldActiveId
let tempActiveData
const drawingListInDB = getDrawingList()
const formConfInDB = getFormConf()
const idGlobal = getIdGlobal()
export default {
components: {
draggable,
render,
FormDrawer,
JsonDrawer,
RightPanel,
CodeTypeDialog,
DraggableItem
},
data() {
return {
logo,
idGlobal,
formConf,
inputComponents,
selectComponents,
layoutComponents,
labelWidth: 100,
drawingList: drawingDefalut,
drawingData: {},
activeId: drawingDefalut[0].formId,
drawerVisible: false,
formData: {},
dialogVisible: false,
jsonDrawerVisible: false,
generateConf: null,
showFileName: false,
activeData: drawingDefalut[0],
saveDrawingListDebounce: debounce(340, saveDrawingList),
saveIdGlobalDebounce: debounce(340, saveIdGlobal),
leftComponents: [
{
title: '输入型组件',
list: inputComponents
},
{
title: '选择型组件',
list: selectComponents
},
{
title: '布局型组件',
list: layoutComponents
}
]
}
},
computed: {
},
watch: {
// eslint-disable-next-line func-names
'activeData.__config__.label': function (val, oldVal) {
if (
this.activeData.placeholder === undefined
|| !this.activeData.__config__.tag
|| oldActiveId !== this.activeId
) {
return
}
this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
},
activeId: {
handler(val) {
oldActiveId = val
},
immediate: true
},
drawingList: {
handler(val) {
this.saveDrawingListDebounce(val)
if (val.length === 0) this.idGlobal = 100
},
deep: true
},
idGlobal: {
handler(val) {
this.saveIdGlobalDebounce(val)
},
immediate: true
}
},
mounted() {
if (Array.isArray(drawingListInDB) && drawingListInDB.length > 0) {
this.drawingList = drawingListInDB
} else {
this.drawingList = drawingDefalut
}
this.activeFormItem(this.drawingList[0])
if (formConfInDB) {
this.formConf = formConfInDB
}
loadBeautifier(btf => {
beautifier = btf
})
const clipboard = new ClipboardJS('#copyNode', {
text: trigger => {
const codeStr = this.generateCode()
this.$notify({
title: '成功',
message: '代码已复制到剪切板,可粘贴。',
type: 'success'
})
return codeStr
}
})
clipboard.on('error', e => {
this.$message.error('代码复制失败')
})
},
methods: {
setObjectValueReduce(obj, strKeys, data) {
const arr = strKeys.split('.')
arr.reduce((pre, item, i) => {
if (arr.length === i + 1) {
pre[item] = data
} else if (!isObjectObject(pre[item])) {
pre[item] = {}
}
return pre[item]
}, obj)
},
setRespData(component, resp) {
const { dataPath, renderKey, dataConsumer } = component.__config__
if (!dataPath || !dataConsumer) return
const respData = dataPath.split('.').reduce((pre, item) => pre[item], resp)
// 将请求回来的数据,赋值到指定属性。
// 以el-tabel为例根据Element文档应该将数据赋值给el-tabel的data属性所以dataConsumer的值应为'data';
// 此时赋值代码可写成 component[dataConsumer] = respData
// 但为支持更深层级的赋值dataConsumer的值为'options.data',使用setObjectValueReduce
this.setObjectValueReduce(component, dataConsumer, respData)
const i = this.drawingList.findIndex(item => item.__config__.renderKey === renderKey)
if (i > -1) this.$set(this.drawingList, i, component)
},
fetchData(component) {
const { dataType, method, url } = component.__config__
if (dataType === 'dynamic' && method && url) {
this.setLoading(component, true)
this.$axios({
method,
url
}).then(resp => {
this.setLoading(component, false)
this.setRespData(component, resp.data)
})
}
},
setLoading(component, val) {
const { directives } = component
if (Array.isArray(directives)) {
const t = directives.find(d => d.name === 'loading')
if (t) t.value = val
}
},
activeFormItem(currentItem) {
this.activeData = currentItem
this.activeId = currentItem.__config__.formId
},
onEnd(obj) {
if (obj.from !== obj.to) {
this.fetchData(tempActiveData)
this.activeData = tempActiveData
this.activeId = this.idGlobal
}
},
addComponent(item) {
const clone = this.cloneComponent(item)
this.fetchData(clone)
this.drawingList.push(clone)
this.activeFormItem(clone)
},
cloneComponent(origin) {
const clone = deepClone(origin)
const config = clone.__config__
config.span = this.formConf.span // 生成代码时会根据span做精简判断
this.createIdAndKey(clone)
clone.placeholder !== undefined && (clone.placeholder += config.label)
tempActiveData = clone
return tempActiveData
},
createIdAndKey(item) {
const config = item.__config__
config.formId = ++this.idGlobal
config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
if (config.layout === 'colFormItem') {
item.__vModel__ = `field${this.idGlobal}`
} else if (config.layout === 'rowFormItem') {
config.componentName = `row${this.idGlobal}`
!Array.isArray(config.children) && (config.children = [])
delete config.label // rowFormItem无需配置label属性
}
if (Array.isArray(config.children)) {
config.children = config.children.map(childItem => this.createIdAndKey(childItem))
}
return item
},
AssembleFormData() {
this.formData = {
fields: deepClone(this.drawingList),
...this.formConf
}
},
generate(data) {
const func = this[`exec${titleCase(this.operationType)}`]
this.generateConf = data
func && func(data)
},
execRun(data) {
this.AssembleFormData()
this.drawerVisible = true
},
execDownload(data) {
const codeStr = this.generateCode()
const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
saveAs(blob, data.fileName)
},
execCopy(data) {
document.getElementById('copyNode').click()
},
empty() {
this.$confirm('确定要清空所有组件吗?', '提示', { type: 'warning' }).then(
() => {
this.drawingList = []
this.idGlobal = 100
}).catch(() => {});
},
drawingItemCopy(item, list) {
let clone = deepClone(item)
clone = this.createIdAndKey(clone)
list.push(clone)
this.activeFormItem(clone)
},
drawingItemDelete(index, list) {
list.splice(index, 1)
this.$nextTick(() => {
const len = this.drawingList.length
if (len) {
this.activeFormItem(this.drawingList[len - 1])
}
})
},
generateCode() {
const { type } = this.generateConf
this.AssembleFormData()
const script = vueScript(makeUpJs(this.formData, type))
const html = vueTemplate(makeUpHtml(this.formData, type))
const css = cssStyle(makeUpCss(this.formData))
return beautifier.html(html + script + css, beautifierConf.html)
},
showJson() {
this.AssembleFormData()
this.jsonDrawerVisible = true
},
download() {
this.dialogVisible = true
this.showFileName = true
this.operationType = 'download'
},
run() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'run'
},
copy() {
this.dialogVisible = true
this.showFileName = false
this.operationType = 'copy'
},
tagChange(newTag) {
newTag = this.cloneComponent(newTag)
const config = newTag.__config__
newTag.__vModel__ = this.activeData.__vModel__
config.formId = this.activeId
config.span = this.activeData.__config__.span
this.activeData.__config__.tag = config.tag
this.activeData.__config__.tagIcon = config.tagIcon
this.activeData.__config__.document = config.document
if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
config.defaultValue = this.activeData.__config__.defaultValue
}
Object.keys(newTag).forEach(key => {
if (this.activeData[key] !== undefined) {
newTag[key] = this.activeData[key]
}
})
this.activeData = newTag
this.updateDrawingList(newTag, this.drawingList)
},
updateDrawingList(newTag, list) {
const index = list.findIndex(item => item.__config__.formId === this.activeId)
if (index > -1) {
list.splice(index, 1, newTag)
} else {
list.forEach(item => {
if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
})
}
},
refreshJson(data) {
this.drawingList = deepClone(data.fields)
delete data.fields
this.formConf = data
}
}
}
</script>
<style lang='scss'>
@import '@/styles/home';
</style>

View File

@@ -0,0 +1,19 @@
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
// add by 芋道源码:引用自 https://github.com/JakHuang/form-generator/tree/dev/src/views/index
new Vue({
router,
render: h => h(App)
}).$mount('#app')

View File

@@ -0,0 +1,61 @@
<template>
<el-form ref="basicInfoForm" :model="info" :rules="rules" label-width="150px">
<el-row>
<el-col :span="12">
<el-form-item label="表名称" prop="tableName">
<el-input placeholder="请输入仓库名称" v-model="info.tableName" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="表描述" prop="tableComment">
<el-input placeholder="请输入" v-model="info.tableComment" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="实体类名称" prop="className">
<el-input placeholder="请输入" v-model="info.className" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="作者" prop="author">
<el-input placeholder="请输入" v-model="info.author" />
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :rows="3" v-model="info.remark"></el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
export default {
name: "BasicInfoForm",
props: {
info: {
type: Object,
default: null
}
},
data() {
return {
rules: {
tableName: [
{ required: true, message: "请输入表名称", trigger: "blur" }
],
tableComment: [
{ required: true, message: "请输入表描述", trigger: "blur" }
],
className: [
{ required: true, message: "请输入实体类名称", trigger: "blur" }
],
author: [
{ required: true, message: "请输入作者", trigger: "blur" }
]
}
};
}
};
</script>

View File

@@ -0,0 +1,235 @@
<template>
<el-card>
<el-tabs v-model="activeName">
<el-tab-pane label="基本信息" name="basic">
<basic-info-form ref="basicInfo" :info="table" />
</el-tab-pane>
<el-tab-pane label="字段信息" name="cloum">
<el-table ref="dragTable" :data="columns" row-key="columnId" :max-height="tableHeight">
<el-table-column
label="字段列名"
prop="columnName"
min-width="10%"
:show-overflow-tooltip="true"
/>
<el-table-column label="字段描述" min-width="10%">
<template slot-scope="scope">
<el-input v-model="scope.row.columnComment"></el-input>
</template>
</el-table-column>
<el-table-column
label="物理类型"
prop="columnType"
min-width="10%"
:show-overflow-tooltip="true"
/>
<el-table-column label="Java类型" min-width="11%">
<template slot-scope="scope">
<el-select v-model="scope.row.javaType">
<el-option label="Long" value="Long" />
<el-option label="String" value="String" />
<el-option label="Integer" value="Integer" />
<el-option label="Double" value="Double" />
<el-option label="BigDecimal" value="BigDecimal" />
<el-option label="Date" value="Date" />
<el-option label="Boolean" value="Boolean" />
</el-select>
</template>
</el-table-column>
<el-table-column label="java属性" min-width="10%">
<template slot-scope="scope">
<el-input v-model="scope.row.javaField"></el-input>
</template>
</el-table-column>
<el-table-column label="插入" min-width="4%">
<template slot-scope="scope">
<el-checkbox true-label="true" false-label="false" v-model="scope.row.createOperation"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="编辑" min-width="4%">
<template slot-scope="scope">
<el-checkbox true-label="true" false-label="false" v-model="scope.row.updateOperation"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="列表" min-width="4%">
<template slot-scope="scope">
<el-checkbox true-label="true" false-label="false" v-model="scope.row.listOperationResult"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="查询" min-width="4%">
<template slot-scope="scope">
<el-checkbox true-label="true" false-label="false" v-model="scope.row.listOperation"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="查询方式" min-width="10%">
<template slot-scope="scope">
<el-select v-model="scope.row.listOperationCondition">
<el-option label="=" value="=" />
<el-option label="!=" value="!=" />
<el-option label=">" value=">" />
<el-option label=">=" value=">=" />
<el-option label="<" value="<>" />
<el-option label="<=" value="<=" />
<el-option label="LIKE" value="LIKE" />
<el-option label="BETWEEN" value="BETWEEN" />
</el-select>
</template>
</el-table-column>
<el-table-column label="允许空" min-width="5%">
<template slot-scope="scope">
<el-checkbox true-label="true" false-label="false" v-model="scope.row.nullable"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="显示类型" min-width="12%">
<template slot-scope="scope">
<el-select v-model="scope.row.htmlType">
<el-option label="文本框" value="input" />
<el-option label="文本域" value="textarea" />
<el-option label="下拉框" value="select" />
<el-option label="单选框" value="radio" />
<el-option label="复选框" value="checkbox" />
<el-option label="日期控件" value="datetime" />
<el-option label="图片上传" value="imageUpload" />
<el-option label="文件上传" value="fileUpload" />
<el-option label="富文本控件" value="editor" />
</el-select>
</template>
</el-table-column>
<el-table-column label="字典类型" min-width="12%">
<template slot-scope="scope">
<el-select v-model="scope.row.dictType" clearable filterable placeholder="请选择">
<el-option
v-for="dict in dictOptions"
:key="dict.id"
:label="dict.name"
:value="dict.type"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="示例" min-width="10%">
<template slot-scope="scope">
<el-input v-model="scope.row.example"></el-input>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="生成信息" name="genInfo">
<gen-info-form ref="genInfo" :info="table" :tables="tables" :menus="menus"/>
</el-tab-pane>
</el-tabs>
<el-form label-width="100px">
<el-form-item style="text-align: center;margin-left:-100px;margin-top:10px;">
<el-button type="primary" @click="submitForm()">提交</el-button>
<el-button @click="close()">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
import { getCodegenDetail, updateCodegen } from "@/api/infra/codegen";
import { listAllSimple as listAllSimpleDictType } from "@/api/system/dict/type";
import { listSimpleMenus } from "@/api/system/menu";
import basicInfoForm from "./basicInfoForm";
import genInfoForm from "./genInfoForm";
import Sortable from 'sortablejs'
export default {
name: "GenEdit",
components: {
basicInfoForm,
genInfoForm
},
data() {
return {
// 选中选项卡的 name
activeName: "cloum",
// 表格的高度
tableHeight: document.documentElement.scrollHeight - 245 + "px",
// 表信息
tables: [],
// 表列信息
columns: [],
// 字典信息
dictOptions: [],
// 菜单信息
menus: [],
// 表详细信息
table: {}
};
},
created() {
const tableId = this.$route.params && this.$route.params.tableId;
if (tableId) {
// 获取表详细信息
getCodegenDetail(tableId).then(res => {
this.table = res.data.table;
this.columns = res.data.columns;
});
/** 查询字典下拉列表 */
listAllSimpleDictType().then(response => {
this.dictOptions = response.data;
});
/** 查询菜单下拉列表 */
listSimpleMenus().then(response => {
this.menus = [];
this.menus.push(...this.handleTree(response.data, "id"));
});
}
},
methods: {
/** 提交按钮 */
submitForm() {
const basicForm = this.$refs.basicInfo.$refs.basicInfoForm;
const genForm = this.$refs.genInfo.$refs.genInfoForm;
Promise.all([basicForm, genForm].map(this.getFormPromise)).then(res => {
const validateResult = res.every(item => !!item);
if (validateResult) {
const genTable = {};
genTable.table = Object.assign({}, basicForm.model, genForm.model);
genTable.columns = this.columns;
genTable.params = {
treeCode: genTable.treeCode,
treeName: genTable.treeName,
treeParentCode: genTable.treeParentCode,
parentMenuId: genTable.parentMenuId
};
updateCodegen(genTable).then(res => {
this.$modal.msgSuccess("修改成功!");
this.close();
});
} else {
this.$modal.msgError("表单校验未通过,请重新检查提交内容");
}
});
},
getFormPromise(form) {
return new Promise(resolve => {
form.validate(res => {
resolve(res);
});
});
},
/** 关闭按钮 */
close() {
this.$tab.closeOpenPage({
path: "/infra/codegen",
query: { t: Date.now(), pageNum: this.$route.query.pageNum } }
);
}
},
mounted() {
const el = this.$refs.dragTable.$el.querySelectorAll(".el-table__body-wrapper > table > tbody")[0];
const sortable = Sortable.create(el, {
handle: ".allowDrag",
onEnd: evt => {
const targetRow = this.columns.splice(evt.oldIndex, 1)[0];
this.columns.splice(evt.newIndex, 0, targetRow);
for (let index in this.columns) {
this.columns[index].sort = parseInt(index) + 1;
}
}
});
}
};
</script>

View File

@@ -0,0 +1,327 @@
<template>
<el-form ref="genInfoForm" :model="info" :rules="rules" label-width="150px">
<el-row>
<el-col :span="12">
<el-form-item prop="templateType">
<span slot="label">生成模板</span>
<el-select v-model="info.templateType" @change="tplSelectChange">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE)"
:key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="scene">
<span slot="label">生成场景</span>
<el-select v-model="info.scene">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_CODEGEN_SCENE)"
:key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item prop="packageName">-->
<!-- <span slot="label">-->
<!-- 生成包路径-->
<!-- <el-tooltip content="生成在哪个java包下例如 com.ruoyi.system" placement="top">-->
<!-- <i class="el-icon-question"></i>-->
<!-- </el-tooltip>-->
<!-- </span>-->
<!-- <el-input v-model="info.packageName" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<el-form-item prop="moduleName">
<span slot="label">
模块名
<el-tooltip content="模块名,即一级目录,例如 system、infra、tool 等等" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="info.moduleName" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="businessName">
<span slot="label">
业务名
<el-tooltip content="业务名,即二级目录,例如 user、permission、dict 等等" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="info.businessName" />
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item prop="businessPackage">-->
<!-- <span slot="label">-->
<!-- 业务包-->
<!-- <el-tooltip content="业务包,自定义二级目录。例如说,我们希望将 dictType 和 dictData 归类成 dict 业务" placement="top">-->
<!-- <i class="el-icon-question"></i>-->
<!-- </el-tooltip>-->
<!-- </span>-->
<!-- <el-input v-model="info.businessPackage" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<el-form-item prop="className">
<span slot="label">
类名称
<el-tooltip content="类名称首字母大写例如SysUser、SysMenu、SysDictData 等等" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="info.className" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="classComment">
<span slot="label">
类描述
<el-tooltip content="用作类描述,例如 用户" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="info.classComment" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<span slot="label">
上级菜单
<el-tooltip content="分配到指定菜单下,例如 系统管理" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<treeselect :append-to-body="true" v-model="info.parentMenuId" :options="menus"
:normalizer="normalizer" :show-count="true" placeholder="请选择系统菜单" />
</el-form-item>
</el-col>
<el-col :span="24" v-if="info.genType == '1'">
<el-form-item prop="genPath">
<span slot="label">
自定义路径
<el-tooltip content="填写磁盘绝对路径若不填写则生成到当前Web项目下" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-input v-model="info.genPath">
<el-dropdown slot="append">
<el-button type="primary">
最近路径快速选择
<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="info.genPath = '/'">恢复默认的生成基础路径</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row v-show="info.tplCategory == 'tree'">
<h4 class="form-header">其他信息</h4>
<el-col :span="12">
<el-form-item>
<span slot="label">
树编码字段
<el-tooltip content="树显示的编码字段名, 如dept_id" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-select v-model="info.treeCode" placeholder="请选择">
<el-option
v-for="(column, index) in info.columns"
:key="index"
:label="column.columnName + '' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<span slot="label">
树父编码字段
<el-tooltip content="树显示的父编码字段名, 如parent_Id" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-select v-model="info.treeParentCode" placeholder="请选择">
<el-option
v-for="(column, index) in info.columns"
:key="index"
:label="column.columnName + '' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<span slot="label">
树名称字段
<el-tooltip content="树节点的显示名称字段名, 如dept_name" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-select v-model="info.treeName" placeholder="请选择">
<el-option
v-for="(column, index) in info.columns"
:key="index"
:label="column.columnName + '' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-show="info.tplCategory == 'sub'">
<h4 class="form-header">关联信息</h4>
<el-col :span="12">
<el-form-item>
<span slot="label">
关联子表的表名
<el-tooltip content="关联子表的表名, 如sys_user" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-select v-model="info.subTableName" placeholder="请选择" @change="subSelectChange">
<el-option
v-for="(table, index) in tables"
:key="index"
:label="table.tableName + '' + table.tableComment"
:value="table.tableName"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item>
<span slot="label">
子表关联的外键名
<el-tooltip content="子表关联的外键名, 如user_id" placement="top">
<i class="el-icon-question"></i>
</el-tooltip>
</span>
<el-select v-model="info.subTableFkName" placeholder="请选择">
<el-option
v-for="(column, index) in subColumns"
:key="index"
:label="column.columnName + '' + column.columnComment"
:value="column.columnName"
></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script>
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "BasicInfoForm",
components: { Treeselect },
props: {
info: {
type: Object,
default: null
},
tables: {
type: Array,
default: null
},
menus: {
type: Array,
default: []
},
},
data() {
return {
subColumns: [],
rules: {
templateType: [
{ required: true, message: "请选择生成模板", trigger: "blur" }
],
scene: [
{ required: true, message: "请选择生成场景", trigger: "blur" }
],
// packageName: [
// { required: true, message: "请输入生成包路径", trigger: "blur" }
// ],
moduleName: [
{ required: true, message: "请输入生成模块名", trigger: "blur" }
],
businessName: [
{ required: true, message: "请输入生成业务名", trigger: "blur" }
],
businessPackage: [
{ required: true, message: "请输入生成业务包", trigger: "blur" }
],
className: [
{ required: true, message: "请输入生成类名称", trigger: "blur" }
],
classComment: [
{ required: true, message: "请输入生成类描述", trigger: "blur" }
],
}
};
},
created() {},
watch: {
'info.subTableName': function(val) {
this.setSubTableColumns(val);
}
},
methods: {
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.id,
label: node.name,
children: node.children
};
},
/** 选择子表名触发 */
subSelectChange(value) {
this.info.subTableFkName = '';
},
/** 选择生成模板触发 */
tplSelectChange(value) {
if (value !== 1) {
// TODO 芋艿:暂时不考虑支持树形结构
this.$modal.msgError('暂时不考虑支持【树形】和【主子表】的代码生成。原因是:导致 vm 模板过于复杂,不利于胖友二次开发');
return false;
}
if(value !== 'sub') {
this.info.subTableName = '';
this.info.subTableFkName = '';
}
},
/** 设置关联外键 */
setSubTableColumns(value) {
for (var item in this.tables) {
const name = this.tables[item].tableName;
if (value === name) {
this.subColumns = this.tables[item].columns;
break;
}
}
}
}
};
</script>

View File

@@ -0,0 +1,106 @@
<template>
<!-- 导入表 -->
<el-dialog title="导入表" :visible.sync="visible" width="800px" top="5vh" append-to-body>
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="表名称" prop="tableName">
<el-input
v-model="queryParams.tableName"
placeholder="请输入表名称"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input
v-model="queryParams.tableComment"
placeholder="请输入表描述"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row>
<el-table @row-click="clickRow" ref="table" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="tableSchema" label="数据库" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableName" label="表名称" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="tableComment" label="表描述" :show-overflow-tooltip="true"></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
</el-table>
</el-row>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleImportTable"> </el-button>
<el-button @click="visible = false"> </el-button>
</div>
</el-dialog>
</template>
<script>
import { getSchemaTableList, createCodegenListFromDB } from "@/api/infra/codegen";
export default {
data() {
return {
// 遮罩层
visible: false,
// 选中数组值
tables: [],
// 总条数
total: 0,
// 表数据
dbTableList: [],
// 查询参数
queryParams: {
tableName: undefined,
tableComment: undefined
}
};
},
methods: {
// 显示弹框
show() {
this.getList();
this.visible = true;
},
clickRow(row) {
this.$refs.table.toggleRowSelection(row);
},
// 多选框选中数据
handleSelectionChange(selection) {
this.tables = selection.map(item => item.tableName);
},
// 查询表数据
getList() {
getSchemaTableList(this.queryParams).then(res => {
this.dbTableList = res.data;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 导入按钮操作 */
handleImportTable() {
createCodegenListFromDB(this.tables.join(",")).then(res => {
this.$modal.msgSuccess("导入成功");
this.visible = false;
this.$emit("ok");
});
}
}
};
</script>

View File

@@ -0,0 +1,376 @@
<template>
<div class="app-container">
<!-- 操作工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="表名称" prop="tableName">
<el-input v-model="queryParams.tableName" placeholder="请输入表名称" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="表描述" prop="tableComment">
<el-input v-model="queryParams.tableComment" placeholder="请输入表描述" clearable size="small"
@keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRange" size="small" style="width: 240px" value-format="yyyy-MM-dd" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工作栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload" size="mini" @click="openImportTable"
v-hasPermi="['infra:codegen:create']">基于 DB 导入</el-button>
<el-button type="info" plain icon="el-icon-upload" size="mini" @click="openImportSQL"
v-hasPermi="['infra:codegen:create']">基于 SQL 导入</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="tableList">
<el-table-column label="表名称" align="center" prop="tableName" :show-overflow-tooltip="true" width="200"/>
<el-table-column label="表描述" align="center" prop="tableComment" :show-overflow-tooltip="true" width="120"/>
<el-table-column label="实体" align="center" prop="className" :show-overflow-tooltip="true" width="200"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="更新时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.updateTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" size="small" icon="el-icon-view" @click="handlePreview(scope.row)" v-hasPermi="['infra:codegen:preview']">预览</el-button>
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEditTable(scope.row)" v-hasPermi="['infra:codegen:update']">编辑</el-button>
<el-button type="text" size="small" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['infra:codegen:delete']">删除</el-button>
<el-button type="text" size="small" icon="el-icon-refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['infra:codegen:update']">同步</el-button>
<el-button type="text" size="small" icon="el-icon-download" @click="handleGenTable(scope.row)" v-hasPermi="['infra:codegen:download']">生成代码</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize" @pagination="getList"/>
<!-- 预览界面 -->
<el-dialog :title="preview.title" :visible.sync="preview.open" width="90%" top="5vh" append-to-body class="scrollbar">
<el-row>
<el-col :span="7">
<el-tree :data="preview.fileTree" :expand-on-click-node="false" default-expand-all highlight-current
@node-click="handleNodeClick"/>
</el-col>
<el-col :span="17">
<el-tabs v-model="preview.activeName">
<el-tab-pane v-for="item in preview.data" :label="item.filePath.substring(item.filePath.lastIndexOf('/') + 1)"
:name="item.filePath" :key="item.filePath">
<el-link :underline="false" icon="el-icon-document-copy" v-clipboard:copy="item.code" v-clipboard:success="clipboardSuccess" style="float:right">复制</el-link>
<pre><code class="hljs" v-html="highlightedCode(item)"></code></pre>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
</el-dialog>
<!-- 基于 DB 导入 -->
<import-table ref="import" @ok="handleQuery" />
<!-- 基于 SQL 导入 -->
<el-dialog :title="importSQL.title" :visible.sync="importSQL.open" width="800px" append-to-body>
<el-form ref="importSQLForm" :model="importSQL.form" :rules="importSQL.rules" label-width="120px">
<el-row>
<el-col :span="12">
<el-form-item label="建表 SQL 语句" prop="sql">
<el-input v-model="importSQL.form.sql" type="textarea" rows="30" style="width: 650px;" placeholder="请输入建 SQL 语句" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitImportSQLForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getCodegenTablePage, previewCodegen, downloadCodegen, deleteCodegen,
syncCodegenFromDB, syncCodegenFromSQL, createCodegenListFromSQL } from "@/api/infra/codegen";
import importTable from "./importTable";
// 代码高亮插件
import hljs from "highlight.js/lib/highlight";
import "highlight.js/styles/github-gist.css";
hljs.registerLanguage("java", require("highlight.js/lib/languages/java"));
hljs.registerLanguage("xml", require("highlight.js/lib/languages/xml"));
hljs.registerLanguage("html", require("highlight.js/lib/languages/xml"));
hljs.registerLanguage("vue", require("highlight.js/lib/languages/xml"));
hljs.registerLanguage("javascript", require("highlight.js/lib/languages/javascript"));
hljs.registerLanguage("sql", require("highlight.js/lib/languages/sql"));
export default {
name: "Codegen",
components: { importTable },
data() {
return {
// 遮罩层
loading: true,
// 唯一标识符
uniqueId: "",
// 选中表数组
tableNames: [],
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 表数据
tableList: [],
// 日期范围
dateRange: "",
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
tableName: undefined,
tableComment: undefined
},
// 预览参数
preview: {
open: false,
title: "代码预览",
fileTree: [],
data: {},
activeName: "",
},
// 基于 SQL 导入
importSQL: {
open: false,
title: "",
form: {
},
rules: {
sql: [{ required: true, message: "SQL 不能为空", trigger: "blur" }]
}
}
};
},
created() {
this.getList();
},
activated() {
const time = this.$route.query.t;
if (time != null && time !== this.uniqueId) {
this.uniqueId = time;
this.resetQuery();
}
},
methods: {
/** 查询表集合 */
getList() {
this.loading = true;
getCodegenTablePage(this.addDateRange(this.queryParams, [
this.dateRange[0] ? this.dateRange[0] + ' 00:00:00' : undefined,
this.dateRange[1] ? this.dateRange[1] + ' 23:59:59' : undefined,
], 'CreateTime')).then(response => {
this.tableList = response.data.list;
this.total = response.data.total;
this.loading = false;
}
);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 生成代码操作 */
handleGenTable(row) {
downloadCodegen(row.id).then(response => {
this.$download.zip(response, 'codegen-' + row.tableName + '.zip');
})
},
/** 同步数据库操作 */
handleSynchDb(row) {
// 基于 SQL 同步
if (row.importType === 2) {
this.importSQL.open = true;
this.importSQL.form.tableId = row.id;
return;
}
// 基于 DB 同步
const tableName = row.tableName;
this.$modal.confirm('确认要强制同步"' + tableName + '"表结构吗?').then(function() {
return syncCodegenFromDB(row.id);
}).then(() => {
this.$modal.msgSuccess("同步成功");
}).catch(() => {});
},
/** 打开导入表弹窗 */
openImportTable() {
this.$refs.import.show();
},
/** 打开 SQL 导入的弹窗 **/
openImportSQL() {
this.importSQL.open = true;
},
/** 重置按钮操作 */
resetQuery() {
this.dateRange = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 预览按钮 */
handlePreview(row) {
previewCodegen(row.id).then(response => {
this.preview.data = response.data;
let files = this.handleFiles(response.data);
this.preview.fileTree = this.handleTree(files, "id", "parentId", "children",
"/"); // "/" 为根节点
// console.log(this.preview.fileTree)
this.preview.activeName = response.data[0].filePath;
this.preview.open = true;
});
},
/** 高亮显示 */
highlightedCode(item) {
// const vmName = key.substring(key.lastIndexOf("/") + 1, key.indexOf(".vm"));
// var language = vmName.substring(vmName.indexOf(".") + 1, vmName.length);
var language = item.filePath.substring(item.filePath.lastIndexOf(".") + 1);
const result = hljs.highlight(language, item.code || "", true);
return result.value || '&nbsp;';
},
/** 复制代码成功 */
clipboardSuccess(){
this.$modal.msgSuccess("复制成功");
},
/** 生成 files 目录 **/
handleFiles(datas) {
let exists = {}; // keyfile 的 idvaluetrue
let files = [];
// 遍历每个元素
for (const data of datas) {
let paths = data.filePath.split('/');
let fullPath = ''; // 从头开始的路径,用于生成 id
// 特殊处理 java 文件
if (paths[paths.length - 1].indexOf('.java') >= 0) {
let newPaths = [];
for (let i = 0; i < paths.length; i++) {
let path = paths[i];
if (path !== 'java') {
newPaths.push(path);
continue;
}
newPaths.push(path);
// 特殊处理中间的 package进行合并
let tmp = undefined;
while (i < paths.length) {
path = paths[i + 1];
if (path === 'controller'
|| path === 'convert'
|| path === 'dal'
|| path === 'enums'
|| path === 'service'
|| path === 'vo' // 下面三个,主要是兜底。可能考虑到有人改了包结构
|| path === 'mysql'
|| path === 'dataobject') {
break;
}
tmp = tmp ? tmp + '.' + path : path;
i++;
}
if (tmp) {
newPaths.push(tmp);
}
}
paths = newPaths;
}
// 遍历每个 path 拼接成树
for (let i = 0; i < paths.length; i++) {
// 已经添加到 files 中,则跳过
let oldFullPath = fullPath;
// 下面的 replaceAll 的原因,是因为上面包处理了,导致和 tabs 不匹配,所以 replaceAll 下
fullPath = fullPath.length === 0 ? paths[i] : fullPath.replaceAll('.', '/') + '/' + paths[i];
if (exists[fullPath]) {
continue;
}
// 添加到 files 中
exists[fullPath] = true;
files.push({
id: fullPath,
label: paths[i],
parentId: oldFullPath || '/' // "/" 为根节点
});
}
}
return files;
},
/** 节点单击事件 **/
handleNodeClick(data, node) {
if (node && !node.isLeaf) {
return false;
}
// 判断,如果非子节点,不允许选中
this.preview.activeName = data.id;
},
/** 修改按钮操作 */
handleEditTable(row) {
const tableId = row.id;
this.$router.push("/codegen/edit/" + tableId);
},
/** 删除按钮操作 */
handleDelete(row) {
const tableIds = row.id;
this.$modal.confirm('是否确认删除表名称为"' + row.tableName + '"的数据项?').then(function() {
return deleteCodegen(tableIds);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
// 取消按钮
cancel() {
this.importSQL.open = false;
this.reset();
},
// 表单重置
reset() {
this.importSQL.form = {
tableId: undefined,
sql: undefined,
};
this.resetForm("importSQLForm");
},
// 提交 import SQL 表单
submitImportSQLForm() {
this.$refs["importSQLForm"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
let form = this.importSQL.form;
if (form.tableId != null) {
syncCodegenFromSQL(form.tableId, form.sql).then(response => {
this.$modal.msgSuccess("同步成功");
this.importSQL.open = false;
this.getList();
});
return;
}
// 添加的提交
createCodegenListFromSQL(form).then(response => {
this.$modal.msgSuccess("导入成功");
this.importSQL.open = false;
this.getList();
});
});
}
}
};
</script>

View File

@@ -0,0 +1,68 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportHtml">导出 HTML</el-button>
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportWord">导出 Word</el-button>
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleExportMarkdown">导出 Markdown</el-button>
</el-col>
</el-row>
<!-- 展示文档 -->
<div v-loading="loading" :style="'height:'+ height">
<i-frame :src="src" />
</div>
</div>
</template>
<script>
import { exportHtml, exportWord, exportMarkdown} from "@/api/infra/dbDoc";
import iFrame from "@/components/iFrame/index";
export default {
name: "DBDoc",
components: { iFrame },
data() {
return {
height: document.documentElement.clientHeight - 94.5 + "px;",
loading: true,
src: undefined,
};
},
mounted: function() {
setTimeout(() => {
this.loading = false;
}, 230);
const that = this;
window.onresize = function temp() {
that.height = document.documentElement.clientHeight - 94.5 + "px;";
};
},
created() {
// 加载 Html进行预览
exportHtml().then(response => {
let blob = new Blob([response], {type : 'text/html'});
this.src = window.URL.createObjectURL(blob);
})
},
methods: {
/** 处理导出 HTML */
handleExportHtml() {
exportHtml().then(response => {
this.$download.html(response, '数据库文档.html');
})
},
/** 处理导出 Word */
handleExportWord() {
exportWord().then(response => {
this.$download.word(response, '数据库文档.doc');
})
},
/** 处理导出 Markdown */
handleExportMarkdown() {
exportMarkdown().then(response => {
this.$download.markdown(response, '数据库文档.md');
})
}
}
};
</script>

View File

@@ -0,0 +1,15 @@
<template>
<i-frame :src="url" />
</template>
<script>
import iFrame from "@/components/iFrame/index";
export default {
name: "Druid",
components: { iFrame },
data() {
return {
url: process.env.VUE_APP_BASE_API + "/doc.html"
};
},
};
</script>

View File

@@ -0,0 +1,262 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名字" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名字" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择类型" clearable size="small">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="分类" prop="category">
<el-input v-model="queryParams.category" placeholder="请输入分类" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['infra:test-demo:create']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" :loading="exportLoading"
v-hasPermi="['infra:test-demo:export']">导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名字" align="center" prop="name" />
<el-table-column label="状态" align="center" prop="status" />
<el-table-column label="类型" align="center" prop="type" />
<el-table-column label="分类" align="center" prop="category" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['infra:test-demo:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['infra:test-demo:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="名字" prop="name">
<el-input v-model="form.name" placeholder="请输入名字" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择类型">
<el-option label="请选择字典生成" value="" />
</el-select>
</el-form-item>
<el-form-item label="分类" prop="category">
<el-input v-model="form.category" placeholder="请输入分类" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createTestDemo, updateTestDemo, deleteTestDemo, getTestDemo, getTestDemoPage, exportTestDemoExcel } from "@/api/infra/testDemo";
export default {
name: "TestDemo",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 字典类型列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
type: null,
category: null,
remark: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "名字不能为空", trigger: "blur" }],
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
type: [{ required: true, message: "类型不能为空", trigger: "change" }],
category: [{ required: true, message: "分类不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行查询
getTestDemoPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
type: undefined,
category: undefined,
remark: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加字典类型";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getTestDemo(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改字典类型";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateTestDemo(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createTestDemo(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除字典类型编号为"' + id + '"的数据项?').then(function() {
return deleteTestDemo(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
// 处理查询参数
let params = {...this.queryParams};
params.pageNo = undefined;
params.pageSize = undefined;
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
// 执行导出
this.$modal.confirm('是否确认导出所有字典类型数据项?').then(() => {
this.exportLoading = true;
return exportTestDemoExcel(params);
}).then(response => {
this.$download.excel(response, '字典类型.xls');
this.exportLoading = false;
}).catch(() => {});
}
}
};
</script>