新增模板

des
吕天方 6 months ago
parent ecf6debfe6
commit c77fe1cd93

@ -5,7 +5,7 @@ VUE_APP_TITLE = 金鸡湖现代服务业品牌管理系统
ENV = 'development'
# 金鸡湖现代服务业品牌管理系统/开发环境
VUE_APP_BASE_API = 'http://192.168.0.110:9040'
VUE_APP_BASE_API = 'http://192.168.0.108:9040'
# VUE_APP_BASE_API = 'http://39.101.188.84:9040'
# VUE_APP_BASE_API = 'https://idp.sipac.gov.cn/api'

@ -1,6 +1,6 @@
{
"name": "ruoyi",
"version": "1.0.202407231321",
"version": "1.0.202407240905",
"description": "金鸡湖现代服务业品牌管理系统",
"author": "若依",
"license": "MIT",

@ -1,5 +1,14 @@
import request from '@/utils/request'
// 新增模板管理列表
export function templateInfo(data) {
return request({
url: '/system/templateInfo',
method: 'post',
data
})
}
// 查询在线申报管理列表
export function listInfo(query) {
return request({

@ -136,6 +136,12 @@ export const constantRoutes = [
name: 'enterpriselibraryInfo',
meta: { title: '企业详情', icon: 'user' }
},
{
path: 'onlineDeclarebBuild',
component: () => import('@/views/tool/build/index'),
name: 'onlineDeclarebBuild',
meta: { title: '表单构建', icon: 'user' }
},
]
}
];

@ -285,6 +285,43 @@ export function deepClone(source) {
return targetObj
}
// 深拷贝对象
export function deepCloneTwo(obj) {
const _toString = Object.prototype.toString
// null, undefined, non-object, function
if (!obj || typeof obj !== 'object') {
return obj
}
// DOM Node
if (obj.nodeType && 'cloneNode' in obj) {
return obj.cloneNode(true)
}
// Date
if (_toString.call(obj) === '[object Date]') {
return new Date(obj.getTime())
}
// RegExp
if (_toString.call(obj) === '[object RegExp]') {
const flags = []
if (obj.global) { flags.push('g') }
if (obj.multiline) { flags.push('m') }
if (obj.ignoreCase) { flags.push('i') }
return new RegExp(obj.source, flags.join(''))
}
const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}
for (const key in obj) {
result[key] = deepCloneTwo(obj[key])
}
return result
}
/**
* @param {Array} arr
* @returns {Array}

@ -0,0 +1,186 @@
<script>
import { deepClone } from '@/utils/index'
import render from './render/render.js'
const ruleTrigger = {
'el-input': 'blur',
'el-input-number': 'blur',
'el-select': 'change',
'el-radio-group': 'change',
'el-checkbox-group': 'change',
'el-cascader': 'change',
'el-time-picker': 'change',
'el-date-picker': 'change',
'el-rate': 'change'
}
const layouts = {
colFormItem(h, scheme) {
const config = scheme.__config__
const listeners = buildListeners.call(this, scheme)
let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
if (config.showLabel === false) labelWidth = '0'
return (
<el-col span={config.span}>
<el-form-item label-width={labelWidth} prop={scheme.__vModel__}
label={config.showLabel ? config.label : ''}>
<render conf={scheme} on={listeners} />
</el-form-item>
</el-col>
)
},
rowFormItem(h, scheme) {
let child = renderChildren.apply(this, arguments)
if (scheme.type === 'flex') {
child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}>
{child}
</el-row>
}
return (
<el-col span={scheme.span}>
<el-row gutter={scheme.gutter}>
{child}
</el-row>
</el-col>
)
}
}
function renderFrom(h) {
const { formConfCopy } = this
return (
<el-row gutter={formConfCopy.gutter}>
<el-form
size={formConfCopy.size}
label-position={formConfCopy.labelPosition}
disabled={formConfCopy.disabled}
label-width={`${formConfCopy.labelWidth}px`}
ref={formConfCopy.formRef}
// model https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
props={{ model: this[formConfCopy.formModel] }}
rules={this[formConfCopy.formRules]}
>
{renderFormItem.call(this, h, formConfCopy.fields)}
{formConfCopy.formBtns && formBtns.call(this, h)}
</el-form>
</el-row>
)
}
function formBtns(h) {
return <el-col>
<el-form-item size="large">
<el-button type="primary" onClick={this.submitForm}>提交</el-button>
<el-button onClick={this.resetForm}>重置</el-button>
</el-form-item>
</el-col>
}
function renderFormItem(h, elementList) {
return elementList.map(scheme => {
const config = scheme.__config__
const layout = layouts[config.layout]
if (layout) {
return layout.call(this, h, scheme)
}
throw new Error(`没有与${config.layout}匹配的layout`)
})
}
function renderChildren(h, scheme) {
const config = scheme.__config__
if (!Array.isArray(config.children)) return null
return renderFormItem.call(this, h, config.children)
}
function setValue(event, config, scheme) {
this.$set(config, 'defaultValue', event)
this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
}
function buildListeners(scheme) {
const config = scheme.__config__
const methods = this.formConf.__methods__ || {}
const listeners = {}
// __methods__thisevent
Object.keys(methods).forEach(key => {
listeners[key] = event => methods[key].call(this, event)
})
// render.js vModel $emit('input', val)
listeners.input = event => setValue.call(this, event, config, scheme)
return listeners
}
export default {
components: {
render
},
props: {
formConf: {
type: Object,
required: true
}
},
data() {
const data = {
formConfCopy: deepClone(this.formConf),
[this.formConf.formModel]: {},
[this.formConf.formRules]: {}
}
this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
return data
},
methods: {
initFormData(componentList, formData) {
componentList.forEach(cur => {
const config = cur.__config__
if (cur.__vModel__) formData[cur.__vModel__] = config.defaultValue
if (config.children) this.initFormData(config.children, formData)
})
},
buildRules(componentList, rules) {
componentList.forEach(cur => {
const config = cur.__config__
if (Array.isArray(config.regList)) {
if (config.required) {
const required = { required: config.required, message: cur.placeholder }
if (Array.isArray(config.defaultValue)) {
required.type = 'array'
required.message = `请至少选择一个${config.label}`
}
required.message === undefined && (required.message = `${config.label}不能为空`)
config.regList.push(required)
}
rules[cur.__vModel__] = config.regList.map(item => {
item.pattern && (item.pattern = eval(item.pattern))
item.trigger = ruleTrigger && ruleTrigger[config.tag]
return item
})
}
if (config.children) this.buildRules(config.children, rules)
})
},
resetForm() {
this.formConfCopy = deepClone(this.formConf)
this.$refs[this.formConf.formRef].resetFields()
},
submitForm() {
this.$refs[this.formConf.formRef].validate(valid => {
if (!valid) return false
// sumit
this.$emit('submit', this[this.formConf.formModel])
return true
})
}
},
render(h) {
return renderFrom.call(this, h)
}
}
</script>

@ -0,0 +1,19 @@
{
"name": "form-gen-render",
"version": "1.0.4",
"description": "表单核心render",
"main": "lib/form-gen-render.umd.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/JakHuang/form-generator.git"
},
"author": "jakhuang",
"license": "MIT",
"bugs": {
"url": "https://github.com/JakHuang/form-generator/issues"
},
"homepage": "https://github.com/JakHuang/form-generator#readme"
}

@ -0,0 +1,122 @@
import { deepClone } from '@/utils/index'
const componentChild = {}
/**
* ./slots中的文件挂载到对象componentChild上
* 文件名为key对应JSON配置中的__config__.tag
* 文件内容为value解析JSON配置中的__slot__
*/
const slotsFiles = require.context('./slots', false, /\.js$/)
const keys = slotsFiles.keys() || []
keys.forEach(key => {
const tag = key.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = slotsFiles(key).default
componentChild[tag] = value
})
function vModel(dataObject, defaultValue) {
dataObject.props.value = defaultValue
dataObject.on.input = val => {
this.$emit('input', val)
}
}
function mountSlotFiles(h, confClone, children) {
const childObjs = componentChild[confClone.__config__.tag]
if (childObjs) {
Object.keys(childObjs).forEach(key => {
const childFunc = childObjs[key]
if (confClone.__slot__ && confClone.__slot__[key]) {
children.push(childFunc(h, confClone, key))
}
})
}
}
function emitEvents(confClone) {
['on', 'nativeOn'].forEach(attr => {
const eventKeyList = Object.keys(confClone[attr] || {})
eventKeyList.forEach(key => {
const val = confClone[attr][key]
if (typeof val === 'string') {
confClone[attr][key] = event => this.$emit(val, event)
}
})
})
}
function buildDataObject(confClone, dataObject) {
Object.keys(confClone).forEach(key => {
const val = confClone[key]
if (key === '__vModel__') {
vModel.call(this, dataObject, confClone.__config__.defaultValue)
} else if (dataObject[key] !== undefined) {
if (dataObject[key] === null
|| dataObject[key] instanceof RegExp
|| ['boolean', 'string', 'number', 'function'].includes(typeof dataObject[key])) {
dataObject[key] = val
} else if (Array.isArray(dataObject[key])) {
dataObject[key] = [...dataObject[key], ...val]
} else {
dataObject[key] = { ...dataObject[key], ...val }
}
} else {
dataObject.attrs[key] = val
}
})
// 清理属性
clearAttrs(dataObject)
}
function clearAttrs(dataObject) {
delete dataObject.attrs.__config__
delete dataObject.attrs.__slot__
delete dataObject.attrs.__methods__
}
function makeDataObject() {
// 深入数据对象:
// https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
return {
class: {},
attrs: {},
props: {},
domProps: {},
nativeOn: {},
on: {},
style: {},
directives: [],
scopedSlots: {},
slot: null,
key: null,
ref: null,
refInFor: true
}
}
export default {
props: {
conf: {
type: Object,
required: true
}
},
render(h) {
const dataObject = makeDataObject()
const confClone = deepClone(this.conf)
const children = this.$slots.default || []
// 如果slots文件夹存在与当前tag同名的文件则执行文件中的代码
mountSlotFiles.call(this, h, confClone, children)
// 将字符串类型的事件,发送为消息
emitEvents.call(this, confClone)
// 将json表单配置转化为vue render可以识别的 “数据对象dataObject
buildDataObject.call(this, confClone, dataObject)
return h(this.conf.__config__.tag, dataObject, children)
}
}

