From 879cd36bafaf108225a79f0455cb54767d962fd8 Mon Sep 17 00:00:00 2001 From: robin <1032740078@qq.com> Date: Thu, 1 Dec 2022 21:48:35 +0800 Subject: [PATCH] 1 --- .cz-config.js | 32 ++ .gitignore | 68 +++ LICENSE | 21 + README.md | 141 ++++++ babel.config.js | 4 + examples/App.vue | 303 +++++++++++ examples/index.html | 11 + examples/main.js | 18 + package.json | 135 +++++ src/components/DragBox.vue | 33 ++ src/components/DragTool.vue | 136 +++++ src/components/FcDesigner.vue | 861 ++++++++++++++++++++++++++++++++ src/components/Fetch.vue | 162 ++++++ src/components/IconRefresh.vue | 14 + src/components/Required.vue | 64 +++ src/components/Struct.vue | 127 +++++ src/components/TableOptions.vue | 75 +++ src/components/Validate.vue | 229 +++++++++ src/config/base/field.js | 84 ++++ src/config/base/form.js | 55 ++ src/config/base/validate.js | 9 + src/config/menu.js | 51 ++ src/config/rule/alert.js | 45 ++ src/config/rule/button.js | 54 ++ src/config/rule/cascader.js | 180 +++++++ src/config/rule/checkbox.js | 45 ++ src/config/rule/col.js | 23 + src/config/rule/color.js | 36 ++ src/config/rule/date.js | 75 +++ src/config/rule/divider.js | 37 ++ src/config/rule/drawer.js | 27 + src/config/rule/editor.js | 23 + src/config/rule/image.ts | 57 +++ src/config/rule/index.js | 61 +++ src/config/rule/input.js | 65 +++ src/config/rule/number.js | 41 ++ src/config/rule/radio.js | 42 ++ src/config/rule/rate.js | 44 ++ src/config/rule/row.js | 41 ++ src/config/rule/select.js | 62 +++ src/config/rule/slider.js | 44 ++ src/config/rule/space.js | 41 ++ src/config/rule/span.js | 33 ++ src/config/rule/switch.js | 39 ++ src/config/rule/tab.js | 36 ++ src/config/rule/tabPane.js | 29 ++ src/config/rule/time.js | 57 +++ src/config/rule/transfer.js | 84 ++++ src/config/rule/tree.js | 100 ++++ src/config/rule/upload.js | 64 +++ src/index.js | 56 +++ src/style/fonts/fc-icons.woff | Bin 0 -> 5448 bytes src/style/fonts/iconfont.css | 17 + src/style/fonts/iconfont.ttf | Bin 0 -> 1772 bytes src/style/index.css | 138 +++++ src/utils/form.js | 9 + src/utils/index.js | 132 +++++ tools/cli/run.ts | 61 +++ tools/lib/build.ts | 163 ++++++ tools/lib/components.ts | 73 +++ tools/lib/packages.ts | 88 ++++ tools/lib/paths.ts | 14 + tools/lib/utils.ts | 140 ++++++ tools/rollup/plugin/index.ts | 69 +++ tsconfig.json | 21 + types/index.d.ts | 31 ++ vite.config.build.js | 85 ++++ vite.config.preview.js | 22 + vue.config.js | 28 ++ 69 files changed, 5265 insertions(+) create mode 100644 .cz-config.js create mode 100755 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 babel.config.js create mode 100644 examples/App.vue create mode 100644 examples/index.html create mode 100644 examples/main.js create mode 100644 package.json create mode 100644 src/components/DragBox.vue create mode 100644 src/components/DragTool.vue create mode 100644 src/components/FcDesigner.vue create mode 100644 src/components/Fetch.vue create mode 100644 src/components/IconRefresh.vue create mode 100644 src/components/Required.vue create mode 100644 src/components/Struct.vue create mode 100644 src/components/TableOptions.vue create mode 100644 src/components/Validate.vue create mode 100644 src/config/base/field.js create mode 100644 src/config/base/form.js create mode 100644 src/config/base/validate.js create mode 100644 src/config/menu.js create mode 100644 src/config/rule/alert.js create mode 100644 src/config/rule/button.js create mode 100644 src/config/rule/cascader.js create mode 100644 src/config/rule/checkbox.js create mode 100644 src/config/rule/col.js create mode 100644 src/config/rule/color.js create mode 100644 src/config/rule/date.js create mode 100644 src/config/rule/divider.js create mode 100644 src/config/rule/drawer.js create mode 100644 src/config/rule/editor.js create mode 100644 src/config/rule/image.ts create mode 100644 src/config/rule/index.js create mode 100644 src/config/rule/input.js create mode 100644 src/config/rule/number.js create mode 100644 src/config/rule/radio.js create mode 100644 src/config/rule/rate.js create mode 100644 src/config/rule/row.js create mode 100644 src/config/rule/select.js create mode 100644 src/config/rule/slider.js create mode 100644 src/config/rule/space.js create mode 100644 src/config/rule/span.js create mode 100644 src/config/rule/switch.js create mode 100644 src/config/rule/tab.js create mode 100644 src/config/rule/tabPane.js create mode 100644 src/config/rule/time.js create mode 100644 src/config/rule/transfer.js create mode 100644 src/config/rule/tree.js create mode 100644 src/config/rule/upload.js create mode 100644 src/index.js create mode 100644 src/style/fonts/fc-icons.woff create mode 100644 src/style/fonts/iconfont.css create mode 100644 src/style/fonts/iconfont.ttf create mode 100644 src/style/index.css create mode 100644 src/utils/form.js create mode 100644 src/utils/index.js create mode 100644 tools/cli/run.ts create mode 100644 tools/lib/build.ts create mode 100644 tools/lib/components.ts create mode 100644 tools/lib/packages.ts create mode 100644 tools/lib/paths.ts create mode 100644 tools/lib/utils.ts create mode 100644 tools/rollup/plugin/index.ts create mode 100644 tsconfig.json create mode 100644 types/index.d.ts create mode 100644 vite.config.build.js create mode 100644 vite.config.preview.js create mode 100644 vue.config.js diff --git a/.cz-config.js b/.cz-config.js new file mode 100644 index 0000000..a8d7c28 --- /dev/null +++ b/.cz-config.js @@ -0,0 +1,32 @@ +module.exports = { + types: [ + {value: 'feat', name: 'feat: 新功能'}, + {value: 'fix', name: 'fix: 修复'}, + {value: 'docs', name: 'docs: 文档变更'}, + {value: 'style', name: 'style: 代码格式(不影响代码运行的变动)'}, + {value: 'cli', name: 'cli: 脚手架优化(不影响代码运行的变动)'}, + {value: 'refactor', name: 'refactor: 重构(既不是增加feature,也不是修复bug)'}, + {value: 'perf', name: 'perf: 性能优化'}, + {value: 'test', name: 'test: 增加测试'}, + {value: 'chore', name: 'chore: 构建过程或辅助工具的变动'}, + {value: 'revert', name: 'revert: 回退'}, + {value: 'build', name: 'build: 打包'} + ], + // override the messages, defaults are as follows + messages: { + type: '请选择提交类型:', + scope: '请输入文件修改范围(可选):', + // used if allowCustomScopes is true + customScope: '请输入修改范围(可选):', + subject: '请简要描述提交(必填):', + body: '请输入详细描述(可选,待优化去除,跳过即可):', + // breaking: 'List any BREAKING CHANGES (optional):\n', + footer: '请输入要关闭的issue(待优化去除,跳过即可):', + confirmCommit: '确认使用以上信息提交?(y/n/e/h)' + }, + allowCustomScopes: true, + // allowBreakingChanges: ['feat', 'fix'], + skipQuestions: ['body', 'footer'], + // limit subject length, commitlint默认是72 + subjectLimit: 72 +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..10e41f2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +stats.html + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next +.idea + +# custom +yarn.lock +/index.html +dist diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..210009a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 xaboy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7c0a77f --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +

+ + + +

+ +# form-create-designer v3 + +**这个是 Vue3 版本** + +[![MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/xaboy/form-create-designer) +[![github](https://img.shields.io/badge/Author-xaboy-blue.svg)](https://github.com/xaboy) + +**form-create-designer 是基于 [@form-create/element-ui](https://github.com/xaboy/form-create) vue3版本实现的表单设计器组件。可以通过拖拽的方式快速创建表单,提高开发者对表单的开发效率,节省开发者的时间。** + +**[文档](http://designer.form-create.com/guide/) | [在线演示](http://form-create.com/v3/designer?fr=github) | [form-create 文档](http://form-create.com/v3/guide/)** + +> 如果对您有帮助,您可以点右上角 "Star" 支持一下 谢谢!本项目还在不断开发完善中,如有任何建议或问题[请在这里提出](https://github.com/xaboy/form-create-designer/issues/new) + +> 本项目QQ讨论群[629709230](https://jq.qq.com/?_wv=1027&k=F1FlEFIV) + + + +![demo1](http://form-create.com/img/designer-review.png) + +## 引入 + +**CDN:** + +```html + + + + + +``` + +**NodeJs:** + +```shell +npm install @form-create/designer@next +``` + +请自行导入`ElementPlus`并挂载 + +```js +import formCreate from '@form-create/element-ui' +import FcDesigner from '@form-create/designer' + +app.use(formCreate) +app.use(FcDesigner) +``` + +## 使用 + +```html + +``` + +## 组件`props` + +- **menu**`MenuList` 重新配置拖拽的组件 + +- **height**`int|string` 设计器组件高度, 默认`100%` + +## 组件方法 + +- 获取当前生成表单的生成规则 + + ```ts + type getRule = () => Rule[] + ``` + **示例: `this.$refs.designer.getRule()`** + +- 获取当前表单的全局配置 + + ```ts + type getOption = () => Object + ``` + +- 设置当前生成表单的规则 + + ```ts + type setRule = (rules: Rule[]) => void; + ``` + +- 设置当前表单的全局配置 + + ```ts + type setOption = (option: Object) => void; + ``` + +- 增加一组拖拽组件 + + ```ts + type addMenu = (menu: Menu) => void; + ``` +- 删除一组拖拽组件 + + ```ts + type removeMenu = (name: string) => void; + ``` + +- 批量覆盖插入拖拽组件 + + ```ts + type setMenuItem = (name: string, items: MenuItem[]) => void; + ``` + +- 插入一个拖拽组件到分组 + + ```ts + type appendMenuItem = (name:string, item: MenuItem) => void; + ``` + +- 删除一个拖拽组件 + + ```ts + type removeMenuItem = (item: string | MenuItem) => void; + ``` + +- 新增一个拖拽组件的生成规则 + + ```ts + type addComponent = (item: DragRule) => void; + ``` +> **提示! 内置的三个组件分组`name`分别为: `main`,`aide`,`layout`** + +## 捐赠 + +![donation.jpg](http://form-create.com/img/donation.jpg) + +## 联系 + +##### email : xaboy2005@qq.com + +## License + +[MIT](http://opensource.org/licenses/MIT) + +Copyright (c) 2021-present xaboy diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..1a3e147 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + 'presets': [['@vue/cli-plugin-babel/preset', {'useBuiltIns': false}]], + 'plugins': ['@vue/babel-plugin-jsx'] +} diff --git a/examples/App.vue b/examples/App.vue new file mode 100644 index 0000000..f165a3c --- /dev/null +++ b/examples/App.vue @@ -0,0 +1,303 @@ + + + + + diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..f145d99 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,11 @@ + + + + + form-create-designer 示例 + + +
+
+ + diff --git a/examples/main.js b/examples/main.js new file mode 100644 index 0000000..8cfd809 --- /dev/null +++ b/examples/main.js @@ -0,0 +1,18 @@ +import {createApp} from 'vue'; +import ELEMENT from 'element-plus'; +import 'element-plus/dist/index.css'; +import formCreate from '@form-create/element-ui'; +import Antd from 'ant-design-vue'; +import App from './App'; +import 'ant-design-vue/dist/antd.css'; +import FcDesigner from '../src/index'; + +const app = createApp(App); + +app.use(Antd); +app.use(ELEMENT); +app.use(formCreate); +app.use(FcDesigner); + + +app.mount('#app') diff --git a/package.json b/package.json new file mode 100644 index 0000000..8cbb1d0 --- /dev/null +++ b/package.json @@ -0,0 +1,135 @@ +{ + "name": "@form-create/designer", + "version": "3.0.2", + "description": "好用的vue可视化表单设计器组件", + "unpkg": "./dist/index.umd.js", + "jsdelivr": "./dist/index.umd.js", + "typings": "./types/index.d.ts", + "main": "./dist/index.umd.js", + "module": "./dist/index.es.js", + "exports": { + ".": { + "import": "./dist/index.es.js", + "require": "./dist/index.umd.js" + } + }, + "scripts": { + "clean": "rimraf dist/", + "dev": "vue-cli-service serve", + "rollup": "rollup -c ./rollup.config.ts", + "build": "vite build --config ./vite.config.build.js", + "build:preview": "vite build --config ./vite.config.preview.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/xaboy/form-create-designer.git" + }, + "keywords": [ + "表单设计器", + "@form-create", + "form-builder", + "form-designer", + "draggable", + "form", + "components", + "vue3", + "element-ui", + "json-form", + "dynamic-form" + ], + "files": [ + "README.md", + "package.json", + "LICENSE", + "src", + "types", + "dist" + ], + "author": "xaboy", + "license": "MIT", + "bugs": { + "url": "https://github.com/xaboy/form-create-designer/issues" + }, + "homepage": "http://designer.form-create.com", + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "@element-plus/icons-vue": "^0.2.6", + "@sixian/css-url": "^1.0.3", + "@types/chalk": "^2.2.0", + "@types/shelljs": "^0.8.9", + "@vitejs/plugin-vue": "^3.1.2", + "@vitejs/plugin-vue-jsx": "^2.0.1", + "@vue/babel-plugin-jsx": "^1.0.7", + "@vue/cli-plugin-babel": "^4.5.13", + "@vue/cli-service": "^4.5.3", + "@vue/compiler-sfc": "^3.0.11", + "babel-eslint": "^10.1.0", + "chalk": "^4.1.2", + "codemirror": "^5.60.0", + "commander": "^6.0.0", + "commitizen": "^4.1.2", + "cross-env": "^7.0.2", + "css-loader": "^4.2.1", + "cssnano": "^5.1.13", + "cssnano-preset-advanced": "^5.3.8", + "cz-conventional-changelog": "^3.2.0", + "cz-customizable": "^6.3.0", + "dayjs": "^1.10.7", + "element-plus": "^2.0.1", + "eslint": "^7.7.0", + "eslint-plugin-vue": "^6.2.2", + "esno": "^0.9.1", + "execa": "^5.1.1", + "fast-glob": "^3.2.7", + "figlet": "^1.5.0", + "fs-extra": "^10.0.0", + "html-webpack-plugin": "^4.3.0", + "humps": "^2.0.1", + "husky": "^4.2.5", + "jsonlint-mod": "^1.7.6", + "lint-staged": "^10.2.11", + "npm-run-all": "^4.1.5", + "ora": "^5.0.0", + "postcss": "^8.4.17", + "rimraf": "^3.0.2", + "rollup-plugin-visualizer": "^5.8.2", + "shelljs": "^0.8.4", + "stringify-author": "^0.1.3", + "tslib": "^2.3.1", + "typescript": "^4.4.3", + "vite": "^3.1.4", + "vite-plugin-banner": "^0.5.0", + "vite-plugin-css-injected-by-js": "^2.1.0", + "vue": "^3.1.5", + "vue-loader": "^15.9.3", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.6.11" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-customizable" + } + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,jsx,vue}": [ + "eslint --fix", + "git add" + ] + }, + "dependencies": { + "@form-create/ant-design-vue": "next", + "@form-create/component-wangeditor": "^3.1", + "@form-create/designer": "^1.0.8", + "@form-create/element-ui": "^3.1.16", + "@form-create/utils": "^3.1.15", + "ant-design-vue": "^3.2.15", + "vuedraggable": "4.1.0" + } +} diff --git a/src/components/DragBox.vue b/src/components/DragBox.vue new file mode 100644 index 0000000..0733977 --- /dev/null +++ b/src/components/DragBox.vue @@ -0,0 +1,33 @@ + diff --git a/src/components/DragTool.vue b/src/components/DragTool.vue new file mode 100644 index 0000000..1a89124 --- /dev/null +++ b/src/components/DragTool.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/src/components/FcDesigner.vue b/src/components/FcDesigner.vue new file mode 100644 index 0000000..9c44256 --- /dev/null +++ b/src/components/FcDesigner.vue @@ -0,0 +1,861 @@ + + + + + + + diff --git a/src/components/Fetch.vue b/src/components/Fetch.vue new file mode 100644 index 0000000..96a69fe --- /dev/null +++ b/src/components/Fetch.vue @@ -0,0 +1,162 @@ + + + + diff --git a/src/components/IconRefresh.vue b/src/components/IconRefresh.vue new file mode 100644 index 0000000..2796dfe --- /dev/null +++ b/src/components/IconRefresh.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/components/Required.vue b/src/components/Required.vue new file mode 100644 index 0000000..7fcac58 --- /dev/null +++ b/src/components/Required.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/components/Struct.vue b/src/components/Struct.vue new file mode 100644 index 0000000..59ecfac --- /dev/null +++ b/src/components/Struct.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/components/TableOptions.vue b/src/components/TableOptions.vue new file mode 100644 index 0000000..15ba384 --- /dev/null +++ b/src/components/TableOptions.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/components/Validate.vue b/src/components/Validate.vue new file mode 100644 index 0000000..edfd987 --- /dev/null +++ b/src/components/Validate.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/config/base/field.js b/src/config/base/field.js new file mode 100644 index 0000000..20485e2 --- /dev/null +++ b/src/config/base/field.js @@ -0,0 +1,84 @@ +import IconRefresh from '../../components/IconRefresh.vue'; +import {markRaw} from 'vue'; + +export default function field() { + return [ + { + type: 'input', + field: 'field', + value: '', + title: '字段 ID', + }, { + type: 'input', + field: 'title', + value: '', + title: '字段名称', + }, { + type: 'input', + field: 'info', + value: '', + title: '提示信息', + }, { + type: 'Struct', + field: '_control', + value: [], + title: '联动数据', + props: { + defaultValue: [], + validate(val) { + if (!Array.isArray(val)) return false; + if (!val.length) return true; + return !val.some(({rule}) => { + return !Array.isArray(rule); + }); + } + } + }, { + type: 'col', + props: { + span: 24 + }, + children: [ + { + type: 'el-button', + props: { + type: 'primary', + size: 'small', + }, + inject: true, + on: { + click({$f}) { + const rule = $f.activeRule; + if (rule) { + rule.__fc__.updateKey(); + rule.value = undefined; + rule.__fc__.$api.sync(rule); + } + }, + }, + native: true, + children: [{type: 'i', class: 'fc-icon icon-delete'}, '清空值'] + }, { + type: 'el-button', + props: { + type: 'success', + size: 'small', + icon: markRaw(IconRefresh), + }, + inject: true, + on: { + click({$f}) { + const rule = $f.activeRule; + if (rule) { + rule.__fc__.updateKey(true); + rule.__fc__.$api.sync(rule); + } + }, + }, + native: true, + children: ['刷新'] + }, + ] + } + ]; +} diff --git a/src/config/base/form.js b/src/config/base/form.js new file mode 100644 index 0000000..4cee918 --- /dev/null +++ b/src/config/base/form.js @@ -0,0 +1,55 @@ +export default function form() { + return [ + { + type: 'radio', + field: 'labelPosition', + value: 'left', + title: '标签位置', + options: [ + {value: 'right', label: 'right'}, + {value: 'left', label: 'left'}, + {value: 'top', label: 'top'}, + ] + }, { + type: 'radio', + field: 'size', + value: 'small', + title: '表单尺寸', + options: [ + {value: 'large', label: 'large'}, + {value: 'default', label: 'default'}, + {value: 'small', label: 'small'}, + ] + }, { + type: 'input', + field: 'labelWidth', + value: '125px', + title: '标签宽度', + }, { + type: 'switch', + field: 'hideRequiredAsterisk', + value: false, + title: '隐藏必填字段的标签旁边的红色星号', + }, { + type: 'switch', + field: 'showMessage', + value: true, + title: '显示校验错误信息', + }, { + type: 'switch', + field: 'inlineMessage', + value: false, + title: '以行内形式展示校验信息', + }, { + type: 'switch', + field: 'formCreateSubmitBtn', + value: true, + title: '是否显示表单提交按钮', + }, { + type: 'switch', + field: 'formCreateResetBtn', + value: false, + title: '是否显示表单重置按钮', + }, + ]; +} diff --git a/src/config/base/validate.js b/src/config/base/validate.js new file mode 100644 index 0000000..91b42ab --- /dev/null +++ b/src/config/base/validate.js @@ -0,0 +1,9 @@ +export default function validate() { + return [ + { + type: 'validate', + field: 'validate', + value: [] + }, + ]; +} \ No newline at end of file diff --git a/src/config/menu.js b/src/config/menu.js new file mode 100644 index 0000000..e71a4bc --- /dev/null +++ b/src/config/menu.js @@ -0,0 +1,51 @@ +import radio from './rule/radio'; +import checkbox from './rule/checkbox'; +import input from './rule/input'; +import number from './rule/number'; +import select from './rule/select'; +import _switch from './rule/switch'; +import slider from './rule/slider'; +import time from './rule/time'; +import date from './rule/date'; +import rate from './rule/rate'; +import color from './rule/color'; +import row from './rule/row'; +import divider from './rule/divider'; +import cascader from './rule/cascader'; +import upload from './rule/upload'; +import transfer from './rule/transfer'; +import tree from './rule/tree'; +import alert from './rule/alert'; +import span from './rule/span'; +import space from './rule/space'; +import button from './rule/button'; +import editor from './rule/editor'; +import tab from './rule/tab'; +import drawer from './rule/drawer'; +import image from './rule/image.ts'; +// 还需要在./rule/index.js文件里注册 +export default function createMenu() { + return [ + { + name: 'main', + title: '表单组件', + list: [ + input, number, radio, checkbox, select, _switch, time, date, slider, rate, color, cascader, upload, transfer, tree, editor + ] + }, + { + name: 'aide', + title: '辅助组件', + list: [ + alert, button, span, divider,image + ] + }, + { + name: 'layout', + title: '布局组件', + list: [ + row, tab, space,drawer + ] + }, + ]; +} diff --git a/src/config/rule/alert.js b/src/config/rule/alert.js new file mode 100644 index 0000000..4e6f5d2 --- /dev/null +++ b/src/config/rule/alert.js @@ -0,0 +1,45 @@ +const label = '提示'; +const name = 'el-alert'; + +export default { + icon: 'icon-alert', + label, + name, + rule() { + return { + type: name, + props: { + title: '提示', + description: 'form-create', + type: 'success', + effect: 'dark', + }, + children: [] + }; + }, + props() { + return [{type: 'input', field: 'title', title: '标题'}, { + type: 'select', + field: 'type', + title: '主题', + options: [{label: 'success', value: 'success'}, {label: 'warning', value: 'warning'}, { + label: 'info', + value: 'info' + }, {label: 'error', value: 'error'}] + }, {type: 'input', field: 'description', title: '辅助性文字'}, { + type: 'switch', + field: 'closable', + title: '是否可关闭', + value: true + }, {type: 'switch', field: 'center', title: '文字是否居中', value: true}, { + type: 'input', + field: 'closeText', + title: '关闭按钮自定义文本' + }, {type: 'switch', field: 'showIcon', title: '是否显示图标'}, { + type: 'select', + field: 'effect', + title: '选择提供的主题', + options: [{label: 'light', value: 'light'}, {label: 'dark', value: 'dark'}] + }]; + } +}; \ No newline at end of file diff --git a/src/config/rule/button.js b/src/config/rule/button.js new file mode 100644 index 0000000..06b2a0a --- /dev/null +++ b/src/config/rule/button.js @@ -0,0 +1,54 @@ +const label = '按钮'; +const name = 'el-button'; + +export default { + icon: 'icon-button', + label, + name, + mask: false, + rule() { + return { + type: name, + props: {}, + children: ['按钮'], + }; + }, + props() { + return [{ + type: 'input', + field: 'formCreateChild', + title: '内容', + }, { + type: 'select', + field: 'size', + title: '尺寸', + options: [{label: 'large', value: 'large'}, {label: 'default', value: 'default'}, { + label: 'small', + value: 'small' + }] + }, { + type: 'select', + field: 'type', + title: '类型', + options: [{label: 'primary', value: 'primary'}, { + label: 'success', + value: 'success' + }, {label: 'warning', value: 'warning'}, {label: 'danger', value: 'danger'}, { + label: 'info', + value: 'info' + }] + }, {type: 'switch', field: 'plain', title: '是否朴素按钮'}, { + type: 'switch', + field: 'round', + title: '是否圆角按钮' + }, {type: 'switch', field: 'circle', title: '是否圆形按钮'}, { + type: 'switch', + field: 'loading', + title: '是否加载中状态' + }, {type: 'switch', field: 'disabled', title: '是否禁用状态'}, { + type: 'input', + field: 'icon', + title: '图标类名' + }]; + } +}; diff --git a/src/config/rule/cascader.js b/src/config/rule/cascader.js new file mode 100644 index 0000000..ce1fa21 --- /dev/null +++ b/src/config/rule/cascader.js @@ -0,0 +1,180 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeOptionsRule, makeRequiredRule} from '../../utils/index'; + +const label = '级联选择器'; +const name = 'cascader'; + +export default { + icon: 'icon-cascader', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + effect: { + fetch: '' + }, + props: { + options: [{ + value: 'zhinan', + label: '指南', + children: [{ + value: 'shejiyuanze', + label: '设计原则', + children: [{ + value: 'yizhi', + label: '一致' + }, { + value: 'fankui', + label: '反馈' + }, { + value: 'xiaolv', + label: '效率' + }, { + value: 'kekong', + label: '可控' + }] + }, { + value: 'daohang', + label: '导航', + children: [{ + value: 'cexiangdaohang', + label: '侧向导航' + }, { + value: 'dingbudaohang', + label: '顶部导航' + }] + }] + }, { + value: 'zujian', + label: '组件', + children: [{ + value: 'basic', + label: 'Basic', + children: [{ + value: 'layout', + label: 'Layout 布局' + }, { + value: 'color', + label: 'Color 色彩' + }, { + value: 'typography', + label: 'Typography 字体' + }, { + value: 'icon', + label: 'Icon 图标' + }, { + value: 'button', + label: 'Button 按钮' + }] + }, { + value: 'form', + label: 'Form', + children: [{ + value: 'radio', + label: 'Radio 单选框' + }, { + value: 'checkbox', + label: 'Checkbox 多选框' + }, { + value: 'input', + label: 'Input 输入框' + }, { + value: 'input-number', + label: 'InputNumber 计数器' + }, { + value: 'select', + label: 'Select 选择器' + }, { + value: 'cascader', + label: 'Cascader 级联选择器' + }, { + value: 'switch', + label: 'Switch 开关' + }, { + value: 'slider', + label: 'Slider 滑块' + }, { + value: 'time-picker', + label: 'TimePicker 时间选择器' + }, { + value: 'date-picker', + label: 'DatePicker 日期选择器' + }, { + value: 'datetime-picker', + label: 'DateTimePicker 日期时间选择器' + }, { + value: 'upload', + label: 'Upload 上传' + }, { + value: 'rate', + label: 'Rate 评分' + }, { + value: 'form', + label: 'Form 表单' + }] + }] + }] + } + }; + }, + props() { + return [ + makeRequiredRule(), + makeOptionsRule('props.options', false), + { + type: 'Object', + field: 'props', + title: '配置选项', + props: { + rule: [{ + type: 'select', + field: 'expandTrigger', + title: '次级菜单的展开方式', + options: [{label: 'click', value: 'click'}, {label: 'hover', value: 'hover'}] + }, {type: 'switch', field: 'multiple', title: '是否多选'}, { + type: 'switch', + field: 'checkStrictly', + title: '是否严格的遵守父子节点不互相关联' + }, { + type: 'switch', + field: 'emitPath', + title: '在选中节点改变时,是否返回由该节点所在的各级菜单的值所组成的数组,若设置 false,则只返回该节点的值', + value: true + }, {type: 'input', field: 'value', title: '指定选项的值为选项对象的某个属性值'}, { + type: 'input', + field: 'label', + title: '指定选项标签为选项对象的某个属性值' + }, {type: 'input', field: 'children', title: '指定选项的子选项为选项对象的某个属性值'}, { + type: 'input', + field: 'disabled', + title: '指定选项的禁用为选项对象的某个属性值' + }, {type: 'input', field: 'leaf', title: '指定选项的叶子节点的标志位为选项对象的某个属性值'}] + } + }, { + type: 'select', + field: 'size', + title: '尺寸', + options: [{label: 'large', value: 'large'}, {label: 'default', value: 'default'}, { + label: 'small', + value: 'small' + }] + }, {type: 'input', field: 'placeholder', title: '输入框占位文本'}, { + type: 'switch', + field: 'disabled', + title: '是否禁用' + }, {type: 'switch', field: 'clearable', title: '是否支持清空选项'}, { + type: 'switch', + field: 'showAllLevels', + title: '输入框中是否显示选中值的完整路径', + value: true + }, {type: 'switch', field: 'collapseTags', title: '多选模式下是否折叠Tag'}, { + type: 'input', + field: 'separator', + title: '选项分隔符' + }]; + } +}; diff --git a/src/config/rule/checkbox.js b/src/config/rule/checkbox.js new file mode 100644 index 0000000..06a7691 --- /dev/null +++ b/src/config/rule/checkbox.js @@ -0,0 +1,45 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeOptionsRule, makeRequiredRule} from '../../utils/index'; + +const label = '多选框'; +const name = 'checkbox'; + +export default { + icon: 'icon-checkbox', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + effect: { + fetch: '' + }, + props: {}, + options: [ + {value: '1', label: '选项1'}, + {value: '2', label: '选项2'}, + ] + }; + }, + props() { + return [ + makeRequiredRule(), makeOptionsRule('options'), + { + type: 'switch', + field: 'type', + title: '按钮类型', + props: {activeValue: 'button', inactiveValue: 'default'} + }, {type: 'switch', field: 'disabled', title: '是否禁用'}, { + type: 'inputNumber', + field: 'min', + title: '可被勾选的 checkbox 的最小数量' + }, {type: 'inputNumber', field: 'max', title: '可被勾选的 checkbox 的最大数量'}, { + type: 'input', + field: 'textColor', + title: '按钮形式的 Checkbox 激活时的文本颜色' + }, {type: 'input', field: 'fill', title: '按钮形式的 Checkbox 激活时的填充色和边框色'}]; + } +}; diff --git a/src/config/rule/col.js b/src/config/rule/col.js new file mode 100644 index 0000000..8a79fa5 --- /dev/null +++ b/src/config/rule/col.js @@ -0,0 +1,23 @@ +const name = 'col'; + +export default { + name, + drag: true, + dragBtn: false, + inside: true, + mask: false, + rule() { + return { + type: name, + props: {span: 12}, + children: [] + }; + }, + props() { + return [ + {type: 'slider', field: 'span', title: '栅格占据的列数', value: 12, props: {min: 0, max: 24}}, + {type: 'slider', field: 'offset', title: '栅格左侧的间隔格数', props: {min: 0, max: 24}}, + {type: 'slider', field: 'push', title: '栅格向右移动格数', props: {min: 0, max: 24}}, + {type: 'slider', field: 'pull', title: '栅格向左移动格数', props: {min: 0, max: 24}}]; + } +}; diff --git a/src/config/rule/color.js b/src/config/rule/color.js new file mode 100644 index 0000000..f3a8e4c --- /dev/null +++ b/src/config/rule/color.js @@ -0,0 +1,36 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '颜色选择器'; +const name = 'colorPicker'; + +export default { + icon: 'icon-color', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [ + makeRequiredRule(), {type: 'switch', field: 'disabled', title: '是否禁用'}, { + type: 'switch', + field: 'showAlpha', + title: '是否支持透明度选择' + }, { + type: 'select', + field: 'colorFormat', + title: '颜色的格式', + options: [{label: 'hsl', value: 'hsl'}, {label: 'hsv', value: 'hsv'}, { + label: 'hex', + value: 'hex' + }, {label: 'rgb', value: 'rgb'}] + }]; + } +}; diff --git a/src/config/rule/date.js b/src/config/rule/date.js new file mode 100644 index 0000000..80a2104 --- /dev/null +++ b/src/config/rule/date.js @@ -0,0 +1,75 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '日期选择器'; +const name = 'datePicker'; + +export default { + icon: 'icon-date', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [makeRequiredRule(), { + type: 'Struct', + field: 'pickerOptions', + title: '当前时间日期选择器特有的选项', + props: {defaultValue: {}} + }, {type: 'switch', field: 'readonly', title: '完全只读'}, { + type: 'switch', + field: 'disabled', + title: '禁用' + }, { + type: 'select', + field: 'type', + title: '显示类型', + options: [{label: 'year', value: 'year'}, {label: 'month', value: 'month'}, { + label: 'date', + value: 'date' + }, {label: 'dates', value: 'dates'}, {label: 'week', value: 'week'}, { + label: 'datetime', + value: 'datetime' + }, {label: 'datetimerange', value: 'datetimerange'}, { + label: 'daterange', + value: 'daterange' + }, {label: 'monthrange', value: 'monthrange'}] + }, {type: 'switch', field: 'editable', title: '文本框可输入', value: true}, { + type: 'switch', + field: 'clearable', + title: '是否显示清除按钮', + value: true + }, {type: 'input', field: 'placeholder', title: '非范围选择时的占位内容'}, { + type: 'input', + field: 'startPlaceholder', + title: '范围选择时开始日期的占位内容' + }, {type: 'input', field: 'endPlaceholder', title: '范围选择时结束日期的占位内容'}, { + type: 'input', + field: 'format', + title: '显示在输入框中的格式' + }, { + type: 'select', + field: 'align', + title: '对齐方式', + options: [{label: 'left', value: 'left'}, {label: 'center', value: 'center'}, { + label: 'right', + value: 'right' + }, {label: 'left', value: 'left'}] + }, {type: 'input', field: 'rangeSeparator', title: '选择范围时的分隔符'}, { + type: 'switch', + field: 'unlinkPanels', + title: '在范围选择器里取消两个日期面板之间的联动' + }, {type: 'input', field: 'prefixIcon', title: '自定义头部图标的类名'}, { + type: 'input', + field: 'clearIcon', + title: '自定义清空图标的类名' + }]; + } +}; diff --git a/src/config/rule/divider.js b/src/config/rule/divider.js new file mode 100644 index 0000000..6b39933 --- /dev/null +++ b/src/config/rule/divider.js @@ -0,0 +1,37 @@ +const label = '分割线'; +const name = 'el-divider'; + +export default { + icon: 'icon-divider', + label, + name, + rule() { + return { + type: name, + props: {}, + wrap: {show: false}, + native: false, + children: [''], + }; + }, + props() { + return [{ + type: 'select', + field: 'direction', + title: '设置分割线方向', + options: [{label: 'horizontal', value: 'horizontal'}, {label: 'vertical', value: 'vertical'}] + }, { + type: 'input', + field: 'formCreateChild', + title: '设置分割线文案', + }, { + type: 'select', + field: 'contentPosition', + title: '设置分割线文案的位置', + options: [{label: 'left', value: 'left'}, {label: 'right', value: 'right'}, { + label: 'center', + value: 'center' + }] + }]; + } +}; \ No newline at end of file diff --git a/src/config/rule/drawer.js b/src/config/rule/drawer.js new file mode 100644 index 0000000..fdcc967 --- /dev/null +++ b/src/config/rule/drawer.js @@ -0,0 +1,27 @@ +const label = "浮层面板"; +const name = "a-drawer"; + +export default { + icon: "icon-row", + label, + name, + drag: true, + dragBtn: false, + mask: false, + rule() { + return { + type : name, + props : { + title:"Create a new account", + width:"720", + visible:false, + }, + children: [] + }; + }, + props() { + return [ + { type: "input", field: "width", title: "宽度" }, + ]; + } +}; diff --git a/src/config/rule/editor.js b/src/config/rule/editor.js new file mode 100644 index 0000000..e41b676 --- /dev/null +++ b/src/config/rule/editor.js @@ -0,0 +1,23 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '富文本框'; +const name = 'fc-editor'; + +export default { + icon: 'icon-editor', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [makeRequiredRule(), {type: 'switch', field: 'disabled', title: '是否禁用'}]; + } +}; diff --git a/src/config/rule/image.ts b/src/config/rule/image.ts new file mode 100644 index 0000000..e4a929a --- /dev/null +++ b/src/config/rule/image.ts @@ -0,0 +1,57 @@ +// @ts-ignore +// import FcDesigner from "@form-create/ant-design-vue"; +import {makeRequiredRule,makeOptionsRule} from '../../utils'; +const label = "图片"; +const name = "a-image"; +const icon = "icon-xingzhuang-tupian"; +let i = 1; +const uniqueId = () => `uni${i++}`; + +export default { + //拖拽组件的图标 + icon, + //拖拽组件的名称 + label, + //拖拽组件的 key + name, + //拖拽组件的生成规则 + rule() { + //如果在 props 方法中需要修改 rule 的属性,需要提前在 rule 上定义对应的属性 + return { + //生成组件的名称 + type: name, + //field 自定不能重复,所以这里每次都会生成一个新的 + field: uniqueId(), + title: label, + info: "", + effect: { + fetch: "" + }, + //这里设置组件的默认props配置项, 在下面的 props 方法里面设置无效 + props: { + src: "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png", + width: 200, + placeholder: true, + }, + options: [ + + ], + }; + }, + + //拖拽组件配置项(props)的生成规则 + props() { + return [ + //生成`checkbox`组件的`options`配置规则 + // makeRequiredRule(), + // makeOptionsRule("props"), + { type: "input", field: "src", title: "图片地址(支持http/base64)" }, + { type: "input", field: "fallback", title: "加载失败容错地址" }, + { type: "input", field: "width", title: "图像宽度" }, + { type: "input", field: "height", title: "图像高度" }, + { type: "switch", field: "preview", title: "预览参数,为 false 时禁用" }, + { type: "switch", field: "placeholder", title: "加载占位, 为 true 时使用默认占位"}, + { type: "input", field: "alt", title: "图像描述"}, + ]; + } +}; diff --git a/src/config/rule/index.js b/src/config/rule/index.js new file mode 100644 index 0000000..aa9b56b --- /dev/null +++ b/src/config/rule/index.js @@ -0,0 +1,61 @@ +import radio from './radio'; +import checkbox from './checkbox'; +import input from './input'; +import number from './number'; +import select from './select'; +import _switch from './switch'; +import slider from './slider'; +import time from './time'; +import date from './date'; +import rate from './rate'; +import color from './color'; +import row from './row'; +import col from './col'; +import tabPane from './tabPane'; +import divider from './divider'; +import cascader from './cascader'; +import upload from './upload'; +import transfer from './transfer'; +import tree from './tree'; +import alert from './alert'; +import span from './span'; +import space from './space'; +import tab from './tab'; +import button from './button'; +import editor from './editor'; +import drawer from './drawer'; +import image from './image.ts'; + +// 还需要在../menu.js文件里注册 + +const ruleList = { + [drawer.name]: drawer, + [radio.name]: radio, + [checkbox.name]: checkbox, + [input.name]: input, + [number.name]: number, + [select.name]: select, + [_switch.name]: _switch, + [slider.name]: slider, + [time.name]: time, + [date.name]: date, + [rate.name]: rate, + [color.name]: color, + [row.name]: row, + [col.name]: col, + [tab.name]: tab, + [tabPane.name]: tabPane, + [divider.name]: divider, + [cascader.name]: cascader, + [upload.name]: upload, + [transfer.name]: transfer, + [tree.name]: tree, + [alert.name]: alert, + [span.name]: span, + [space.name]: space, + [button.name]: button, + [editor.name]: editor, + [image.name]: image, +}; + +export default ruleList; diff --git a/src/config/rule/input.js b/src/config/rule/input.js new file mode 100644 index 0000000..6c935fd --- /dev/null +++ b/src/config/rule/input.js @@ -0,0 +1,65 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '输入框'; +const name = 'input'; + +export default { + icon: 'icon-input', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {} + }; + }, + props() { + return [makeRequiredRule(), { + type: 'select', + field: 'type', + title: '类型', + options: [{label: 'text', value: 'text'}, { + label: 'textarea', + value: 'textarea' + }, {label: 'number', value: 'number'}, {label: 'password', value: 'password'}] + }, {type: 'inputNumber', field: 'maxlength', title: '最大输入长度'}, { + type: 'inputNumber', + field: 'minlength', + title: '最小输入长度' + }, {type: 'switch', field: 'showWordLimit', title: '是否显示输入字数统计'}, { + type: 'input', + field: 'placeholder', + title: '输入框占位文本' + }, {type: 'switch', field: 'clearable', title: '是否可清空'}, { + type: 'switch', + field: 'showPassword', + title: '是否显示切换密码图标' + }, {type: 'switch', field: 'disabled', title: '禁用'}, { + type: 'input', + field: 'prefixIcon', + title: '输入框头部图标' + }, {type: 'input', field: 'suffixIcon', title: '输入框尾部图标'}, { + type: 'inputNumber', + field: 'rows', + info: '只对 type="textarea" 有效', + title: '输入框行数' + }, { + type: 'select', + field: 'autocomplete', + title: '自动补全', + options: [{label: 'on', value: 'on'}, {label: 'off', value: 'off'}] + }, {type: 'switch', field: 'readonly', title: '是否只读'}, { + type: 'select', + field: 'resize', + title: '控制是否能被用户缩放', + options: [{label: 'none', value: 'none'}, {label: 'both', value: 'both'}, { + label: 'horizontal', + value: 'horizontal' + }, {label: 'vertical', value: 'vertical'}] + }, {type: 'switch', field: 'autofocus', title: '自动获取焦点'}]; + } +}; diff --git a/src/config/rule/number.js b/src/config/rule/number.js new file mode 100644 index 0000000..55937bf --- /dev/null +++ b/src/config/rule/number.js @@ -0,0 +1,41 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '计数器'; +const name = 'inputNumber'; + +export default { + icon: 'icon-number', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {} + }; + }, + props() { + return [makeRequiredRule(), {type: 'inputNumber', field: 'min', title: '设置计数器允许的最小值'}, { + type: 'inputNumber', + field: 'max', + title: '设置计数器允许的最大值' + }, {type: 'inputNumber', field: 'step', title: '计数器步长'}, { + type: 'switch', + field: 'stepStrictly', + title: '是否只能输入 step 的倍数' + }, {type: 'switch', field: 'disabled', title: '是否禁用计数器'}, { + type: 'switch', + field: 'controls', + title: '是否使用控制按钮', + value: true + }, { + type: 'select', + field: 'controlsPosition', + title: '控制按钮位置', + options: [{label: 'default', value: ''}, {label: 'right', value: 'right'}] + }, {type: 'input', field: 'placeholder', title: '输入框默认 placeholder'}]; + } +}; diff --git a/src/config/rule/radio.js b/src/config/rule/radio.js new file mode 100644 index 0000000..85551f0 --- /dev/null +++ b/src/config/rule/radio.js @@ -0,0 +1,42 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeOptionsRule, makeRequiredRule} from '../../utils/index'; + +const label = '单选框'; +const name = 'radio'; + +export default { + icon: 'icon-radio', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + effect: { + fetch: '' + }, + props: {}, + options: [ + {value: '1', label: '选项1'}, + {value: '2', label: '选项2'}, + ] + }; + }, + props() { + return [ + makeRequiredRule(), + makeOptionsRule('options'), + {type: 'switch', field: 'disabled', title: '是否禁用'}, { + type: 'switch', + field: 'type', + title: '按钮形式', + props: {activeValue: 'button', inactiveValue: 'default'} + }, {type: 'input', field: 'textColor', title: '按钮形式的 Radio 激活时的文本颜色'}, { + type: 'input', + field: 'fill', + title: '按钮形式的 Radio 激活时的填充色和边框色' + }]; + } +}; diff --git a/src/config/rule/rate.js b/src/config/rule/rate.js new file mode 100644 index 0000000..463fee8 --- /dev/null +++ b/src/config/rule/rate.js @@ -0,0 +1,44 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '评分'; +const name = 'rate'; + +export default { + icon: 'icon-rate', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [ + makeRequiredRule(), {type: 'inputNumber', field: 'max', title: '最大分值'}, { + type: 'switch', + field: 'disabled', + title: '是否为只读' + }, {type: 'switch', field: 'allowHalf', title: '是否允许半选'}, { + type: 'input', + field: 'voidColor', + title: '未选中 icon 的颜色' + }, {type: 'input', field: 'disabledVoidColor', title: '只读时未选中 icon 的颜色'}, { + type: 'input', + field: 'voidIconClass', + title: '未选中 icon 的类名' + }, {type: 'input', field: 'disabledVoidIconClass', title: '只读时未选中 icon 的类名'}, { + type: 'switch', + field: 'showScore', + title: '是否显示当前分数,show-score 和 show-text 不能同时为真' + }, {type: 'input', field: 'textColor', title: '辅助文字的颜色'}, { + type: 'input', + field: 'scoreTemplate', + title: '分数显示模板' + }]; + } +}; diff --git a/src/config/rule/row.js b/src/config/rule/row.js new file mode 100644 index 0000000..eca88d8 --- /dev/null +++ b/src/config/rule/row.js @@ -0,0 +1,41 @@ +const label = '栅格布局'; +const name = 'row'; + +export default { + icon: 'icon-row', + label, + name, + mask: false, + rule() { + return { + type: 'FcRow', + props: {}, + children: [] + }; + }, + children: 'col', + props() { + return [{type: 'inputNumber', field: 'gutter', title: '栅格间隔'}, { + type: 'switch', + field: 'type', + title: 'flex布局模式', + props: {activeValue: 'flex', inactiveValue: 'default'} + }, { + type: 'select', + field: 'justify', + title: 'flex 布局下的水平排列方式', + options: [{label: 'start', value: 'start'}, {label: 'end', value: 'end'}, { + label: 'center', + value: 'center' + }, {label: 'space-around', value: 'space-around'}, {label: 'space-between', value: 'space-between'}] + }, { + type: 'select', + field: 'align', + title: 'flex 布局下的垂直排列方式', + options: [{label: 'top', value: 'top'}, {label: 'middle', value: 'middle'}, { + label: 'bottom', + value: 'bottom' + }] + }]; + } +}; diff --git a/src/config/rule/select.js b/src/config/rule/select.js new file mode 100644 index 0000000..acfc1af --- /dev/null +++ b/src/config/rule/select.js @@ -0,0 +1,62 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeOptionsRule, makeRequiredRule} from '../../utils/index'; + +const label = '选择器'; +const name = 'select'; + +export default { + icon: 'icon-select', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + effect: { + fetch: '' + }, + props: {}, + options: [ + {value: '1', label: '选项1'}, + {value: '2', label: '选项2'}, + ] + }; + }, + props() { + return [ + makeRequiredRule(), + makeOptionsRule('options'), + {type: 'switch', field: 'multiple', title: '是否多选'}, { + type: 'switch', + field: 'disabled', + title: '是否禁用' + }, {type: 'switch', field: 'clearable', title: '是否可以清空选项'}, { + type: 'switch', + field: 'collapseTags', + title: '多选时是否将选中值按文字的形式展示' + }, {type: 'inputNumber', field: 'multipleLimit', title: '多选时用户最多可以选择的项目数,为 0 则不限制'}, { + type: 'input', + field: 'autocomplete', + title: 'autocomplete 属性' + }, {type: 'input', field: 'placeholder', title: '占位符'}, { + type: 'switch', + field: 'filterable', + title: '是否可搜索' + }, {type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目'}, { + type: 'input', + field: 'noMatchText', + title: '搜索条件无匹配时显示的文字' + }, {type: 'input', field: 'noDataText', title: '选项为空时显示的文字'}, { + type: 'switch', + field: 'reserveKeyword', + title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词' + }, {type: 'switch', field: 'defaultFirstOption', title: '在输入框按下回车,选择第一个匹配项'}, { + type: 'switch', + field: 'popperAppendToBody', + title: '是否将弹出框插入至 body 元素', + value: true + }, {type: 'switch', field: 'automaticDropdown', title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单'}]; + } +}; diff --git a/src/config/rule/slider.js b/src/config/rule/slider.js new file mode 100644 index 0000000..c3cf4ca --- /dev/null +++ b/src/config/rule/slider.js @@ -0,0 +1,44 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '滑块'; +const name = 'slider'; + +export default { + icon: 'icon-slider', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [makeRequiredRule(), {type: 'inputNumber', field: 'min', title: '最小值'}, { + type: 'inputNumber', + field: 'max', + title: '最大值' + }, {type: 'switch', field: 'disabled', title: '是否禁用'}, { + type: 'inputNumber', + field: 'step', + title: '步长' + }, {type: 'switch', field: 'showInput', title: '是否显示输入框,仅在非范围选择时有效'}, { + type: 'switch', + field: 'showInputControls', + title: '在显示输入框的情况下,是否显示输入框的控制按钮', + value: true + }, {type: 'switch', field: 'showStops', title: '是否显示间断点'}, { + type: 'switch', + field: 'range', + title: '是否为范围选择' + }, {type: 'switch', field: 'vertical', title: '是否竖向模式'}, { + type: 'input', + field: 'height', + title: 'Slider 高度,竖向模式时必填' + }]; + } +}; diff --git a/src/config/rule/space.js b/src/config/rule/space.js new file mode 100644 index 0000000..86c1f63 --- /dev/null +++ b/src/config/rule/space.js @@ -0,0 +1,41 @@ +const label = '间距'; +const name = 'div'; + +export default { + icon: 'icon-space', + label, + name, + rule() { + return { + type: name, + wrap: { + show: false + }, + native: false, + style: { + width: '100%', + height: '20px', + }, + children: [] + }; + }, + props() { + return [ + { + type: 'object', + field: 'formCreateStyle', + native: true, + props: { + rule: [ + { + type: 'input', + field: 'height', + title: 'height', + }, + ] + } + } + + ]; + } +}; \ No newline at end of file diff --git a/src/config/rule/span.js b/src/config/rule/span.js new file mode 100644 index 0000000..6d983e0 --- /dev/null +++ b/src/config/rule/span.js @@ -0,0 +1,33 @@ +const label = '文字'; +const name = 'span'; + +export default { + icon: 'icon-span', + label, + name, + rule() { + return { + type: name, + title: '文字', + native: false, + children: ['这是一段文字'], + }; + }, + props() { + return [ + { + type: 'input', + field: 'formCreateTitle', + title: 'title', + }, + { + type: 'input', + field: 'formCreateChild', + title: '内容', + props: { + type: 'textarea' + } + } + ]; + } +}; \ No newline at end of file diff --git a/src/config/rule/switch.js b/src/config/rule/switch.js new file mode 100644 index 0000000..ca65ba3 --- /dev/null +++ b/src/config/rule/switch.js @@ -0,0 +1,39 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '开关'; +const name = 'switch'; + +export default { + icon: 'icon-switch', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [makeRequiredRule(), {type: 'switch', field: 'disabled', title: '是否禁用'}, { + type: 'inputNumber', + field: 'width', + title: '宽度(px)' + }, {type: 'input', field: 'activeText', title: 'switch 打开时的文字描述'}, { + type: 'input', + field: 'inactiveText', + title: 'switch 关闭时的文字描述' + }, {type: 'input', field: 'activeValue', title: 'switch 打开时的值'}, { + type: 'input', + field: 'inactiveValue', + title: 'switch 关闭时的值' + }, {type: 'input', field: 'activeColor', title: 'switch 打开时的背景色'}, { + type: 'input', + field: 'inactiveColor', + title: 'switch 关闭时的背景色' + }]; + } +}; diff --git a/src/config/rule/tab.js b/src/config/rule/tab.js new file mode 100644 index 0000000..95a7b8a --- /dev/null +++ b/src/config/rule/tab.js @@ -0,0 +1,36 @@ +const label = '标签页'; +const name = 'tab'; + +export default { + icon: 'icon-tab', + label, + name, + children: 'tab-pane', + mask: false, + rule() { + return { + type: 'el-tabs', + style:'width:100%;', + children: [] + }; + }, + props() { + return [{ + type: 'select', + field: 'type', + title: '风格类型', + options: [{ + label: 'card', + value: 'card' + }, {label: 'border-card', value: 'border-card'}] + }, {type: 'switch', field: 'closable', title: '标签是否可关闭'}, { + type: 'select', + field: 'tabPosition', + title: '选项卡所在位置', + options: [{label: 'top', value: 'top'}, {label: 'right', value: 'right'}, { + label: 'left', + value: 'left' + }] + }, {type: 'switch', field: 'stretch', title: '标签的宽度是否自撑开'}]; + } +}; diff --git a/src/config/rule/tabPane.js b/src/config/rule/tabPane.js new file mode 100644 index 0000000..6656e58 --- /dev/null +++ b/src/config/rule/tabPane.js @@ -0,0 +1,29 @@ +const label = '标签页'; +const name = 'tab-pane'; + +export default { + label, + name, + inside: true, + drag: true, + dragBtn: false, + mask: false, + rule() { + return { + type: 'el-tab-pane', + props: {label: '新标签页'}, + children: [] + }; + }, + props() { + return [{type: 'input', field: 'label', title: '选项卡标题'}, { + type: 'switch', + field: 'disabled', + title: '是否禁用' + }, {type: 'input', field: 'name', title: '与选项卡绑定值 value 对应的标识符,表示选项卡别名'}, { + type: 'switch', + field: 'lazy', + title: '标签是否延迟渲染' + }]; + } +}; diff --git a/src/config/rule/time.js b/src/config/rule/time.js new file mode 100644 index 0000000..cda6b48 --- /dev/null +++ b/src/config/rule/time.js @@ -0,0 +1,57 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '时间选择器'; +const name = 'timePicker'; + +export default { + icon: 'icon-time', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: {}, + }; + }, + props() { + return [makeRequiredRule(), { + type: 'Struct', + field: 'pickerOptions', + title: '当前时间日期选择器特有的选项', + props: {defaultValue: {}} + }, {type: 'switch', field: 'readonly', title: '完全只读'}, { + type: 'switch', + field: 'disabled', + title: '禁用' + }, {type: 'switch', field: 'editable', title: '文本框可输入', value: true}, { + type: 'switch', + field: 'clearable', + title: '是否显示清除按钮', + value: true + }, {type: 'input', field: 'placeholder', title: '非范围选择时的占位内容'}, { + type: 'input', + field: 'startPlaceholder', + title: '范围选择时开始日期的占位内容' + }, {type: 'input', field: 'endPlaceholder', title: '范围选择时开始日期的占位内容'}, { + type: 'switch', + field: 'isRange', + title: '是否为时间范围选择' + }, {type: 'switch', field: 'arrowControl', title: '是否使用箭头进行时间选择'}, { + type: 'select', + field: 'align', + title: '对齐方式', + options: [{label: 'left', value: 'left'}, {label: 'center', value: 'center'}, { + label: 'right', + value: 'right' + }] + }, {type: 'input', field: 'prefixIcon', title: '自定义头部图标的类名'}, { + type: 'input', + field: 'clearIcon', + title: '自定义清空图标的类名' + }]; + } +}; diff --git a/src/config/rule/transfer.js b/src/config/rule/transfer.js new file mode 100644 index 0000000..0a99bc8 --- /dev/null +++ b/src/config/rule/transfer.js @@ -0,0 +1,84 @@ +import uniqueId from '@form-create/utils/lib/unique'; + +const label = '穿梭框'; +const name = 'el-transfer'; + +const generateData = _ => { + const data = []; + for (let i = 1; i <= 15; i++) { + data.push({ + key: i, + label: `备选项 ${i}`, + disabled: i % 4 === 0 + }); + } + return data; +}; + +export default { + icon: 'icon-transfer', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: { + data: generateData() + } + }; + }, + props() { + return [{ + type: 'Struct', + field: 'data', + title: 'Transfer 的数据源', + props: {defaultValue: []} + }, {type: 'switch', field: 'filterable', title: '是否可搜索'}, { + type: 'input', + field: 'filterPlaceholder', + title: '搜索框占位符' + }, { + type: 'select', + field: 'targetOrder', + title: '右侧列表元素的排序策略', + info: '若为 original,则保持与数据源相同的顺序;若为 push,则新加入的元素排在最后;若为 unshift,则新加入的元素排在最前', + options: [{label: 'original', value: 'original'}, { + label: 'push', + value: 'push' + }, {label: 'unshift', value: 'unshift'}] + }, { + type: 'Struct', + field: 'titles', + title: '自定义列表标题', + props: {defaultValue: []} + }, { + type: 'Struct', + field: 'buttonTexts', + title: '自定义按钮文案', + props: {defaultValue: []} + }, { + type: 'Struct', + field: 'format', + title: '列表顶部勾选状态文案', + props: {defaultValue: {}} + }, { + type: 'Struct', + field: 'props', + title: '数据源的字段别名', + props: {defaultValue: {}} + }, { + type: 'Struct', + field: 'leftDefaultChecked', + title: '初始状态下左侧列表的已勾选项的 key 数组', + props: {defaultValue: []} + }, { + type: 'Struct', + field: 'rightDefaultChecked', + title: '初始状态下右侧列表的已勾选项的 key 数组', + props: {defaultValue: []} + }]; + } +}; \ No newline at end of file diff --git a/src/config/rule/tree.js b/src/config/rule/tree.js new file mode 100644 index 0000000..5184b7e --- /dev/null +++ b/src/config/rule/tree.js @@ -0,0 +1,100 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeOptionsRule, makeRequiredRule} from '../../utils/index'; + +const label = '树形控件'; +const name = 'tree'; + +export default { + icon: 'icon-tree', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + effect: { + fetch: '' + }, + props: { + props: { + label: 'label', + }, + showCheckbox: true, + nodeKey: 'id', + data: [{ + id: 1, + label: '一级 1', + children: [{ + id: 4, + label: '二级 1-1', + children: [{ + id: 9, + label: '三级 1-1-1' + }, { + id: 10, + label: '三级 1-1-2' + }] + }] + }, { + id: 2, + label: '一级 2', + children: [{ + id: 5, + label: '二级 2-1' + }, { + id: 6, + label: '二级 2-2' + }] + }, { + id: 3, + label: '一级 3', + children: [{ + id: 7, + label: '二级 3-1' + }, { + id: 8, + label: '二级 3-2' + }] + }] + }, + }; + }, + props() { + return [ + makeRequiredRule(), + makeOptionsRule('props.data', false), + {type: 'input', field: 'emptyText', title: '内容为空的时候展示的文本'}, { + type: 'Struct', + field: 'props', + title: '配置选项,具体看下表', + props: {defaultValue: {}} + }, {type: 'switch', field: 'renderAfterExpand', title: '是否在第一次展开某个树节点后才渲染其子节点', value: true}, { + type: 'switch', + field: 'defaultExpandAll', + title: '是否默认展开所有节点', + }, { + type: 'switch', + field: 'expandOnClickNode', + title: '是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。', + value: true + }, { + type: 'switch', + field: 'checkOnClickNode', + title: '是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。' + }, {type: 'switch', field: 'autoExpandParent', title: '展开子节点的时候是否自动展开父节点', value: true}, { + type: 'switch', + field: 'checkStrictly', + title: '在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false' + }, {type: 'switch', field: 'accordion', title: '是否每次只打开一个同级树节点展开'}, { + type: 'inputNumber', + field: 'indent', + title: '相邻级节点间的水平缩进,单位为像素' + }, {type: 'input', field: 'iconClass', title: '自定义树节点的图标'}, { + type: 'input', + field: 'nodeKey', + title: '每个树节点用来作为唯一标识的属性,整棵树应该是唯一的' + }]; + } +}; diff --git a/src/config/rule/upload.js b/src/config/rule/upload.js new file mode 100644 index 0000000..cb49be4 --- /dev/null +++ b/src/config/rule/upload.js @@ -0,0 +1,64 @@ +import uniqueId from '@form-create/utils/lib/unique'; +import {makeRequiredRule} from '../../utils'; + +const label = '上传'; +const name = 'upload'; + +export default { + icon: 'icon-upload', + label, + name, + rule() { + return { + type: name, + field: uniqueId(), + title: label, + info: '', + props: { + action: '', + onSuccess(res, file) { + file.url = res.data.url; + } + } + }; + }, + props() { + return [makeRequiredRule(), { + type: 'select', + field: 'uploadType', + title: '上传类型', + value: 'image', + options: [{label: '图片', value: 'image'}, { + label: '文件', + value: 'file' + }] + }, {type: 'input', field: 'action', title: '上传的地址(必填)'}, { + type: 'Struct', + field: 'headers', + title: '设置上传的请求头部', + props: {defaultValue: {}} + }, {type: 'switch', field: 'multiple', title: '是否支持多选文件'}, { + type: 'Struct', + field: 'data', + title: '上传时附带的额外参数', + props: {defaultValue: {}} + }, {type: 'input', field: 'name', title: '上传的文件字段名'}, { + type: 'switch', + field: 'withCredentials', + title: '支持发送 cookie 凭证信息' + }, {type: 'input', field: 'accept', title: '接受上传的文件类型(thumbnail-mode 模式下此参数无效)'}, { + type: 'switch', + field: 'autoUpload', + title: '是否在选取文件后立即进行上传', + value: true + }, { + type: 'switch', + field: 'disabled', + title: '是否禁用' + }, { + type: 'inputNumber', + field: 'limit', + title: '最大允许上传个数' + }]; + } +}; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..823bc7a --- /dev/null +++ b/src/index.js @@ -0,0 +1,56 @@ +import FcDesigner from './components/FcDesigner.vue'; +import DragTool from './components/DragTool.vue'; +import Struct from './components/Struct.vue'; +import Fetch from './components/Fetch.vue'; +import Validate from './components/Validate.vue'; +import DragBox from './components/DragBox.vue'; +import Required from './components/Required.vue'; +import TableOptions from './components/TableOptions.vue'; +import {designerForm} from './utils/form'; +import FcEditor from '@form-create/component-wangeditor'; +import './style/index.css'; +import draggable from 'vuedraggable/src/vuedraggable'; +import unique from '@form-create/utils/lib/unique'; +import {makeOptionsRule} from './utils/index'; +import formCreate from './utils/form'; + +designerForm.component('draggable', draggable); +designerForm.component('DragTool', DragTool); +designerForm.component('DragBox', DragBox); +designerForm.component('Validate', Validate); +designerForm.component('Struct', Struct); +designerForm.component('Fetch', Fetch); +designerForm.component('Required', Required); +designerForm.component('TableOptions', TableOptions); +designerForm.component('FcEditor', FcEditor); +formCreate.component('FcEditor', FcEditor); + +designerForm.register('_fc', { + init(fc, rule) { + rule._id = unique(); + if (fc.repeat) + rule.field = unique(); + if (fc.value) { + rule.effect._fc = false; + } + } +}); + +designerForm.register('_fc_tool', { + init(_, rule) { + rule.props.unique = unique(); + } +}); + +const install = function (Vue) { + Vue.component('FcDesigner', FcDesigner); +}; + +FcDesigner.install = install; +FcDesigner.makeOptionsRule = makeOptionsRule; +FcDesigner.formCreate = formCreate; +FcDesigner.designerForm = designerForm; + +export default FcDesigner; + +export {formCreate, designerForm, install}; diff --git a/src/style/fonts/fc-icons.woff b/src/style/fonts/fc-icons.woff new file mode 100644 index 0000000000000000000000000000000000000000..830e21fff4163e32f4287fcb15246389fc6937b2 GIT binary patch literal 5448 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 literal 0 HcmV?d00001 diff --git a/src/style/index.css b/src/style/index.css new file mode 100644 index 0000000..270412e --- /dev/null +++ b/src/style/index.css @@ -0,0 +1,138 @@ +@font-face { + font-family: "fc-icon"; + src: url(fonts/fc-icons.woff) format('woff'); +} +@font-face { + font-family: "fc-icon"; /* Project id */ + src: url('fonts/iconfont.ttf') format('truetype'); +} +.fc-icon { + font-family: "fc-icon" !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.icon-xingzhuang-tupian:before { + content: "\eb98"; +} +.icon-add-child:before { + content: "\e789"; +} + +.icon-switch:before { + content: "\e77c"; +} + +.icon-tab:before { + content: "\e77b"; +} + +.icon-button:before { + content: "\e77e"; +} + +.icon-input:before { + content: "\e77f"; +} + +.icon-checkbox:before { + content: "\e780"; +} + +.icon-radio:before { + content: "\e781"; +} + +.icon-rate:before { + content: "\e782"; +} + +.icon-number:before { + content: "\e783"; +} + +.icon-upload:before { + content: "\e784"; +} + +.icon-cascader:before { + content: "\e785"; +} + +.icon-space:before { + content: "\e786"; +} + +.icon-color:before { + content: "\e787"; +} + +.icon-span:before { + content: "\e788"; +} + +.icon-alert:before { + content: "\e78a"; +} + +.icon-row:before { + content: "\e78b"; +} + +.icon-divider:before { + content: "\e78d"; +} + +.icon-select:before { + content: "\e78e"; +} + +.icon-transfer:before { + content: "\e78f"; +} + +.icon-editor:before { + content: "\e790"; +} + +.icon-slider:before { + content: "\e791"; +} + +.icon-tree:before { + content: "\e792"; +} + +.icon-date:before { + content: "\e793"; +} + +.icon-time:before { + content: "\e794"; +} + +.icon-delete:before { + content: "\e770"; +} + +.icon-copy:before { + content: "\e771"; +} + +.icon-import:before { + content: "\e773"; +} + +.icon-add:before { + content: "\e774"; +} + +.icon-preview:before { + content: "\e776"; +} + +.icon-move:before { + content: "\e777"; +} + diff --git a/src/utils/form.js b/src/utils/form.js new file mode 100644 index 0000000..8a181c1 --- /dev/null +++ b/src/utils/form.js @@ -0,0 +1,9 @@ +import formCreate from '@form-create/element-ui'; + +const viewForm = formCreate; + +const designerForm = formCreate.factory(); + +export default viewForm; + +export {designerForm}; diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..c6bfea2 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,132 @@ +import is, {hasProperty} from '@form-create/utils/lib/type'; +import {parseFn} from '@form-create/utils/lib/json'; + +export function makeRequiredRule() { + return { + type: 'Required', field: 'formCreate$required', title: '是否必填' + }; +} + +export function makeOptionsRule(to, flag) { + const options = [ + {'label': 'JSON数据', 'value': 0}, + {'label': '接口数据', 'value': 1}, + ]; + + const control = [ + { + value: 0, + rule: [ + { + type: 'Struct', + field: 'formCreate' + upper(to).replace('.', '>'), + props: {defaultValue: []} + }, + ], + }, + { + value: 1, + rule: [ + { + type: 'Fetch', + field: 'formCreateEffect>fetch', + props: { + to + } + } + ] + } + ]; + + if (flag !== false) { + options.splice(0, 0, {'label': '静态数据', 'value': 2}); + control.push({ + value: 2, + rule: [ + { + type: 'TableOptions', + field: 'formCreate' + upper(to).replace('.', '>'), + props: {defaultValue: []} + }, + ], + }); + } + + return { + type: 'radio', + title: '选项数据', + field: '_optionType', + value: flag !== false ? 2 : 0, + options, + props: { + type: 'button' + }, + control + }; +} + +export function upper(str) { + return str.replace(str[0], str[0].toLocaleUpperCase()); +} + + +export const toJSON = function (val) { + const type = /object ([a-zA-Z]*)/.exec(Object.prototype.toString.call(val)); + if (type && _toJSON[type[1].toLowerCase()]) { + return _toJSON[type[1].toLowerCase()](val); + } else { + return val; + } +}; + +const _toJSON = { + object: function (val) { + var json = []; + for (var i in val) { + if (!hasProperty(val, i)) continue; + json.push( + toJSON(i) + ': ' + + ((val[i] != null) ? toJSON(val[i]) : 'null') + ); + } + return '{\n ' + json.join(',\n ') + '\n}'; + }, + array: function (val) { + for (var i = 0, json = []; i < val.length; i++) + json[i] = (val[i] != null) ? toJSON(val[i]) : 'null'; + return '[' + json.join(', ') + ']'; + }, + string: function (val) { + var tmp = val.split(''); + for (var i = 0; i < tmp.length; i++) { + var c = tmp[i]; + (c >= ' ') ? + (c === '\\') ? (tmp[i] = '\\\\') : + (c === '"') ? (tmp[i] = '\\"') : 0 : + (tmp[i] = + (c === '\n') ? '\\n' : + (c === '\r') ? '\\r' : + (c === '\t') ? '\\t' : + (c === '\b') ? '\\b' : + (c === '\f') ? '\\f' : + (c = c.charCodeAt(), ('\\u00' + ((c > 15) ? 1 : 0) + (c % 16))) + ); + } + return '"' + tmp.join('') + '"'; + } +}; + +export const deepParseFn = function (target) { + for (let key in target) { + if (Object.prototype.hasOwnProperty.call(target, key)) { + let data = target[key]; + if (Array.isArray(data) || is.Object(data)) { + deepParseFn(data); + } + if (is.String(data)) { + target[key] = parseFn(data); + } + } + } + return target; +}; diff --git a/tools/cli/run.ts b/tools/cli/run.ts new file mode 100644 index 0000000..89122da --- /dev/null +++ b/tools/cli/run.ts @@ -0,0 +1,61 @@ +/* + * @Author : djkloop + * @Date : 2021-09-15 15:13:17 + * @LastEditors : djkloop + * @LastEditTime : 2021-09-19 16:10:22 + * @Description : 头部注释 + * @FilePath : /form-create2/tools/cli/run.ts + */ +import {program} from 'commander'; +import path from 'path'; +import chalk from 'chalk'; +import build from '../lib/build'; +// pkgUrl +const log = console.log +const commandExecUrl = process.cwd() +log() +log(' ' + chalk.green('command run path with: ', commandExecUrl)) +log() +const pkgUrl = path.join(commandExecUrl, '/lerna.json'); + +program.on('--help', () => { + log(); + log(chalk.blue.bold(' Usage:'), chalk.cyan.bold('tools build tools with Node 👍 ~')); + log(); +}) + +/// tools 版本 +program + .version(`@form-create/tools v${require(pkgUrl).version}`, '-v, --version', 'tools versions') + +program + .command('build [flag]') + .description('build components && packages || build components || build packages') + .option('-a,--all', 'Build @form-create/[all]packages') /// 默认打包components和packages + .option('-c, --components ', 'Build @form-create/component- package or packages array') // 打单独的组件 + .option('-p, --packages ', 'Build @form-create/ package or packages array') // 打单独的包 + .action((_: any, cmd: any) => build(_, cleanArgs(cmd))); + +program.parse(process.argv); + + + +// code with vue-cli: https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli/bin/vue.js#L275 +function camelize(str: string) { + return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : '') +} + +// commander passes the Command object itself as options, +// extract only actual options into a fresh object. +function cleanArgs(cmd: any) { + const args: any = {} + cmd.options.forEach((o: any) => { + const key = camelize(o.long.replace(/^--/, '')) + // if an option is not present and Command has a method with the same name + // it should not be copied + if (typeof cmd[key] !== 'function' && typeof cmd[key] !== 'undefined') { + args[key] = cmd[key] + } + }) + return args +} \ No newline at end of file diff --git a/tools/lib/build.ts b/tools/lib/build.ts new file mode 100644 index 0000000..450cbad --- /dev/null +++ b/tools/lib/build.ts @@ -0,0 +1,163 @@ +/* + * @Author : djkloop + * @Date : 2020-08-15 17:21:22 + * @LastEditors : djkloop + * @LastEditTime : 2021-10-04 19:33:38 + * @Description : 头部注释 + * @FilePath : /form-create2/tools/lib/build.ts + */ +import chalk from 'chalk'; +import ora from 'ora'; +import dayjs from 'dayjs'; +import fs from 'fs'; +import { exit } from 'process'; +import shell from 'shelljs'; +import createBuildComponents from './components'; +import createBuildPackages from './packages'; +import { blue, getFolderNames, getSingleComponentPaths, getSinglePackagePaths, red, targets as getAllTargetsPath, yellow } from './utils' + + +let spinner +const log = console.log +const context = process.cwd() + + +async function buildAllComponents(paths: string[] = []) { + let componentsPathsObject = Object.create(null) + paths.forEach((comPath: string) => { + const [name, cname] = comPath.split('/') + const componentsPaths = getSingleComponentPaths('components', name, cname) + + if (!componentsPathsObject[name]) { + componentsPathsObject[name] = componentsPaths[name] + } else { + componentsPathsObject[name] = [...componentsPathsObject[name], ...componentsPaths[name]] + } + }); + await createBuildComponents(componentsPathsObject) + await exit(); +} + + +async function createBuildTask(args: any) { + const isAll = args.all || Object.keys(args).length === 0 + const isComponentsAll = args?.components?.includes('all') + const isPackagesAll = args?.packages?.includes('all') + let tips = ''; + if (isAll) { + tips = 'build all components and all packages' + } else if (isComponentsAll) { + tips = 'build all components' + } else if (isPackagesAll) { + tips = 'build all packages' + } else { + tips = ' build task time' + ' ' + dayjs().format('YYYY-MM-DD HH:mm:ss') + '\n' + } + yellow(tips, true) + blue(' ---------------------- start ----------------------\n', true) + /// not all components + if (!isComponentsAll && args?.components?.length) { + const libs = args?.components + if (libs.length === 1) { + /// 先切包名 + const lib = libs[0]; + /// 单包下存在多个包 + if (lib.includes('*') || !lib.includes('/')) { + /// xxx/* + const [name] = !lib.includes('/') ? [lib] : lib.split('/') + const _p = getFolderNames('components', name) + buildAllComponents(_p) + } else { + const [name, cname] = lib.split('/') + const componentsPaths = getSingleComponentPaths('components', name, cname) + await createBuildComponents(componentsPaths) + await exit(); + } + } else { + const compsPaths = args?.components; + const allPaths = compsPaths.filter(cp => cp.includes('*') || !cp.includes('/')); + const fixPaths = compsPaths.filter(cp => !cp.includes('*') && cp.includes('/')); + let _all_ = [...fixPaths] + if (allPaths.length) { + allPaths.forEach(allp => { + const [name] = !allp.includes('/') ? [allp] : allp.split('/') + const _p = getFolderNames('components', name) + _all_ = [..._all_, ..._p] + }); + } + buildAllComponents(_all_) + } + } + + if (!isPackagesAll && args?.packages?.length) { + const libs = args?.packages + if (libs.length === 1) { + /// 先切包名 + const lib = libs[0]; + const [name] = lib.split('/') + const packagesAllPaths = getSinglePackagePaths('packages', name) + await createBuildPackages(packagesAllPaths) + await exit(); + } else { + let componentsPathsObject = Object.create(null) + libs.forEach(async lib => { + const componentsPaths = getSinglePackagePaths('packages', lib) + componentsPathsObject[lib] = componentsPaths[lib] + }); + await createBuildPackages(componentsPathsObject) + await exit(); + } + } + + /// 如果是打包全部 + if (isAll) { + /// 先打对应的依赖components + const componentsAllPaths = getAllTargetsPath('components') + const packagesAllPaths = getAllTargetsPath('packages') + if (!Object.keys(componentsAllPaths).length && !Object.keys(packagesAllPaths).length) { + yellow('\n no build components and packages task! please check buildFormCreateOptions or private with components/*/*/package.json \n') + exit(1); + } + if (!Object.keys(componentsAllPaths).length) { + yellow('\n no build components task! please check buildFormCreateOptions or private with components/*/*/package.json \n') + } + if (!Object.keys(packagesAllPaths).length) { + yellow('\n no build packages task! please check buildFormCreateOptions or private with packages/*/package.json \n') + } + /// 打印对应的组件 + await createBuildComponents(componentsAllPaths) + /// 再打对应的包 + await createBuildPackages(packagesAllPaths) + /// 退出 + await exit(); + } else { + + /// 单独打所有的component组件 + if(isComponentsAll) { + const componentsAllPaths = getAllTargetsPath('components') + if (!Object.keys(componentsAllPaths).length) { + yellow('\n no build components task! please check buildFormCreateOptions or private with components/*/*/package.json \n') + await exit(1) + } + + await createBuildComponents(componentsAllPaths) + await exit(); + } + + /// 单独打所有的package包 + if (isPackagesAll) { + const packagesAllPaths = getAllTargetsPath('packages') + if (!Object.keys(packagesAllPaths).length) { + yellow('\n no build packages task! please check buildFormCreateOptions or private with packages/*/package.json \n') + await exit(1) + } + + await createBuildPackages(packagesAllPaths) + await exit(); + } + } +} + +export default (_: any, args: any) => { + return createBuildTask(args) +} \ No newline at end of file diff --git a/tools/lib/components.ts b/tools/lib/components.ts new file mode 100644 index 0000000..5b1a884 --- /dev/null +++ b/tools/lib/components.ts @@ -0,0 +1,73 @@ +/** + * 打包components文件 + */ +import type { Ora } from 'ora'; +import chalk from 'chalk'; +import ora from 'ora'; +import os from 'os'; +import execa from 'execa'; +import dayjs from 'dayjs'; + +function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + + +let spinner: Ora; +const build = async (target: string, comp: string, targetName: string) => { + dayjs().startOf('millisecond'); + /// env 先写死 + const env = 'production' + await execa( + 'rollup', + [ + `-c`, + '--environment', + [ + `NODE_ENV:${env}`, + `BUILD_TARGET:${targetName}`, + `BUILD_TARGET_COMP:${comp}`, + `BUILD_TYPE:component`, + `BUILD_TARGET_PATH:${target}` + ].filter(Boolean).join(',') + ], + { stdio: 'inherit' } + ); + console.log('build task time' + ' ' + dayjs().format('YYYY-MM-DD HH:mm:ss')) +} + + +const runParallel = async (maxConcurrency: number, source: string[], buildName: string, iteratorFn: Function) => { + const ret = [] + const executing = [] + for (const item of source) { + const comp = item.split('/').pop() + const p = Promise.resolve().then(() => iteratorFn(item, comp, buildName)) + ret.push(p) + + if (maxConcurrency <= source.length) { + const e = p.then(() => executing.splice(executing.indexOf(e), 1)) + executing.push(e) + if (executing.length >= maxConcurrency) { + await Promise.race(executing) + } + } + } + return Promise.all(ret) +} + +const buildAll = async (comAllTargets) => { + await runParallel(os.cpus().length, comAllTargets[1], comAllTargets[0], build) +} + +const createBuildComponents = async (cpaths: { [k: string]: any }) => { + /// 根据每个不同的ui库去生成每个ui库下面的不同的组件打包 + const cps = Object.entries(cpaths) + for (const cpath of cps) { + await buildAll(cpath) + } +} + +export default createBuildComponents \ No newline at end of file diff --git a/tools/lib/packages.ts b/tools/lib/packages.ts new file mode 100644 index 0000000..08f35f1 --- /dev/null +++ b/tools/lib/packages.ts @@ -0,0 +1,88 @@ +/** + * 打包packages文件 + */ +import type {Ora} from 'ora'; +import chalk from 'chalk'; +import ora from 'ora'; +import os from 'os'; +import execa from 'execa'; +import dayjs from 'dayjs'; + +let spinner: Ora; +const build = async (target: string, comp: string, targetName: string) => { + dayjs().startOf('millisecond'); + spinner.text = chalk.bold.yellow(`start build ${targetName} ui \n`); + /// env 先写死 + const env = 'production' + await execa( + 'rollup', + [ + '-c', + '--environment', + [ + `NODE_ENV:${env}`, + `BUILD_TARGET:${targetName}`, + `BUILD_TARGET_COMP:${comp}`, + 'BUILD_TYPE:packages', + `BUILD_TARGET_PATH:${target}` + ].filter(Boolean).join(',') + ], + {stdio: 'inherit'} + ); + spinner.text = chalk.bold.green(`finished build ${targetName} ui time: ${dayjs().startOf('millisecond').format('SSS')}ms. \n `); +} + + +const runParallel = async (maxConcurrency: number, source: string[], buildName: string, iteratorFn: Function) => { + const ret = [] + const executing = [] + for (const item of source) { + const comp = item.split('/').pop() + const p = Promise.resolve().then(() => iteratorFn(item, comp, buildName)) + ret.push(p) + + if (maxConcurrency <= source.length) { + const e = p.then(() => executing.splice(executing.indexOf(e), 1)) + executing.push(e) + if (executing.length >= maxConcurrency) { + await Promise.race(executing) + } + } + } + return Promise.all(ret) +} + +const buildAll = async (comAllTargets) => { + await runParallel(os.cpus().length, comAllTargets[1], comAllTargets[0], build) +} + +const _filter_current_url_ = (urls: Array) => { + const arr = [] + urls.map((url) => { + const itemUrlLibName = url[0]; + let itemUrlLibsUrl = ''; + url[1].forEach((item, idx) => { + const suffix = item.split('/').pop() + if (suffix === itemUrlLibName) { + itemUrlLibsUrl = item + } + }) + const item = [itemUrlLibName, [itemUrlLibsUrl]] + arr.push(item) + }) + return arr +} + +const createBuildPackages = async (cpaths: { [k: string]: any }) => { + const tips = chalk.redBright.bold('\n start build all packages \n') + spinner = ora(tips).start() + /// 根据每个不同的ui库去生成每个ui库下面的不同的组件打包 + const cps = Object.entries(cpaths) + /// 处理下当前的url + const fCps = _filter_current_url_(cps) + for (const cpath of fCps) { + await buildAll(cpath) + } +} + +export default createBuildPackages diff --git a/tools/lib/paths.ts b/tools/lib/paths.ts new file mode 100644 index 0000000..9fb7daa --- /dev/null +++ b/tools/lib/paths.ts @@ -0,0 +1,14 @@ +import path from 'path'; + +/// 项目目录 +export const projRoot = path.resolve(__dirname, '../../'); +/// 单独component目录 +export const compRoot = path.resolve(projRoot, './components'); +/// packages目录 +export const pkgRoot = path.resolve(projRoot, './packages'); +/// file - packages.json \\\ +export const pkgFileRoot = path.resolve(projRoot, './package.json'); +/// file - lerna.json \\\ +export const lernaFileRoot = path.resolve(projRoot, './lerna.json'); +/// file - rollup.config.js \\\ +export const rollupFileRoot = path.resolve(projRoot, './rollup.config.ts'); \ No newline at end of file diff --git a/tools/lib/utils.ts b/tools/lib/utils.ts new file mode 100644 index 0000000..a544066 --- /dev/null +++ b/tools/lib/utils.ts @@ -0,0 +1,140 @@ +import chalk from "chalk"; +import fs from "fs"; +import { projRoot } from "./paths"; + + +export function yellow(str: string, isBold: boolean = false) { + isBold ? console.log(chalk.bold.yellow(str)) : console.log(chalk.yellow(str)) +} + +export function green(str: string, isBold: boolean = false) { + isBold ? console.log(chalk.bold.green(str)) : console.log(chalk.green(str)) +} + +export function blue(str: string, isBold: boolean = false) { + isBold ? console.log(chalk.bold.blue(str)) : console.log(chalk.blue(str)) +} + +export function red(str: string, isBold: boolean = false) { + isBold ? console.log(chalk.bold.red(str)) : console.log(chalk.red(str)) +} + +export function errorAndExit(e) { + red(e.message) + process.exit(1) +} + + +export const targets = (dir: 'packages' | 'components' = 'packages') => { + const componentsAllPaths = Object.create(null); + const uiFoldersPath = fs.readdirSync(dir).filter(uiFolderPath => { + if (fs.statSync(`${projRoot}/${dir}/${uiFolderPath}`).isDirectory()) { + return true + } + }) + + // uiFoldersPath.forEach(uiPath => { + // const pkg = require(`${projRoot}/${dir}/${uiPath}/package.json`) + // if (pkg.private || !pkg.buildFormCreateOptions) { + // blue(`\n info: ${projRoot}/${dir}/${uiPath}/package.json private is true or buildFormCreateOptions is not exists!`) + // } + // }) + + if (dir === 'packages') { + const packagesFolderPath = [] + for (let index = 0; index < uiFoldersPath.length; index++) { + const uiPath = uiFoldersPath[index]; + if (!fs.statSync(`${projRoot}/${dir}/${uiPath}`).isDirectory()) { + continue; + } + const pkg = require(`${projRoot}/${dir}/${uiPath}/package.json`) + fs.rmdirSync(`${projRoot}/${dir}/${uiPath}/dist`, { recursive: true }); + if (pkg.private || !pkg.buildFormCreateOptions) { + red(`\n info: ${projRoot}/${dir}/${uiPath}/package.json private is true or buildFormCreateOptions is not exists!`) + continue; + } + packagesFolderPath.push(`${projRoot}/${dir}/${uiPath}`) + if (packagesFolderPath.length) { + componentsAllPaths[uiPath] = packagesFolderPath + } + } + } + + if (dir === 'components') { + uiFoldersPath.forEach(uiPath => { + const componentFolderPath = [] + const alen = fs.readdirSync(`${projRoot}/${dir}/${uiPath}`).length + for (let index = 0; index < alen; index++) { + const comPath = fs.readdirSync(`${projRoot}/${dir}/${uiPath}`)[index]; + if (!fs.statSync(`${projRoot}/${dir}/${uiPath}/${comPath}`).isDirectory()) { + continue; + } + const pkg = require(`${projRoot}/${dir}/${uiPath}/${comPath}/package.json`) + fs.rmdirSync(`${projRoot}/${dir}/${uiPath}/${comPath}/dist`, { recursive: true }); + if (pkg.private || !pkg.buildFormCreateOptions) { + red(`\n info: ${projRoot}/${dir}/${uiPath}/${comPath}/package.json private is true or buildFormCreateOptions is not exists!`) + continue; + } + componentFolderPath.push(`${projRoot}/${dir}/${uiPath}/${comPath}`) + } + if (componentFolderPath.length) { + componentsAllPaths[uiPath] = componentFolderPath + } + }) + } + + + return componentsAllPaths +} + + +export const getSingleComponentPaths = (dir: string = 'components', libname: string = '', comname: string = '') => { + const fpath = Object.create(null) + const _rootPath = dir + const _libPath = libname + const _compPath = comname + + if (!fs.statSync(`${projRoot}/${_rootPath}/${_libPath}/${_compPath}`).isDirectory()) { + return + } + + const pkg = require(`${projRoot}/${_rootPath}/${_libPath}/${_compPath}/package.json`) + fs.rmdirSync(`${projRoot}/${_rootPath}/${_libPath}/${_compPath}/dist`, { recursive: true }); + if (pkg.private || !pkg.buildFormCreateOptions) { + red(`\n info: ${projRoot}/${_rootPath}/${_libPath}/${_compPath}/package.json private is true or buildFormCreateOptions is not exists!`) + return + } + fpath[_libPath] = [`${projRoot}/${_rootPath}/${_libPath}/${_compPath}`] + + return fpath +} + +export const getSinglePackagePaths = (dir: string = 'packages', libname: string = '') => { + const fpath = Object.create(null) + const _rootPath = dir + const _libPath = libname + + if (!fs.statSync(`${projRoot}/${_rootPath}/${_libPath}`).isDirectory()) { + return + } + + const pkg = require(`${projRoot}/${_rootPath}/${_libPath}/package.json`) + fs.rmdirSync(`${projRoot}/${_rootPath}/${_libPath}/dist`, { recursive: true }); + if (pkg.private || !pkg.buildFormCreateOptions) { + red(`\n info: ${projRoot}/${_rootPath}/${_libPath}/package.json private is true or buildFormCreateOptions is not exists!`) + return + } + fpath[_libPath] = [`${projRoot}/${_rootPath}/${_libPath}`] + + return fpath +} + +/// Get all the folder names under the current folder +export const getFolderNames = (folder: string, uiFolder: string) => { + return fs.readdirSync(`${folder}/${uiFolder}`).map(uiFolderPath => { + if (fs.statSync(`${projRoot}/${folder}/${uiFolder}`).isDirectory()) { + fs.rmdirSync(`${projRoot}/${folder}/${uiFolder}/dist`, { recursive: true }); + return `${uiFolder}/${uiFolderPath}` + } + }) +} \ No newline at end of file diff --git a/tools/rollup/plugin/index.ts b/tools/rollup/plugin/index.ts new file mode 100644 index 0000000..71c0eb4 --- /dev/null +++ b/tools/rollup/plugin/index.ts @@ -0,0 +1,69 @@ +/* + * @Author : djkloop + * @Date : 2021-09-19 13:53:31 + * @LastEditors : djkloop + * @LastEditTime : 2021-09-19 14:11:52 + * @Description : 头部注释 + * @FilePath : /form-create2/tools/rollup/plugin/index.ts + */ +import vue from 'rollup-plugin-vue'; +import postcss from 'rollup-plugin-postcss'; +import { cssUrl } from '@sixian/css-url'; +import externals from 'rollup-plugin-node-externals'; +import nodeResolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import babel from '@rollup/plugin-babel'; + +export const createRollupPlugins = () => { + + const rollupPlugins = [ + vue({ + preprocessStyles: true, + }) + ]; + + /// css settings + rollupPlugins.push(postcss({ + minimize: true, + extract: false, + plugins: [ + cssUrl({ + imgExtensions: /\.(png|jpg|jpeg|gif|svg)$/, + fontExtensions: /\.(ttf|woff|woff2|eot)$/, + limit: 8192, + hash: false, + slash: false + }) + ] + })); + + + /// devDependencies + rollupPlugins.push(externals({ + devDeps: true + })); + + /// j + rollupPlugins.push(nodeResolve({ + extensions: ['.js', '.json', '.jsx', '.ts', '.tsx'], + preferBuiltins: true, + browser: true + })); + + /// commonjs + rollupPlugins.push(commonjs()); + + rollupPlugins.push(babel({ + babelHelpers: 'bundled', + exclude: 'node_modules/**', + extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue'], + })); + + + // if (visualizer) { + + // } + + + return rollupPlugins +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..116a8d9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ + +{ + "compilerOptions": { + "target": "ESNext", + "sourceMap": true, + "allowUnreachableCode": true, + "allowSyntheticDefaultImports": true, + "allowJs": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "noImplicitThis": true, + "noImplicitAny": false, + "importHelpers": true, + "jsx": "preserve" + }, + "include": [ + "*.vue", + "*", + "types/typing.d.ts" + ] +} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..337be3b --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,31 @@ +import {Rule} from "@form-create/element-ui"; + +export interface MenuItem { + label: string, + name: string, + icon: string; +} + +export interface Menu { + title: string; + name: string; + list: MenuItem[] +} + +export interface MenuList extends Array { + +} + +export interface DragRule { + name: string; + + rule(): Rule; + + props(): Rule[]; + + children?: string; + inside?: true; + drag?: true | String; + dragBtn?: false; + mask?: false; +} diff --git a/vite.config.build.js b/vite.config.build.js new file mode 100644 index 0000000..42b96e8 --- /dev/null +++ b/vite.config.build.js @@ -0,0 +1,85 @@ +import {defineConfig} from 'vite' +import vue from '@vitejs/plugin-vue' +import vueJSX from '@vitejs/plugin-vue-jsx' +import banner from 'vite-plugin-banner' +import cssnano from 'cssnano' +import visualizer from 'rollup-plugin-visualizer'; +import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js' +import {author, license, name, version} from './package.json' + +const extnedsPlugins = []; + +function getBanner(banner, pkg) { + if (!banner || typeof banner === 'string') { + return banner || ''; + } + + banner = {...pkg, ...(banner === true ? {} : banner)}; + + const author =banner.author + + const license = banner.license || ''; + return ( + '/*!\n' + + ' * form-create 可视化表单设计器\n' + + ` * ${banner.name} v${banner.version}\n` + + ` * (c) ${author || ''}\n` + + (license && ` * Released under the ${license} License.\n`) + + ' */' + ); +} + +const __banner__ = { + author: `2021-${new Date().getFullYear()} ${author}\n * Github https://github.com/xaboy/form-create-designer`, + license, + name, + version +} + +// 打包生产环境才引入的插件 +if (process.env.NODE_ENV === 'production') { + // 打包依赖展示 + extnedsPlugins.push( + visualizer({ + open: true, + gzipSize: true, + brotliSize: true, + }) + ); +} + +// https://vitejs.dev/config/ +export default defineConfig({ + build: { + lib: { + entry: 'src/index.js', + name: 'FcDesigner', + fileName: format => `index.${format}.js`, + }, + rollupOptions: { + output: { + exports: 'named', + globals: { + vue: 'Vue' + } + }, + external: [ + 'vue', + 'element-plus', + '@form-create/element-ui' + ], + + }, + brotliSize: true + }, + css: { + postcss: { + plugins: [ + cssnano({ + preset: 'advanced' + }) + ] + } + }, + plugins: [vue(), vueJSX(), banner(getBanner(__banner__)),cssInjectedByJsPlugin(), ...extnedsPlugins] +}) diff --git a/vite.config.preview.js b/vite.config.preview.js new file mode 100644 index 0000000..9905287 --- /dev/null +++ b/vite.config.preview.js @@ -0,0 +1,22 @@ +import {defineConfig} from 'vite' +import vue from '@vitejs/plugin-vue' +import vueJSX from '@vitejs/plugin-vue-jsx' +// https://vitejs.dev/config/ +export default defineConfig({ + build: { + cssCodeSplit: true, + rollupOptions: { + output: { + manualChunks: { + vue: ['vue'], + 'element-plus': ['element-plus'], + }, + globals: { + vue: 'Vue', + } + }, + }, + chunkSizeWarningLimit: 2000, + }, + plugins: [vue(), vueJSX()] +}) diff --git a/vue.config.js b/vue.config.js new file mode 100644 index 0000000..8457860 --- /dev/null +++ b/vue.config.js @@ -0,0 +1,28 @@ +/* + * @Author : djkloop + * @Date : 2020-08-15 21:16:03 + * @LastEditors : djkloop + * @LastEditTime : 2021-09-21 17:18:46 + * @Description : 头部注释 + * @FilePath : /form-create2/packages/element-ui/vue.config.js + */ +module.exports = { + pages: { + app: { + entry: 'examples/main.js', + template: 'examples/index.html', + filename: 'index.html' + } + }, + configureWebpack: { + module: { + rules: [ + { + test: /\.mjs$/, + include: /node_modules/, + type: 'javascript/auto' + }, + ] + } + } +}