From dffd175ccf4e7782a13e12b56cfaf096f14fc763 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 2 Jan 2022 09:59:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=20form=20=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E8=A1=A8=E5=8D=95=E7=9A=84=E8=AF=A6=E6=83=85=EF=BC=8C?= =?UTF-8?q?=E6=9A=82=E6=9C=AA=E6=8E=A5=E5=85=A5=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yudao-admin-ui/src/utils/generator/README.md | 1 + yudao-admin-ui/src/utils/index.js | 89 +++-- yudao-admin-ui/src/utils/parser/Parser.vue | 188 ++++++++++ yudao-admin-ui/src/utils/parser/README.md | 19 + .../src/utils/parser/example/Index.vue | 324 ++++++++++++++++++ yudao-admin-ui/src/utils/parser/index.js | 3 + yudao-admin-ui/src/utils/parser/package.json | 25 ++ yudao-admin-ui/src/utils/render/README.md | 1 + yudao-admin-ui/src/utils/render/package.json | 19 + yudao-admin-ui/src/utils/render/render.js | 122 +++++++ .../src/utils/render/slots/el-button.js | 5 + .../utils/render/slots/el-checkbox-group.js | 13 + .../src/utils/render/slots/el-input.js | 8 + .../src/utils/render/slots/el-radio-group.js | 13 + .../src/utils/render/slots/el-select.js | 9 + .../src/utils/render/slots/el-upload.js | 17 + yudao-admin-ui/src/views/bpm/form/index.vue | 202 ++++++++++- 17 files changed, 1032 insertions(+), 26 deletions(-) create mode 100644 yudao-admin-ui/src/utils/generator/README.md create mode 100644 yudao-admin-ui/src/utils/parser/Parser.vue create mode 100644 yudao-admin-ui/src/utils/parser/README.md create mode 100644 yudao-admin-ui/src/utils/parser/example/Index.vue create mode 100644 yudao-admin-ui/src/utils/parser/index.js create mode 100644 yudao-admin-ui/src/utils/parser/package.json create mode 100644 yudao-admin-ui/src/utils/render/README.md create mode 100644 yudao-admin-ui/src/utils/render/package.json create mode 100644 yudao-admin-ui/src/utils/render/render.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-button.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-checkbox-group.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-input.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-radio-group.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-select.js create mode 100644 yudao-admin-ui/src/utils/render/slots/el-upload.js diff --git a/yudao-admin-ui/src/utils/generator/README.md b/yudao-admin-ui/src/utils/generator/README.md new file mode 100644 index 000000000..46a4c15ec --- /dev/null +++ b/yudao-admin-ui/src/utils/generator/README.md @@ -0,0 +1 @@ +【add by 芋道源码】来自 https://github.com/JakHuang/form-generator/tree/dev/src/components/generator 目录 \ No newline at end of file diff --git a/yudao-admin-ui/src/utils/index.js b/yudao-admin-ui/src/utils/index.js index 918580f7b..4d29dd0ec 100644 --- a/yudao-admin-ui/src/utils/index.js +++ b/yudao-admin-ui/src/utils/index.js @@ -5,12 +5,12 @@ import { parseTime } from './ruoyi' */ export function formatDate(cellValue) { if (cellValue == null || cellValue == "") return ""; - var date = new Date(cellValue) + var date = new Date(cellValue) var year = date.getFullYear() var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 - var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() - var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() - var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds } @@ -250,26 +250,65 @@ export function debounce(func, wait, immediate) { } } -/** - * This is just a simple version of deep copy - * Has a lot of edge cases bug - * If you want to use a perfect deep copy, use lodash's _.cloneDeep - * @param {Object} source - * @returns {Object} - */ -export function deepClone(source) { - if (!source && typeof source !== 'object') { - throw new Error('error arguments', 'deepClone') +// /** +// * This is just a simple version of deep copy +// * Has a lot of edge cases bug +// * If you want to use a perfect deep copy, use lodash's _.cloneDeep +// * @param {Object} source +// * @returns {Object} +// */ +// export function deepClone(source) { +// if (!source && typeof source !== 'object') { +// throw new Error('error arguments', 'deepClone') +// } +// const targetObj = source.constructor === Array ? [] : {} +// Object.keys(source).forEach(keys => { +// if (source[keys] && typeof source[keys] === 'object') { +// targetObj[keys] = deepClone(source[keys]) +// } else { +// targetObj[keys] = source[keys] +// } +// }) +// return targetObj +// } + +// 深拷贝对象 +// 【add by 芋道源码】https://github.com/JakHuang/form-generator/blob/dev/src/utils/index.js#L107 +export function deepClone(obj) { + const _toString = Object.prototype.toString + + // null, undefined, non-object, function + if (!obj || typeof obj !== 'object') { + return obj } - const targetObj = source.constructor === Array ? [] : {} - Object.keys(source).forEach(keys => { - if (source[keys] && typeof source[keys] === 'object') { - targetObj[keys] = deepClone(source[keys]) - } else { - targetObj[keys] = source[keys] - } - }) - return targetObj + + // 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] = deepClone(obj[key]) + } + + return result } /** @@ -330,7 +369,7 @@ export function makeMap(str, expectsLowerCase) { ? val => map[val.toLowerCase()] : val => map[val] } - + export const exportDefault = 'export default ' export const beautifierConf = { @@ -387,4 +426,4 @@ export function camelCase(str) { export function isNumberStr(str) { return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) } - + diff --git a/yudao-admin-ui/src/utils/parser/Parser.vue b/yudao-admin-ui/src/utils/parser/Parser.vue new file mode 100644 index 000000000..632371a66 --- /dev/null +++ b/yudao-admin-ui/src/utils/parser/Parser.vue @@ -0,0 +1,188 @@ + diff --git a/yudao-admin-ui/src/utils/parser/README.md b/yudao-admin-ui/src/utils/parser/README.md new file mode 100644 index 000000000..6239e7c3e --- /dev/null +++ b/yudao-admin-ui/src/utils/parser/README.md @@ -0,0 +1,19 @@ +## form-generator JSON 解析器 +>用于将form-generator导出的JSON解析成一个表单。 + +### 安装组件 +``` +npm i form-gen-parser +``` +或者 +``` +yarn add form-gen-parser +``` + +### 使用示例 +> [查看在线示例](https://mrhj.gitee.io/form-generator/#/parser) + +示例代码: +> [src\components\parser\example\Index.vue](https://github.com/JakHuang/form-generator/blob/dev/src/components/parser/example/Index.vue) + +【add by 芋道源码】https://github.com/JakHuang/form-generator/blob/dev/src/components/parser/ \ No newline at end of file diff --git a/yudao-admin-ui/src/utils/parser/example/Index.vue b/yudao-admin-ui/src/utils/parser/example/Index.vue new file mode 100644 index 000000000..d218509c2 --- /dev/null +++ b/yudao-admin-ui/src/utils/parser/example/Index.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/yudao-admin-ui/src/utils/parser/index.js b/yudao-admin-ui/src/utils/parser/index.js new file mode 100644 index 000000000..0a44b2ccc --- /dev/null +++ b/yudao-admin-ui/src/utils/parser/index.js @@ -0,0 +1,3 @@ +import Parser from './Parser' + +export default Parser diff --git a/yudao-admin-ui/src/utils/parser/package.json b/yudao-admin-ui/src/utils/parser/package.json new file mode 100644 index 000000000..ffeaba32f --- /dev/null +++ b/yudao-admin-ui/src/utils/parser/package.json @@ -0,0 +1,25 @@ +{ + "name": "form-gen-parser", + "version": "1.0.3", + "description": "表单json解析器", + "main": "lib/form-gen-parser.umd.js", + "directories": { + "example": "example" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/JakHuang/form-generator.git" + }, + "dependencies": { + "form-gen-render": "^1.0.0" + }, + "author": "jakHuang", + "license": "MIT", + "bugs": { + "url": "https://github.com/JakHuang/form-generator/issues" + }, + "homepage": "https://github.com/JakHuang/form-generator/blob/dev/src/components/parser" +} diff --git a/yudao-admin-ui/src/utils/render/README.md b/yudao-admin-ui/src/utils/render/README.md new file mode 100644 index 000000000..14fd3aee7 --- /dev/null +++ b/yudao-admin-ui/src/utils/render/README.md @@ -0,0 +1 @@ +【add by 芋道源码】https://github.com/JakHuang/form-generator/blob/dev/src/components/render/ \ No newline at end of file diff --git a/yudao-admin-ui/src/utils/render/package.json b/yudao-admin-ui/src/utils/render/package.json new file mode 100644 index 000000000..96bffcfec --- /dev/null +++ b/yudao-admin-ui/src/utils/render/package.json @@ -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" +} diff --git a/yudao-admin-ui/src/utils/render/render.js b/yudao-admin-ui/src/utils/render/render.js new file mode 100644 index 000000000..f3325dcdf --- /dev/null +++ b/yudao-admin-ui/src/utils/render/render.js @@ -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) + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-button.js b/yudao-admin-ui/src/utils/render/slots/el-button.js new file mode 100644 index 000000000..a2d9684eb --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-button.js @@ -0,0 +1,5 @@ +export default { + default(h, conf, key) { + return conf.__slot__[key] + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-checkbox-group.js b/yudao-admin-ui/src/utils/render/slots/el-checkbox-group.js new file mode 100644 index 000000000..0a85c8e75 --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-checkbox-group.js @@ -0,0 +1,13 @@ +export default { + options(h, conf, key) { + const list = [] + conf.__slot__.options.forEach(item => { + if (conf.__config__.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-input.js b/yudao-admin-ui/src/utils/render/slots/el-input.js new file mode 100644 index 000000000..8bd02db2a --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-input.js @@ -0,0 +1,8 @@ +export default { + prepend(h, conf, key) { + return + }, + append(h, conf, key) { + return + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-radio-group.js b/yudao-admin-ui/src/utils/render/slots/el-radio-group.js new file mode 100644 index 000000000..c78506f9a --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-radio-group.js @@ -0,0 +1,13 @@ +export default { + options(h, conf, key) { + const list = [] + conf.__slot__.options.forEach(item => { + if (conf.__config__.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-select.js b/yudao-admin-ui/src/utils/render/slots/el-select.js new file mode 100644 index 000000000..cbf4a2030 --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-select.js @@ -0,0 +1,9 @@ +export default { + options(h, conf, key) { + const list = [] + conf.__slot__.options.forEach(item => { + list.push() + }) + return list + } +} diff --git a/yudao-admin-ui/src/utils/render/slots/el-upload.js b/yudao-admin-ui/src/utils/render/slots/el-upload.js new file mode 100644 index 000000000..8ce3c351c --- /dev/null +++ b/yudao-admin-ui/src/utils/render/slots/el-upload.js @@ -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() + } else { + list.push({config.buttonText}) + } + if (config.showTip) { + list.push( +
只能上传不超过 {config.fileSize}{config.sizeUnit} 的{conf.accept}文件
+ ) + } + return list + } +} diff --git a/yudao-admin-ui/src/views/bpm/form/index.vue b/yudao-admin-ui/src/views/bpm/form/index.vue index 9abb7cef3..f2998c963 100644 --- a/yudao-admin-ui/src/views/bpm/form/index.vue +++ b/yudao-admin-ui/src/views/bpm/form/index.vue @@ -38,6 +38,8 @@