@ -0,0 +1,5 @@
export default {
default(h, conf, key) {
return conf.__slot__[key]
}
}

@ -0,0 +1,13 @@
export default {
options(h, conf, key) {
const list = []
conf.__slot__.options.forEach(item => {
if (conf.__config__.optionType === 'button') {
list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
} else {
list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
}
})
return list
}
}

@ -0,0 +1,8 @@
export default {
prepend(h, conf, key) {
return <template slot="prepend">{conf.__slot__[key]}</template>
},
append(h, conf, key) {
return <template slot="append">{conf.__slot__[key]}</template>
}
}

@ -0,0 +1,13 @@
export default {
options(h, conf, key) {
const list = []
conf.__slot__.options.forEach(item => {
if (conf.__config__.optionType === 'button') {
list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
} else {
list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
}
})
return list
}
}

@ -0,0 +1,9 @@
export default {
options(h, conf, key) {
const list = []
conf.__slot__.options.forEach(item => {
list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
})
return list
}
}

@ -0,0 +1,17 @@
export default {
'list-type': (h, conf, key) => {
const list = []
const config = conf.__config__
if (conf['list-type'] === 'picture-card') {
list.push(<i class="el-icon-plus"></i>)
} else {
list.push(<el-button size="small" type="primary" icon="el-icon-upload">{config.buttonText}</el-button>)
}
if (config.showTip) {
list.push(
<div slot="tip" class="el-upload__tip">只能上传不超过 {config.fileSize}{config.sizeUnit} {conf.accept}文件</div>
)
}
return list
}
}

@ -1,6 +1,7 @@
<template>
<div class="app-container" id="app-container">
<el-form :model="queryParams" id="I-form" ref="queryForm" size="small" :inline="true">
<el-col :span="22">
<el-form-item label="模版名称" prop="templateName">
<el-input
v-model="queryParams.templateName"
@ -9,25 +10,7 @@
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<!-- <el-form-item label="项目分类" prop="projectClassify">
<el-select v-model="queryParams.projectClassify" placeholder="请选择产业领域" clearable>
<el-option
v-for="dict in dict.type.jjh_project_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item> -->
<el-form-item label="项目分类" prop="projectClassify">
<!-- <el-select v-model="queryParams.projectClassify" placeholder="请选择项目小类" clearable>
<el-option
v-for="dict in dict.type.project_small_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select> -->
<el-cascader
placeholder="请选择项目分类"
v-model="queryParams.projectClassify"
@ -36,26 +19,6 @@
>
</el-cascader>
</el-form-item>
<!-- <el-form-item label="产业领域" prop="productArea">
<el-select v-model="queryParams.productArea" placeholder="请选择产业领域" clearable>
<el-option
v-for="dict in dict.type.bms_product_area"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="申报类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择申报类型" clearable>
<el-option
v-for="dict in dict.type.bms_declaration_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item> -->
<el-form-item label="责任单位" prop="responsibilityUnit">
<el-select v-model="queryParams.responsibilityUnit" placeholder="请选择责任单位" clearable>
<el-option
@ -70,6 +33,10 @@
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery" class="btn">查询</el-button>
</el-form-item>
</el-col>
<el-col :span="2">
<el-button type="primary" class="import-btn" icon="icon iconfont icon-jc-daoru" size="mini" @click="importBtn"></el-button>
</el-col>
</el-form>
<el-table
@ -81,7 +48,7 @@
>
<el-table-column label="序号" align="center" width="50" fixed>
<template slot-scope="scope">
<span>{{(queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1}}</span>
<span>{{(queryParams.current - 1) * queryParams.size + scope.$index + 1}}</span>
</template>
</el-table-column>
<el-table-column label="年份" align="center" prop="createTime" min-width="80">
@ -152,8 +119,8 @@
id="L-pagination"
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
:page.sync="queryParams.current"
:limit.sync="queryParams.size"
@pagination="getList"
/>
</div>
@ -180,8 +147,8 @@ export default {
projectClassify: [],
// type: undefined,
responsibilityUnit: undefined,
pageNum: 1,
pageSize: 20,
current: 1,
size: 10,
},
tabHeader: undefined,
optionsTwo: [],
@ -209,6 +176,12 @@ export default {
mounted() {
this.cancalDebounce();
window.addEventListener('resize', this.cancalDebounce);
if(this.$route.params.code == 200) {
this.$message({
type: "success",
message: "新增模板成功!",
});
}
},
destroyed() {
window.removeEventListener('resize', this.cancalDebounce);
@ -243,8 +216,8 @@ export default {
getList() {
this.loading = true;
listInfo(this.queryParams).then(response => {
this.InfoList = response.rows;
this.total = response.total
this.InfoList = response.data.records;
this.total = response.data.total
this.loading = false;
});
},
@ -283,6 +256,12 @@ export default {
window.URL.revokeObjectURL(url)
})
},
//
importBtn(){
this.$router.push({
name: 'onlineDeclarebBuild'
})
},
}
};
</script>

@ -78,6 +78,9 @@
<div class="center-board">
<div class="action-bar">
<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>
@ -132,6 +135,74 @@
@confirm="generate"
/>
<input id="copyNode" type="hidden">
<!-- 新增模板 -->
<el-dialog title="创建新模板" :visible.sync="createTemplate" width="680px" append-to-body :close-on-click-modal="false">
<el-form ref="form" :model="dialogForm" :rules="rules" label-width="120px">
<el-row>
<el-col :span="24">
<el-form-item label="模板名称" prop="templateName">
<el-input v-model="dialogForm.templateName" placeholder="请输入模板名称"/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="项目分类" prop="projectClassify">
<el-cascader
style="width: 100%;"
v-model="dialogForm.projectClassify"
:options="options"
:props="props"
@change="changeProjectClassify"
>
</el-cascader>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="级别" prop="level">
<el-select v-model="dialogForm.level" placeholder="请选择级别" style="width: 100%;">
<el-option
v-for="dict in dict.type.bms_level"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="责任单位" prop="responsibilityUnit">
<el-select v-model="dialogForm.responsibilityUnit" placeholder="请选择责任单位" style="width: 100%;">
<el-option
v-for="dict in dict.type.bms_responsibility_unit"
:key="dict.value"
:label="dict.label"
:value="dict.value"
></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="模板使用有效期" prop="reportTime">
<el-date-picker
v-model="dialogForm.reportTime"
type="daterange"
range-separator="-"
start-placeholder="模板使用有效期开始时间"
end-placeholder="模板使用有效期结束时间"
style="width: 100%;"
value-format="yyyy-MM-dd"
@change="changeDateRange"
>
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="cancel"> </el-button>
<el-button type="primary" @click="submitForm" class="btn"> </el-button>
</div>
</el-dialog>
</div>
</template>
@ -150,6 +221,8 @@ import drawingDefault from '@/utils/generator/drawingDefault'
import logo from '@/assets/logo/logo.png'
import CodeTypeDialog from './CodeTypeDialog'
import DraggableItem from './DraggableItem'
import { templateInfo } from "@/api/onlineDeclartion/declarationManagement"
import { getAllList } from "@/api/system/dict/data";
let oldActiveId
let tempActiveData
@ -162,7 +235,15 @@ export default {
CodeTypeDialog,
DraggableItem
},
dicts: ['bms_responsibility_unit', 'bms_level'],
data() {
let validateTime = (rule, value, callback) => {
if (new Date(value[1]) < new Date()) {
callback(new Error('选择结束时间需大于当前时间'))
} else {
callback()
}
}
return {
logo,
idGlobal: 100,
@ -179,7 +260,55 @@ export default {
dialogVisible: false,
generateConf: null,
showFileName: false,
activeData: drawingDefault[0]
activeData: drawingDefault[0],
options: [],
props:{
lazy:true,
lazyLoad (node, resolve) {
const { level, data, children } = node;
if(children?.length == 0 ) {
getAllList({dictType: data.dict}).then(res=>{
const nodes = Array.from(res.data).map((item)=>{
return {
value:item.dictValue,
label:item.dictLabel,
dict:item.remark,
leaf: level >= 2
}
})
resolve(nodes)
})
}
}
},
//
dialogForm: {},
//
rules: {
// year: [
// { required: true, message: "", trigger: "blur" },
// ],
responsibilityUnit: [
{ required: true, message: "请选择责任单位", trigger: "blur" }
],
level: [
{ required: true, message: "请选择级别", trigger: "blur" }
],
projectClassify: [
{ required: true, message: "请选择项目分类", trigger: "blur" }
],
templateName: [
{ required: true, message: "请输入模板名称", trigger: "blur" }
],
reportTime: [
{ required: true, message: "请选择模板使用有效期", trigger: "blur" },
{validator: validateTime, trigger: 'blur'},
],
},
projectBigType: undefined,
projectMiddleType: undefined,
projectSmallType: undefined,
createTemplate: true,
}
},
created() {
@ -223,8 +352,61 @@ export default {
clipboard.on('error', e => {
this.$message.error('代码复制失败')
})
this.getDataList();
},
methods: {
getDataList(){
getAllList({dictType:"project_categories"}).then(res=>{
res.data.map(item => {
let obj = {};
obj.value = item.dictValue;
obj.label = item.dictLabel;
obj.dict = item.remark;
this.options.push(obj);
})
})
},
//
changeProjectClassify(valId){
if(valId) {
this.projectBigType = valId[0]
this.projectMiddleType = valId[1]
this.projectSmallType = valId[2]
}
},
//
changeDateRange(val) {
// console.log('val: ', val);
this.dialogForm.useStart = val[0]
this.dialogForm.useEnd = val[1]
},
//
cancel() {
this.createTemplate = false;
this.reset();
},
//
reset() {
this.dialogForm = {
responsibilityUnit: undefined,
templateName: undefined,
level: undefined,
projectClassify:[],
reportTime: [],
};
this.projectBigType = undefined
this.projectMiddleType = undefined
this.projectSmallType = undefined
this.resetForm("form");
},
/** 提交按钮 */
submitForm: function() {
this.$refs["form"].validate(valid => {
if (valid) {
this.createTemplate = false;
}
});
},
activeFormItem(element) {
this.activeData = element
this.activeId = element.formId
@ -324,6 +506,24 @@ export default {
const css = cssStyle(makeUpCss(this.formData))
return beautifier.html(html + script + css, beautifierConf.html)
},
showJson() {
this.AssembleFormData()
// 使使
const { level, responsibilityUnit, useStart, useEnd, templateName } = this.dialogForm;
if(level && responsibilityUnit && useStart && useEnd && templateName) {
templateInfo({level, responsibilityUnit, templateName, useStart, useEnd, projectBigType:this.projectBigType, projectMiddleType:this.projectMiddleType, projectSmallType:this.projectSmallType,templateJson: JSON.stringify(this.formData)}).then(res=>{
if(res.code == 200) {
this.$router.push({
name: 'Template',
params: {code:200},
})
}
})
} else {
this.createTemplate = true;
}
},
download() {
this.dialogVisible = true
this.showFileName = true

Loading…
Cancel
Save