├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── README.md
├── babel.config.js
├── dist
├── demo.html
├── es-form.common.js
├── es-form.umd.js
└── es-form.umd.min.js
├── docs
├── .vuepress
│ ├── components
│ │ ├── demo-block.vue
│ │ ├── demo-custom.vue
│ │ ├── entry.js
│ │ ├── slots
│ │ │ └── scoped.vue
│ │ └── version
│ │ │ ├── entry.js
│ │ │ └── select.vue
│ ├── config.js
│ └── styles
│ │ └── index.styl
├── README.md
├── base
│ ├── README.md
│ ├── array.md
│ ├── auto-match.md
│ ├── com-format.md
│ ├── component.md
│ ├── console.md
│ ├── d-col-group.md
│ ├── desc.md
│ ├── explain.md
│ ├── form.md
│ ├── format.md
│ ├── help.md
│ ├── hidden.md
│ ├── install.md
│ ├── label.md
│ ├── parse.md
│ ├── properties.md
│ ├── quickstart.md
│ ├── rules.md
│ ├── scopedSlots.md
│ ├── settings.md
│ ├── tabs.md
│ ├── title.md
│ ├── ui.md
│ ├── unit.md
│ └── value.md
├── donate.md
├── images
│ ├── alipay.png
│ └── wechat.png
└── version
│ └── overview.md
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ ├── common
│ │ ├── header.vue
│ │ ├── main-footer.vue
│ │ └── nav.vue
│ ├── demo-frame
│ │ ├── index.vue
│ │ ├── mixins
│ │ │ └── sys-mixin.js
│ │ └── schemas
│ │ │ └── operation-schema.js
│ ├── help
│ │ └── index.vue
│ ├── register.js
│ ├── tags
│ │ └── index.vue
│ └── units
│ │ ├── README.md
│ │ ├── align-desc.vue
│ │ ├── checkbox-group.vue
│ │ ├── desc.vue
│ │ ├── label.vue
│ │ ├── radio-group.vue
│ │ ├── select.vue
│ │ ├── slot.vue
│ │ ├── title.vue
│ │ └── unit.vue
├── libs
│ ├── constant.js
│ ├── listeners.js
│ ├── storage.js
│ ├── uri.js
│ └── utils.js
├── main.js
├── package
│ ├── README.md
│ ├── base.js
│ ├── components
│ │ ├── btn.vue
│ │ ├── console.vue
│ │ ├── edit-abbr-btns.vue
│ │ ├── edit-bottom-btns.vue
│ │ ├── edit-btns.vue
│ │ ├── help.vue
│ │ ├── tabs-btn.vue
│ │ ├── tabs-nav-item.vue
│ │ └── tabs-nav.vue
│ ├── form-item.vue
│ ├── index.js
│ ├── index.vue
│ ├── layout
│ │ ├── array-card.vue
│ │ ├── array-legend.vue
│ │ ├── array-row.vue
│ │ ├── array-table.vue
│ │ ├── array-tabs.vue
│ │ ├── object-table.vue
│ │ ├── object.vue
│ │ └── tabs.vue
│ ├── libs
│ │ ├── component-utils.js
│ │ ├── constant.js
│ │ ├── data-cache.js
│ │ ├── drag.js
│ │ ├── form-utils.js
│ │ ├── global.js
│ │ ├── parse.js
│ │ ├── pop-utils.js
│ │ ├── rules.js
│ │ ├── schema-rules.js
│ │ ├── schema-utils.js
│ │ ├── submit.js
│ │ ├── tabs-observer.js
│ │ └── utils.js
│ ├── mixins
│ │ ├── array-del-pop-mixin.js
│ │ ├── array-edit-item-mixin.js
│ │ ├── array-mixin.js
│ │ └── item-mixin.js
│ └── static
│ │ └── css
│ │ ├── index.scss
│ │ └── mixins.scss
├── router
│ ├── index.js
│ └── nav-route.js
├── static
│ └── css
│ │ ├── index.scss
│ │ └── mixins.scss
└── views
│ ├── README.md
│ ├── demo
│ ├── array-card.vue
│ ├── array-legend.vue
│ ├── array-row.vue
│ ├── array-table.vue
│ ├── array-tabs.vue
│ ├── component.vue
│ ├── desc.vue
│ ├── example.vue
│ ├── help.vue
│ ├── label.vue
│ ├── properties.vue
│ ├── scopedSlots.vue
│ ├── simple.vue
│ ├── standard.vue
│ ├── tabs.vue
│ ├── title.vue
│ └── unit.vue
│ ├── home
│ ├── components
│ │ ├── README.md
│ │ └── native.vue
│ └── index.vue
│ └── notfound
│ ├── components
│ └── README.md
│ └── index.vue
├── tests
└── unit
│ ├── .eslintrc.js
│ └── libs
│ └── utils.spec.js
└── vue.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: ["plugin:vue/essential", "@vue/prettier"],
7 | rules: {
8 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
9 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
10 | },
11 | parserOptions: {
12 | parser: "babel-eslint"
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | # /dist
4 | package-lock.json
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | debug.log
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-easy-form
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-easy-form:简称esForm,是一个独立、不依赖第三方类库的vue表单组件。通过一份json配置,动态输出用户所需要的表单。组件布局丰富、逻辑控制简洁、事件联动灵活、`无缝对接第三方类库`, 极大地提高用户开发效率。
9 |
10 | ## 项目安装
11 |
12 | ### Node 版本要求
13 | [Node.js](https://nodejs.org/en/) >= 8.11; 若需要升级;可使用[nvm](https://github.com/nvm-sh/nvm) 或 [nvm-windows](https://github.com/coreybutler/nvm-windows) 进行多个 Node 版本管理。
14 |
15 |
16 | ### 项目信息
17 | [esForm文档](https://chengaohe45.github.io/vue-easy-form-docs/dist/)
18 | [esForm源码](https://github.com/chengaohe45/vue-easy-form)
19 |
20 | ### Install
21 | ```
22 | npm install
23 | ```
24 |
25 | ### Compiles and hot-reloads for development
26 | ```
27 | npm run serve
28 | npm run dev
29 | ```
30 |
31 | ### Compiles and minifies for production
32 | ```
33 | npm run build
34 | ```
35 |
36 | ### Lints and fixes files
37 | ```
38 | npm run lint
39 | ```
40 |
41 | ### Run your unit tests
42 | ```
43 | npm run test:unit
44 | ```
45 |
46 | ## 目录结构
47 | 本项目包含`esForm框架源码`和`实例源码`,其中`esForm框架源码`独立存在于`/src/package`文件夹中。整个项目具体结构如下:
48 |
49 | ```js
50 | ├── dist # 构建相关
51 | ├── public # 编译资源
52 | │ │── favicon.ico # favicon图标
53 | │ └── index.html # html模板
54 | ├── src # 源代码
55 | │ ├── assets # 静态资源
56 | │ ├── components # 全局公用组件
57 | │ ├── libs # 工具类
58 | │ ├── package # 独立的esform源码,不依赖其它文件夹
59 | │ ├── router # 菜单配置和路由实现
60 | │ ├── static # 全局样式
61 | │ ├── views # 页面视图,demo就存在于此
62 | │ ├── App.vue # vue入口组件
63 | │ ├── main.js # 入口文件
64 | ├── tests # 测试模块
65 | ├── .eslintrc.js # eslint配置项
66 | ├── .gitignore # git忽略文件
67 | ├── vue.config.js # vue-cli3脚手架配置
68 | ├── postcss.config.js # postcss配置
69 | ├── README.md # readme文件
70 | └── package.json # package.json安装依赖
71 | ```
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/app"]
3 | };
4 |
--------------------------------------------------------------------------------
/dist/demo.html:
--------------------------------------------------------------------------------
1 |
2 | es-form demo
3 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/docs/.vuepress/components/entry.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | // import App from "./App.vue";
3 | // import router from "./router/index";
4 |
5 | // 引入UI样式,各模块按需引入ui组件
6 | import "element-ui/lib/theme-chalk/index.css";
7 |
8 | // import "https://shadow.elemecdn.com/npm/highlight.js@9.3.0/styles/color-brewer.css";
9 | //引入ui组件
10 | import elementUI from "element-ui";
11 |
12 | // 引入基本样式
13 | // import esForm from "../../../../vue-easy-form/src/package/index.js";
14 |
15 | import esForm from "@/package/index.js";
16 |
17 | import gComponent from "@/components/register.js";
18 |
19 | // 注册全局组件
20 | gComponent.register();
21 | Vue.use(elementUI);
22 | Vue.use(esForm, {
23 | rowHeight: 40,
24 | rowSpace: 12,
25 | labelWidth: 100,
26 | offsetLeft: 0,
27 | offsetRight: 0,
28 | colon: false,
29 | direction: "h",
30 | defaultCom: "el-input", // 如:若用element-ui, 改为el-input
31 | defaultVal: "", // 对defaultCom这个组件的value设置默认值
32 | trimDoms: ["input", "textarea", "el-input"], // 数组,空数组会全部清空
33 | hasConsole: true // 推荐写成动态,编译时不用修改
34 | });
35 |
36 | /*
37 | 打印类库的版本
38 | 当处于生产环境时:hash中存在libs_version=就行了,不宜写过多的代码来做查询判断;因为只是个调试信息,不影响功能
39 | */
40 | // if (
41 | // process.env.NODE_ENV != "production" ||
42 | // (location.hash && location.hash.indexOf("libs_version=") >= 1)
43 | // ) {
44 | // console.log("esForm's version: " + esForm.version);
45 | // }
46 |
47 | Vue.config.productionTip = false;
48 |
49 | // window.vm = new Vue({
50 | // router,
51 | // render: h => h(App)
52 | // }).$mount("#app");
--------------------------------------------------------------------------------
/docs/.vuepress/components/slots/scoped.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
113 |
--------------------------------------------------------------------------------
/docs/.vuepress/components/version/entry.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | // 引入UI样式,各模块按需引入ui组件
3 | import "element-ui/lib/theme-chalk/index.css";
4 | import elementUI from "element-ui";
5 | Vue.use(elementUI);
6 | Vue.config.productionTip = false;
--------------------------------------------------------------------------------
/docs/.vuepress/components/version/select.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
110 |
111 |
116 |
--------------------------------------------------------------------------------
/docs/.vuepress/config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | let version = require("../../package.json").version;
4 |
5 | let nums = version.split(".");
6 | nums = nums.slice(0, nums.length - 1); // 两个数字,中间版本
7 | let urlVersion = nums.join("."); // 打开就是编译最新版本
8 |
9 | let isLatest = true;
10 | let argvs = process.argv;
11 | if (argvs && argvs.length > 0) {
12 | let paramIndex = argvs.indexOf("version");
13 | if (paramIndex > 0) {
14 | isLatest = false;
15 | }
16 | }
17 |
18 | const MAIN_DIR_PATH = "vue-easy-form-docs"; // 也可“vue-easy-form-docs/xx”;左右不要有"/"
19 | // 这几个主要用于版本控制
20 | var latestBaseUrl = "/" + MAIN_DIR_PATH + "/dist"; // 最新版本的baseUrl
21 | var baseUrl = isLatest ? (latestBaseUrl + "/") : ("/" + MAIN_DIR_PATH + "/" + urlVersion + "/"); // 最右边一定要有“/”否则编译有问题
22 | var prefixVerBaseUrl = "/" + MAIN_DIR_PATH; // 当选择不是最新版本时,此时组合的前缀
23 | var suffixVersion = ".x"; // 点后面不能是数字
24 |
25 | module.exports = {
26 | title: "vue-easy-form(当前版本:" + urlVersion + suffixVersion + ")",
27 | description: '后台自动化平台',
28 | base: baseUrl,
29 | dest: "../vue-easy-form-docs/" + (isLatest ? "dist/" : (urlVersion + "/")),
30 | themeConfig: {
31 | // lastUpdated: '最后更新',
32 | nav: [
33 | { text: '指南', link: '/' },
34 | { text: '查看版本', link: '/version/overview' },
35 | // {
36 | // text: '版本',
37 | // items: [
38 | // {
39 | // text: '版本总览',
40 | // link: '/version/overview'
41 | // },
42 | // {
43 | // text: '1.6.*',
44 | // link: '/version/1.6.all'
45 | // },
46 | // {
47 | // text: '1.5.*',
48 | // link: '/version/1.5.all'
49 | // },
50 | // {
51 | // text: '1.4.*',
52 | // link: '/version/1.4.all'
53 | // }
54 | // ]
55 | // },
56 | { text: '捐赠', link: '/donate' },
57 | { text: 'GitHub', link: 'https://github.com/chengaohe45/vue-easy-form' },
58 | { text: 'esForm例子', link: 'https://chengaohe45.github.io/vue-easy-form-docs/demo' }
59 | ],
60 | sidebar: {
61 | '/': [
62 | {
63 | title: '指南',
64 | collapsable: false,
65 | children: [
66 | '',
67 | 'base/install',
68 | 'base/quickstart',
69 | 'base/settings',
70 | 'base/explain',
71 | 'base/com-format',
72 | 'base/scopedSlots',
73 | 'base/parse',
74 | 'base/console'
75 | ]
76 | },
77 | {
78 | title: '基础属性详解',
79 | collapsable: false,
80 | children: [
81 | 'base/d-col-group',
82 | 'base/ui',
83 | 'base/hidden',
84 | 'base/properties',
85 | 'base/tabs',
86 | 'base/rules',
87 | 'base/array',
88 | 'base/value',
89 | 'base/format',
90 | 'base/auto-match',
91 | 'base/form'
92 | ]
93 | },
94 | {
95 | title: '可组件化属性详解',
96 | collapsable: false,
97 | children: [
98 | 'base/component',
99 | 'base/label',
100 | 'base/title',
101 | 'base/help',
102 | 'base/unit',
103 | 'base/desc'
104 | ]
105 | }
106 | ],
107 | '/version/': ['overview', '1.4.all']
108 | }
109 | },
110 |
111 | configureWebpack: {
112 | resolve: {
113 | alias: {
114 | // "$src": path.resolve( __dirname, "../../src"),
115 | "@": path.resolve( __dirname, "../../src")
116 | }
117 | },
118 |
119 | plugins: [
120 | new webpack.DefinePlugin({
121 | "process.env": {
122 | VUE_APP_VERSION: JSON.stringify(version), // 与库的写法同步
123 | // 文档中将要用到
124 | VUE_DOCS_VERSION: JSON.stringify(urlVersion),
125 | URL_SEPARATOR: JSON.stringify(baseUrl.replace(/\/+$/g, "")), // 去年右边的“/”才好split
126 | LATEST_BASE_URL: JSON.stringify(latestBaseUrl),
127 | PREFIX_VERSION: JSON.stringify(prefixVerBaseUrl),
128 | SUFFIX_VERSION: JSON.stringify(suffixVersion)
129 | }
130 | })
131 | ]
132 | }
133 | };
134 |
--------------------------------------------------------------------------------
/docs/.vuepress/styles/index.styl:
--------------------------------------------------------------------------------
1 |
2 |
3 | .page .content:not(.custom),.page .content__default:not(.custom),.page .page-edit, .page .page-nav{
4 | max-width: 880px;
5 | }
6 |
7 | .divider-line {
8 | margin: 0;
9 | padding: 0;
10 | border-top: 1px solid #47494c;
11 | }
12 |
13 | .divider-line + .extra-class {
14 | border-radius: 0;
15 | }
16 |
17 | .divider-line + .extra-class > pre{
18 | border-radius: 0;
19 | }
20 |
21 | // 覆盖一下样式,因为被vuepress>table影响了
22 | .el-year-table {
23 | display: table;
24 |
25 | tr {
26 | border-top: none;
27 | }
28 | td, th {
29 | border: none;
30 | }
31 |
32 | }
33 |
34 | .el-date-table {
35 | margin: 0;
36 | display: table;
37 | border-collapse: separate;
38 | border-spacing: 0;
39 | border: none;
40 |
41 | td {
42 | border: none;
43 | }
44 |
45 | th {
46 | border: none;
47 | border-bottom-width: 1px;
48 | }
49 |
50 | tr:nth-child(2n) {
51 | background-color: transparent;
52 | }
53 | }
54 |
55 | .es-form-array-table {
56 |
57 | table {
58 | margin: 0;
59 | display: table;
60 | border-collapse: separate;
61 | border-spacing: 0;
62 | border: none;
63 | }
64 |
65 | td {
66 | border: none;
67 | border-top-width: 1px;
68 | border-right-width: 1px;
69 | }
70 |
71 | th {
72 | border: none;
73 | border-top-width: 1px;
74 | border-right-width: 1px;
75 | }
76 |
77 | tr:nth-child(2n) {
78 | background-color: transparent;
79 | }
80 | }
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # vue-easy-form介绍
2 |
3 | > vue-easy-form:简称esForm,是一个独立、不依赖第三方类库的vue表单组件。通过一份json配置动态输出用户所需要的表单。组件布局丰富、逻辑控制简洁、事件联动灵活、`无缝对接第三方类库`, 极大地提高用户开发效率。
4 |
5 | ## 为什么选择esForm?
6 | - 只需要一个json配置,几乎无需html, css;
7 | - 布局丰富,包含弹性布局(分为24栏)和tabs布局;
8 | - 无缝对接第三方组件,无需为框架定制化组件(可自由选择`element-ui`、`fish-ui`等类库);
9 | - 表单功能齐全,包含标题、验证、事件、单位、帮助、描述、`数组`等;
10 | - 逻辑控制灵活方便(支持[动态解析](./base/parse.md))
11 |
12 | ## 目录结构
13 | 本项目包含`esForm框架源码`和`实例源码`,其中`esForm框架源码`独立存在于`/src/package`文件夹中。整个项目具体结构如下:
14 | ```js
15 | ├── dist # 构建相关
16 | ├── public # 编译资源
17 | │ │── favicon.ico # favicon图标
18 | │ └── index.html # html模板
19 | ├── src # 源代码
20 | │ ├── assets # 静态资源
21 | │ ├── components # 全局公用组件
22 | │ ├── libs # 工具类
23 | │ ├── package # 独立的esform源码,不依赖其它文件夹
24 | │ ├── router # 菜单配置和路由实现
25 | │ ├── static # 全局样式
26 | │ ├── views # 页面视图,demo就存在于此
27 | │ ├── App.vue # vue入口组件
28 | │ ├── main.js # 入口文件
29 | ├── tests # 测试模块
30 | ├── .eslintrc.js # eslint配置项
31 | ├── .gitignore # git忽略文件
32 | ├── vue.config.js # vue-cli3脚手架配置
33 | ├── postcss.config.js # postcss配置
34 | ├── README.md # readme文件
35 | └── package.json # package.json安装依赖
36 | ```
--------------------------------------------------------------------------------
/docs/base/README.md:
--------------------------------------------------------------------------------
1 | # vue-esay-form
2 |
3 | > vue-esay-form:简称esForm,是一个独立、不依赖第三方类库的vue表单组件。通过一份json配置动态输出用户所需要的表单。组件布局丰富、逻辑控制简洁、事件联动灵活、`无缝对接第三方类库`, 极大地提高用户开发效率。
4 |
5 |
--------------------------------------------------------------------------------
/docs/base/auto-match.md:
--------------------------------------------------------------------------------
1 | # 自动匹配
2 | `自动匹配就是把所有的二级的项在输出时提升为一级。`
3 | `应用场景`:一般用于表单结构和接口字段不一致时匹配。比如:现需要保存一个页面信息,后台需要保存接口的字段只需要一级{pageName, fontSize, fontColor, backgroundImage, backgroundColor), 但对产品设计来说,可能需要把字体和背景按各自模块区分,页面结构才更加清晰。
4 | 字段(见`行高亮`):
5 | ```js {2}
6 | schema: {
7 | autoMatch: true, // 设置autoMatch为true; autoMatch只有在要节点中有效
8 | properties: {
9 | name: {
10 | label: "名称",
11 | component: "el-input",
12 | value: "天天"
13 | }
14 | // ... 其它项
15 | }
16 | }
17 | ```
18 |
19 | ### 实例
20 | 可打开`调试面版`查看输出结果,`formValue`全部都平级了
21 |
22 |
23 |
24 |
25 | ```html
26 |
27 |
28 |
79 | ```
80 |
81 |
82 |
83 | ::: warning 注意
84 | - autoMatch设置只能在根节点中设置,其它块是无效的;
85 | - 自动匹配只影响一级和二级的表单值输出,对根值rootData没有影响;
86 | - 因为输出表单值时,二级要提升为一级,为了保持key的唯一性,编写schema时,一级和二级的所有的key(如:pageName、font、background、fontSize、backgroundImage)不能相同。
87 | :::
88 |
89 | ### 设值
90 |
91 | 在自动匹配中,因为key是唯一的,下面的值的设置效果是一样的:
92 | 如:
93 | ```js
94 | form.setValue({ // 正常结构
95 | font: {
96 | fontSize: 12
97 | }
98 | });
99 |
100 | form.setValue({ // 简化结构
101 | fontSize: 12
102 | });
103 |
104 | form.setValue("font.fontSize", 12); // 正常结构
105 | form.setValue("fontSize", 12); // 简化结构
106 | ```
107 |
--------------------------------------------------------------------------------
/docs/base/com-format.md:
--------------------------------------------------------------------------------
1 | # 组件格式
2 | 从一个Object开始,按一定的格式配置引入相应的Vue组件;目前支持组件化的属性有:[项标签label](./label.md)、[描述desc](./desc.md)、[帮助help](./help.md)、[单元unit](./unit.md)、[标题title](./title.md)、[项组件component](./component.md),当然,这些属性都有自己特殊的写法,具体的写法见`可组件化属性详解`。
3 | ## 配置格式
4 | ``` js
5 | // attrName为某个属性的索引,如: label, title, component
6 | `attrName`: { // 若是一个对象,可写成一个Object
7 | name: "el-button", // 组件名,组件名不支持动态解析
8 | style: { color: "#000" }, // 内联样式,一个对象;支持动态解析
9 | class: "box1 box2", // 样式类;支持动态解析
10 | props: { // 属性: 以下全都支持动态解析
11 | disabled: "es: $root.status ? true : false", // 动态解析:es语法
12 | type: function(options) { // 动态解析:函数写法
13 | return options.rootData.status ? "primary" : "success";
14 | },
15 | size: "mini", // 固定值
16 | },
17 | text: "新建", // 组件的文本,如新建;支持动态解析
18 | value: "", // 组件的值,这个不支持动态解析。因为其会与v-model实行双向绑定
19 |
20 | actions: { // 组件的行为,多个时可写数组
21 | trigger: "click",
22 | handler: function() {}
23 | },
24 |
25 | directives: { // 指令;directives可简写为v;单值时也可直接写上指令名;多个值时也可写成数组
26 | name: "loading", // 指令名
27 | value: 2, // 指令的绑定值;支持动态解析;注:只有指令绑定值value支持动态解析
28 | expression: "1 + 1", // 指令表达式
29 | arg: "foo", // 传给指令的参数
30 | modifiers: { // 一个包含修饰符的对象
31 | bar: true
32 | }
33 | },
34 |
35 | scopedSlots: { // 插槽:键值对写法:具体见“组件插槽”
36 | default: { // 自定义组件
37 | name: "el-tag",
38 | text: "123"
39 | },
40 | otherSlot: "123" // 字符串,数值,布尔,函数等
41 | }
42 | }
43 | ```
44 | - `name`: 组件名;`类型`:字符串/组件对象;`必填`
45 | - `style`: 内联样式;`类型`:对象;`非必填`
46 | - `class`: 样式类;`类型`:字符串/对象/数组;要符合[Vue Class](https://cn.vuejs.org/v2/guide/class-and-style.html)写法;`非必填`
47 | - `props`: 组件属性;`类型`:对象;`非必填`
48 | - `text`: 组件文本;`类型`:字符串、数值、布尔;`非必填`
49 | - `value`: [组件的值](#value配置)(与v-model实行双向绑定);`类型`:任何;`非必填`
50 | - `actions`: [组件事件](#组件事件);`类型`:对象/数组;`非必填`
51 | - `directives`: 指令;`类型`:字符串/对象/数组;`非必填`;索引`directives`可简写为`v`
52 | - `scopedSlots`: [组件插槽](./scopedSlots.md);`类型`:字符串/布尔/数值/对象
53 |
54 | > 动态解析是指支持[es写法](./parse.md#es写法)或[函数写法](./parse.md#函数写法)
55 |
56 | ::: warning 注意
57 | `没有this指针` 当props里的的属性(如:disabled)写成一个函数时,this并不指向表单的。
58 | 为什么? 因为当执行这些函数时,表单内的组件正处于未构造或正在重复构造,不建议调用表单内的函数(如:form.getValue等);这个与验证[rules.checks](rules.md)、数组[array.insertValue](array.md#数组默认插入值)、项组件[component.actions](component.md#组件事件)里面的函数不同,后者们的this是指向表单的
59 | :::
60 |
61 | ## Value配置
62 | 平时写value时一般有两种写法,不同的写法代表的意义是不同的。
63 | 1. 写在`v-model`进行双向绑定,也是表单组件常见的写法
64 | ``` html
65 |
66 |
75 | ```
76 | 等价于`json配置型`写法,如下:
77 | ``` js
78 | `attrName`: {
79 | name: "el-input",
80 | props: {
81 | placeholder: "请输入内容"
82 | },
83 | value: "" // 与name同级,说明是绑定v-model(与组件内的value进行双向绑定),所以不支持动态解析
84 | }
85 | ```
86 |
87 | 2. 直接配置value属值
88 | ``` html
89 |
90 |
99 | ```
100 | 等价于`json配置`型写法,如下:
101 | ``` js
102 | `attrName`: {
103 | name: "el-input",
104 | props: {
105 | placeholder: "请输入内容",
106 | value: "" // 当成一个普通属性,不会与组件内的value进行双向绑写,所以动态解析
107 | }
108 | }
109 | ```
110 |
111 | ## 组件事件
112 | 字段:actions
113 | ```js
114 | 标准写法
115 | actions: {
116 | // 默认为click, 多个事件写法: ["change", true]或"change input"
117 | // 注: true会转换为input事件(v1.8.1增加)
118 | trigger: "change",
119 | // options => {value, event, pathKey, index, idxChain,target}
120 | handler: function(options){...}
121 | }
122 |
123 | 简写 (直接写成一个函数),trigger默认为click
124 | actions: function(options){...}
125 |
126 | 多个事件
127 | actions: [标准写法或简写组成的数组]
128 | ```
129 | 函数handler返回的参数options包含的信息有:
130 | - `value`: 当前项组件的值,表单的值可以通过this取出
131 | - `args`: 事件本身所携带的信息(就是函数的局部变量`arguments`);如`keyup.native`,可以从这里提取键值;
132 | - `event`: 事件本身所携带的第一个信息, 也就是`args[0]`
133 | - `target`: 当前项组件(若是`数组事件`,这个为`null`)
134 | - `pathKey`: 需要检查的组件的路径
135 | - `idxChain`: 需要检查的组件所要数组所组成的id 如: 1,2
136 | - `index`:组件处于数组的子节点(非孙子)时的索引,其余的返回-1
137 | - `instance`:表单实例(当handler为箭头函数,若需要可用instance取出表单实例使用)
138 | > handler函数的this指针指向当前的表单实例(`instance`也是当前的表单实例),这样可以方便取出其它组件(如`this.getRef("xxxx")`),从而做联动等功能
139 |
140 | ### 当props里面的某属性是的类型是函数怎么办?
141 |
142 | 例子:`showText`是函数类型;若要表达其值是一个函数,则在其名称前加上`s:`前缀,系统将不会对此函数进行动态解析
143 | ``` js {3}
144 | `attrName`: {
145 | name: "自定义组件",
146 | props: { // 注意:`s:`是对props里面的属性进行有效补充,对其它的属性(如`text`)是没有作用的
147 | "s:showText": function(names) { // 注意空格,因为空格也可以当作key,所以不要留空格
148 | return names.join(",");
149 | },
150 | color: "#fff000"
151 | },
152 | text: "hello"
153 | }
154 | ```
155 | ::: tip 为什么这样写?
156 | 因为在应用过程中,`表单组件`一般都不会用一个函数作为一个属性类型(虽理论上是可以的,但至今笔者都极少极少碰到此类型的`表单组件`),所以为了保持简洁且和其它属性保持一致的写法,当前面不加`s:`前缀,则认为是可动态解析的属性。
157 | :::
--------------------------------------------------------------------------------
/docs/base/console.md:
--------------------------------------------------------------------------------
1 | # 调试面板
2 | 若设置了调试面板为打开时,表单的左上角有一个`C`按钮;在面版里面可以`实时`看到表单的[根值](.explain.md#根值)和[表单值](.explain.md#表单值);常用于开发调试用。
3 | ### 实例
4 | 以下用的是`全局引入设置`,设置了hasConsole为true
5 |
6 |
7 |
8 |
9 | ```html
10 |
11 |
12 |
31 | ```
32 |
33 |
34 |
35 | ### 全局引入设置
36 |
37 | 在引入时直接设置; 全局`hasConsole`的默认值为`false`
38 |
39 | ```js
40 | Vue.use(esForm,
41 | {
42 | rowSpace: 10,
43 | hasConsole: process.env.NODE_ENV != "production" // 推荐写成动态,这样编译时不用修改;
44 | });
45 | ```
46 |
47 | ### 表单属性设置
48 |
49 | 对表单的属性hasConsole进行设置;表单`hasConsole`的优先级高于全局`hasConsole`的优先级;当不设置表单`hasConsole`时,则会自动取全局设置
50 |
51 | ```html
52 |
57 |
58 | ```
59 |
--------------------------------------------------------------------------------
/docs/base/desc.md:
--------------------------------------------------------------------------------
1 | # 描述/desc
2 |
3 | 字段:`desc`
4 | 值类型有:
5 | - `string`: 描述的内容;支持[动态解析](./parse.md)
6 | - `object`: 一个对象,见[组件格式](./com-format.md)
7 |
8 | ### 实例
9 |
10 |
11 |
12 | ```html
13 |
14 |
15 |
65 | ```
66 |
67 |
68 |
69 | ### 组件详解
70 |
71 | | 属性名 | 说明 | 类型 | 可选值| 默认值
72 | | -- | -- | -- | -- | --
73 | | hidden | 控制组件是否隐藏, 支持[动态解析](./parse.md) | boolean | -- | false
74 | | 其它 | 跟[组件格式](./com-format.md)一样 | -- | -- | --
--------------------------------------------------------------------------------
/docs/base/explain.md:
--------------------------------------------------------------------------------
1 | # 表单相关概念
2 |
3 | ## 根值
4 |
5 | rootData: 表单中所有的值,包含隐藏的;也就是说有什么值就取什么值出来;用于操作`表单项组件`的逻辑控制
6 |
7 | ## 表单值
8 |
9 | formValue: 表单中用户所需要的值(一般不包含隐藏的值、临时值;场景如下);用于数据提交;可认为formValue是rootData的一个子集。
10 |
11 | 影响formValue的场景:
12 | - 在表单逻辑控制中,一般隐藏的`表单项组件`不需要提交的(若需要提交, 可设置`hdValue`);
13 | - 在表单逻辑控制中,隐藏的`表单项组件`需要提交的指定的值(可设置`hdValue`);
14 | - 在`表单项组件`件显示中,有的值只是显示,但并不需要提交(可设置`isTmp`)
15 |
16 | ## 索引链
17 | idxChain: 指出`表单项组件`所处于数组的位置
18 | 如:base.student[0].courses[1].name, 如索引链为"0,1"; 当父节点或祖先节点不是数组时,则`表单项组件`为`空字符`
19 | > 应用场景:[项组件事件](./component.md#组件事件)、[项组件验证](./rules.md)、[数组事件](./array.md#数组事件)所携带的信息会返回此值
20 |
21 |
22 | ## 项组件路径
23 | pathKey: `表单项组件`位置的具体路径. 如:
24 | ```js
25 | base.student[0].courses[1].name // 推荐的写法
26 | base.target.name // 推荐的写法
27 | base.target["name"]
28 | ```
29 | 生成形式见[表单值/value](./form-value.md)
30 | > 应用场景:[项组件事件](./component.md#组件事件)、[项组件验证](./rules.md)、[数组事件](./array.md#数组事件)所携带的信息会返回此值; [form.setValue](./form.md#表单方法)等需要此值设置
31 |
32 |
33 | ## es语法
34 | es语法就一条`es:`为前缀的字符串,再按照一定的规则解析出来的js语句。
35 | 如:
`es: $root.isJson ? "JSON格式" : "XML格式"`
36 | 说明:字符串以es:开头,用表单根数据代替$root, 最终变为
37 | `rootData.isJson ? "JSON格式" : "XML格式"`
38 |
39 | 支持es有4个数据源,这4个值共同影响整个es的解析:
40 | 1. `rootData`: 整个表单的`根值/rootData`. root在es语法中的写法是`$root`
41 | 2. `global`: 从表单中传入,用于外部对表单影响, 不设置则默认为`空对象`; global在es语法中的写法是`$global`
42 | 3. `index`:数组中孩子节点(非孙子节点))项所在的索引,其它节点(非孩子)节点此值是-1. index在es语法中的写法是`$index`
43 | 4. `hidden`: 用于判断某一项是否隐藏. hidden在es语法中的写法是`$hidden("base.target")`; 括号中是路径
44 |
45 | [es写法](./parse.md#es写法)
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/base/form.md:
--------------------------------------------------------------------------------
1 | # 表单属性/事件/方法
2 |
3 | ### 实例
4 | ```html
5 |
6 |
15 |
16 |
17 |
37 | ```
38 | ## 表单属性
39 |
40 | | 事件名称 | 类型 | 默认值 | 说明
41 | | -- | -- | -- | --
42 | | value/v-model | Object | {} | 绑定值(表单值)
43 | | schema | Object | {} | 具体的表单配置
44 | | global | Object | {} | 外部值;和rootData共同影响项隐藏等
45 | | hasConsole | boolean | -- | 是否有调试控制台;当设置true或false时,优先级会高于全局的hasConsole,不设置则取全局的hasConsole
46 |
47 | ## 表单事件
48 |
49 | | 事件名称 | 说明 | 回调参数 | 备注
50 | | -- | -- | -- | --
51 | | inited | 表单初始化完成时触发 | (formValue) | schema改变时([非深度监听](https://cn.vuejs.org/v2/api/#watch)),表单会重新初始化,inited会再次调用(`inited未完成之前,用户行为事件(change、submit)暂不会触发,其它事件(input)不受影响`)
52 | | input | 表单的值有改变时触发 | (formValue, keyPath) | --
53 | | change | 表单组件改变时触发 | (formValue, keyPath, eventData) | setValue不会触发
54 | | submit | 提交表单 | (formValue) | form.submit(); 组件事件(@enterSubmit; @submit)会触发
55 | ::: warning 注意
56 | 1. 表单事件写法有两种,最好根据自已的应用场景,两者选其一:
57 | - 写法一:在`元素中直接配置`
58 | - 写法二:在schema的`actions中配置`
59 | 2. 若写法上两者都存在,两者都会触发:先触发`actions中配置`的事件,再触发`元素中直接配置`的事件;
60 | 3. 两种写法的所返回的参数是一样(见上面),跟[组件事件的参数](../base/component.md#actions组件事件)不同,但写法跟[组件事件的写法](../base/com-format.md#组件事件)一样;
61 | 4. actions函数的this指针是指向此表单。
62 | :::
63 |
64 | ## 表单方法
65 |
66 | | 属性名 | 说明 | 参数 | 备注
67 | | -- | -- | -- | --
68 | | getRef | 取元素或组件 | (name, hasEmpty, idxChain) | 类似于ref;若项组件在表单数组中,则返回来的是一个数组[见下面详解](#getref);
`注意:`隐藏的项是不会取出的
`hasEmpty`为添加
69 | | checkAll | 检查表单是否有错 | 空 | 返回true/false
70 | | submit | 触发submit事件 | 空 | --
71 | | isHidden | 判断某项是否隐藏 | ([pathKey](./explain.md#项组件路径)) | --
72 | | getGlobal | 取表单的全局数据 | 空 | 应用表单传入来的global
73 | | getRootData | 取表单根值 | 空 | 实时取值,表单存在的值,包括隐藏的或临时的
74 | | getValue | 取表单值 | 空 | 实时取值,表单存在的值;也是getRootData的别名
75 | | setValue | 设置表单值 | ([pathKey](./explain.md#项组件路径), value) | 当pathKey是Object时,值自动匹配设置;当pathKey为字符串时,则是设置某个值
76 | | getFormValue | 取表单值 | 空 | 实时取值,用户提交所需要的值,不包括隐藏的或临时的;也就是v-model
77 | | getTabsIndex | 取某一个tabs的索引 | ([pathKey](./explain.md#项组件路径)) | 返回当前tabs的索引,不是tabs返回false;(支持普通或数组tabs)
78 | | setTabsIndex | 设某一个tabs的索引 | ([pathKey](./explain.md#项组件路径), index) | 设置当前tabs的索引;(支持普通或数组tabs)
79 | | clearErrMsg | 清除错误信息 | ([pathKey](./explain.md#项组件路径), clearNext) | 1. 当pathKey没有值时则清除所有,clearNext无效;
2. 当pathKey有值时,则是清除某一项,clearNext是对pathKey的补充,是否一起清除此项的后代
80 | | reset | 重置表单值 | (onlySchema) | `onlySchema`为添加;默认为`false`
`false`:重置的值为`schema配置`和`formValue`初始化时结合的值;
`true`:重置的值为仅为`schema配置`初始化时的值
81 |
82 |
83 | ### getRef
84 | 写法:form.getRef(name, hasEmpty, idxChain)
85 | 参数:
86 | - `name` 必填;在[项组件](./component.html)中设置的ref名称
87 | - `hasEmpty` 选填;用于`数组`,默认为`false`; 当返回值是数组时,数组中是否可返回空值
88 | - `idxChain` 选填;用于`数组`; 项组件所在的[索引链](./explain.md#索引链); 具体作用是当取出是数组的时候,可以用idxChain指出来出是哪一个
89 | ::: warning 注意
90 | 注意:隐藏的项(也就是属性hidden为true, 包括自身或父类隐藏)
91 | 1. 返回值为非数组情况下:`隐藏的项`不会取出来;
92 | 2. 返回值为数组情况下:当`hasEmpty`为`false`时,则`null`(`隐藏的项`)值不会返回;当`hasEmpty`为`true`,则`null`会返回;
93 | :::
94 | [项组件actions的具体写法](./component.html#组件事件)
95 |
--------------------------------------------------------------------------------
/docs/base/format.md:
--------------------------------------------------------------------------------
1 | # 值转换
2 |
3 | 控制入值和输出值的转换
4 | > `场景`:当外部值不是表单所需要的数据或类型时(比如:el-switch的值是一个boolean值,但是后台保存的是0/1),此时就需要做转换
5 |
6 | `值转换`有两种形式写法:
7 | - `枚举`: (推荐)
8 | - `函数`: ---
9 |
10 | 字段(见`行高亮`):
11 | ```js {8,23}
12 | propName: {
13 |
14 | label: "label名称",
15 | properties: {
16 | status: {
17 | label: "名称",
18 | component: "el-switch",
19 | format: [ // 枚举写法:写成一个数组[{outer和inner一一对应}]
20 | {
21 | outer: 0, // 表单输出的值或设置时用的值
22 | inner: false // 转换为表单内部的值
23 | },
24 | {
25 | outer: 1,
26 | inner: true
27 | }
28 | ],
29 | value: false
30 | },
31 | isOpen: {
32 | label: "状态",
33 | component: "el-switch",
34 | format: { // 函数写法:写成一个对象{outer函数, inner函数}
35 | outer: value => { // 从外部值转化为内部值
36 | if (value == 0) {
37 | return false;
38 | } else {
39 | return true;
40 | }
41 | },
42 | inner: value => { // 从内部值转化为外部值
43 | if (value == true) {
44 | return 1;
45 | } else {
46 | return 0;
47 | }
48 | }
49 | },
50 | value: 1
51 | }
52 | // ... 其它项
53 | }
54 | }
55 | ```
56 |
57 |
58 |
59 | ### 实例
60 |
61 |
62 |
63 | ```html
64 |
65 |
66 |
126 | ```
127 |
128 |
129 |
130 | ### 枚举写法
131 |
132 | 写成一个数组,每一项是一个对象,对象属性包含`outer`和`inner`,它们是一一对应的。
133 | - `outer`: 表单输出的值或设置时用的值
134 | - `inner`: 转换为表单内部的值
135 |
136 | ### 函数写法
137 |
138 | 写成一个对象,format中包含两个属性`outer`和`inner`,这两个属性是一个函数,至少写一个。
139 | - `outer`: 外部数据转换为表单内部数据的函数,参数value就是外部值
140 | - `inner`: 表单内部数据转换为外部数据的函数,参数value就是内部值
141 |
142 | ::: warning 注意
143 | - 1. 只有在组件项(叶子节点)中有效;
144 | - 2. 当是函数写法时,`inner` 所值入的参数value就是表单组件的值,这个值要进行健壮性判断。比如: 表单组件有值的情况返回一个数组,没有值的情况返回一个空数组,也有可能返回null值,主要是看组件怎么实现。
145 | :::
146 |
147 |
--------------------------------------------------------------------------------
/docs/base/help.md:
--------------------------------------------------------------------------------
1 |
2 | # 帮助/help
3 |
4 | 字段:`help`
5 | 值类型有:
6 | - `string`: 提示的内容,支持html;会直接调用系统默认的help组件;支持[动态解析](./parse.md)
7 | - `object`: 一个对象,见[组件格式](./com-format.md)
8 |
9 | ### 实例:写法/位置
10 |
11 |
12 |
13 | ```html
14 |
15 |
16 |
104 | ```
105 |
106 |
107 |
108 | ### 组件详解
109 |
110 | | 属性名 | 说明 | 类型 | 可选值| 默认值
111 | | -- | -- | -- | -- | --
112 | | hidden | 控制组件是否隐藏, 支持[动态解析](./parse.md) | boolean | -- | false
113 | | 其它 | 跟[组件格式](./com-format.md)一样 | -- | -- | --
114 |
115 | ### `系统帮助组件`属性详解
116 | 也就是help.props, 支持[动态解析](./parse.md)
117 | | 属性名 | 说明 | 类型 | 可选值| 默认值
118 | | -- | -- | -- | -- | --
119 | | content | tooltip提示的内容,支持html | string | -- | ""
120 | | maxWidth | tooltip提示框的最大宽度 | number | -- | 300
121 | | href | 是否有超链接 | string | -- | ""
122 | | placement | tooltip的方向 | -- | top right bottom left | "top"
123 |
--------------------------------------------------------------------------------
/docs/base/hidden.md:
--------------------------------------------------------------------------------
1 | # 隐藏控制/隐藏值/临时值
2 |
3 | 字段(见`行高亮`):
4 | ```js {2,3,4,8,9,10}
5 | propName: {
6 | hidden: false, // 是否隐藏此项; 支持动态解析;在根节点(最外级)中无效
7 | hdValue: undefined, // 当hidden为true时,根据此设置取出隐藏时的值
8 | isTmp: false, // 临时值; 在根节点(最外级)中无效
9 | label: "label值",
10 | properties: {
11 | name: {
12 | hidden: false, // 是否隐藏此项; 在根节点(最外级)中无效
13 | hdValue: undefined, // 当hidden为true时,根据此设置取出隐藏时的值
14 | isTmp: false, // 临时值; 在根节点(最外级)中无效
15 | label: "名称",
16 | component: "el-input",
17 | value: "天天"
18 | }
19 | // ... 其它项
20 | }
21 | }
22 | ```
23 |
24 | ### 实例
25 |
26 |
27 |
28 | ```html
29 |
30 |
31 |
142 | ```
143 |
144 |
145 |
146 | ### 临时值
147 | 临时值是指此项在页面中是存在的,但表单值并不会输出。
`应用场景`:当编辑用户信息,界面显示从后台接口取出`姓名、性别、生日`等信息,但`姓名、性别`不能提交到后端修改,此时就可以把`姓名、性别`设置为临时值,表单并不会取出此两项值
148 |
149 |
150 | ### 隐藏值
151 | 隐藏值只有当当前的hidden为true时有效;当hidden为true时,hdValue的设置对此项值的影响:
152 | - `不设置/undefined`: 不取出此项的值;
153 | - `null`: 此项的值是什么照样取出;若是properties,则其子节点也要设置为null;
154 | - `其它值`: 取出此值,如值为hdValue为5,则取出为5
155 |
156 | ::: warning 注意
157 | 1. `隐藏`包括本节点的hidden为true或祖先节点存在hidden为true;
158 | 2. `临时值`的优先级比`hdValue`大,当同时符合条件时(为临时值、hidden为true且hdValue非undefined),也不会取出此项值。
159 | 3. `临时值`只是值不输出而已,它和`hdValue`一样,并不影响[根值](./explain.md#根值)的动态解析。
160 | :::
161 |
162 |
163 |
--------------------------------------------------------------------------------
/docs/base/install.md:
--------------------------------------------------------------------------------
1 | # 安装/全局配置
2 |
3 | ## npm安装
4 | ```js
5 | // 安装
6 | npm install --save vue-easy-form
7 |
8 | // 引用
9 | import esForm from "vue-easy-form";
10 | ```
11 |
12 | ## cdn安装
13 | 可以通过[unpkg.com/vue-easy-form](https://unpkg.com/browse/vue-easy-form/) 获取到最新版本的资源,在页面上引入`js`文件即可
14 | ```html
15 |
16 |
17 |
21 |
22 | // 引用
23 | var esForm = window["esForm"];
24 | ```
25 |
26 | ## 注册
27 | 注册的作用主要是:声明一个全局的`es-form`组件和表单框的基本配置。
28 | `语法`:Vue.use(esForm, options);
29 |
30 | ```js
31 | Vue.use(esForm);
32 | 或
33 | Vue.use(esForm,
34 | {
35 | rowHeight: 40,
36 | rowSpace: 20,
37 | labelWidth: 100,
38 | offsetLeft: 0,
39 | offsetRight: 0,
40 | colon: false,
41 | direction: "h",
42 | defaultCom: "input", // 如:若用element-ui, 改为el-input
43 | defaultVal: "", // 对defaultCom这个组件的value设置默认值
44 | trimDoms: ["input", "textarea", "el-input"], // 数组,空数组会全部清空
45 | hasConsole: process.env.NODE_ENV != "production" // 推荐写成动态,编译时不用修改
46 | });
47 | ```
48 | 参数(get):
49 |
50 | - esForm: 必填,所引入的esForm组件
51 | - options:非必填,全局设置
52 |
53 | ## 全局设置
54 |
55 | 表单全局配置有如下
56 | | 属性名 | 默认值 | 说明
57 | | -- | -- | --
58 | | rowHeight | 40 | 设置每一项(行)的高度;主要用于项label和项组件横向对齐
59 | | rowSpace | 20 | 整数(px) 设置项(行)与项(行)的距离
60 | | labelWidth | 100 | 整数(px) 设置项(行)的label的宽度
61 | | offsetLeft | 0 | 整数(px) 项的左偏移量
62 | | offsetRight | 0 | 整数(px) 项的右偏移量
63 | | colon | false | 是否有冒号
64 | | direction | "h" | 竖排还是横排
65 | | defaultCom | "input" | 当配置时,不写component.name时用这个
66 | | defaultVal | "" | 对defaultCom的补充,当组件为defaultCom时且没有设置默认值,则取此值;
`注:此值对其它组件不补充`
67 | | trimDoms | ["input", "textarea", "el-input"] | 指出哪些表单元素需要去掉左右两边空格
68 | | hasConsole | false | 所有的表单是否有调试控制台; 若想设置`单个表单`,可在`对应的表单`中设置hasConsole
--------------------------------------------------------------------------------
/docs/base/properties.md:
--------------------------------------------------------------------------------
1 | # 块布局/标题
2 |
3 | 字段(见`行高亮`):
4 | ```js {4,5,6,7,8,14,15}
5 | propName: {
6 | ui: { // 块(properties)的ui配置
7 |
8 | showBody: true, // 隐藏/打开切换按钮;不设置则没有切换按钮
9 | toggleTexts: ["打开", "隐藏"], // 切换按钮不同状态显示的文字
10 | type: "", // 整个块的布局类型;值:"bg","block","bg-block"或空值
11 | hasBorder: false, // 内容区是否有边框
12 | padding: undefined, // 内容区的内边距;若没有设置,则根据type和hasBorder的值进行自动取值
13 |
14 | colon: false // label中是否有冒号
15 | // ...
16 | },
17 | label: false, // 块properties的label一般都设置为false
18 | title: "标题",
19 | properties: {
20 | name: {
21 | label: "名称",
22 | component: "el-input",
23 | value: "天天"
24 | }
25 | // ... 其它项
26 | }
27 | }
28 | ```
29 |
30 | ### 实例
31 |
32 |
33 |
34 | ```html
35 |
36 |
37 |
148 | ```
149 |
150 |
151 |
152 | ### 子属性
153 | 当properties的子属性(如name)设置为`null`、`undefined`、`false`时,说明此项是不显示的; 这样写的目的是为了提高代码的可读性。
154 | > `场景`:新增和编辑可能用到同一个页面,编辑时可能某些项是不需要的,但是新增是需要填写的;写法如:name: this.$route.params.id ? false : {正常的组件}
155 |
156 | ::: warning 注意
157 | 当`properties`和`component`同时存在, `component`将失效。
158 | :::
159 |
160 |
--------------------------------------------------------------------------------
/docs/base/quickstart.md:
--------------------------------------------------------------------------------
1 | # 快速上手
2 |
3 | ### 实例
4 | ```html
5 |
6 | ```
7 |
8 | ## 标准写法
9 |
10 | 标准的表单配置从一个对象开始,里面包含一个properties,可在每一层properties这一相同的层级中设置一些影响此表单的基本配置(如ui)。
11 |
12 |
13 |
14 |
15 | ```html
16 |
65 | ```
66 |
67 |
68 |
69 | ## 简单写法
70 |
71 | 当表单的基础配置已经完成,不需要个性化设置,可以写一个简单的写法,省略掉properties这一层,直接进入表单属性
72 |
73 |
74 |
75 |
76 | ```html
77 |
111 | ```
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/docs/base/scopedSlots.md:
--------------------------------------------------------------------------------
1 | # 组件插槽
2 |
3 | 字段:`scopedSlots`
4 |
5 | ## 配置格式
6 | ``` js
7 | // attrName为某个属性的索引,如: label, title, component
8 | `attrName`: { // 若是一个对象,可写成一个Object
9 | name: "el-select", // 组件名,组件名不支持动态解析
10 |
11 | //... 组件的其它配置:如 style class props
12 |
13 | // 写法一:若是字符串,数值,布尔,函数,则可简写
14 | scopedSlots: "123", // 则默认的slotName为default
15 |
16 | // 写法二:数组
17 | scopedSlots: []; // 内容可为字符串,数值,布尔,组件对象,但不能是函数(可以用函数返回数组)
18 |
19 | // 写法三:对象(键值对):指出匹配哪个slotName
20 | scopedSlots: { // 插槽:键值对写法:具体见“组件插槽”
21 |
22 | default: { // 自定义组件,支持动态解析:scopedSlots必须指定slotName
23 | // hidden: false, // 控制是否隐藏
24 | name: "el-tag",
25 | text: "测试slot"
26 | },
27 |
28 | slotName1: "slotName1测试",
29 |
30 | slotName2: ["slotName1测试"], // 内容可为字符串,数值,布尔,组件对象,但不能是函数(可以用函数返回数组)
31 |
32 | // data => {global, rootData, root, pathKey, idxChain, index, $hidden} 见动态解析函数写法
33 | // scoped => 作用域插槽所携带的值
34 | slotName3: function(data, scoped) {
35 |
36 | // 返回值可返回:字符串,数值,布尔,组件,虚拟节点
37 | // 组件: 这里不支持动态了,用户可用data和scoped改造组件
38 | return {
39 | name: "el-tag",
40 | text: "测试slot"
41 | };
42 |
43 | // jsx写法:返回虚拟节点
44 | // 为了兼容低版本的vue,无论是否使用scoped,虚拟节点都必须在函数里面返回
45 | return {scoped.color};
46 |
47 | // 返回数组
48 | return [];
49 |
50 | // 返回null,undefined
51 | return undefined; // 说明此插槽为空
52 | }
53 | }
54 |
55 | }
56 | ```
57 | [动态解析 > 函数写法](./parse.md#函数写法)
58 |
59 | ### 实例
60 |
61 |
62 |
63 |
64 | <<< @/docs/.vuepress/components/slots/scoped.vue
65 |
66 |
67 | <<< @/docs/../src/components/units/slot.vue
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/docs/base/settings.md:
--------------------------------------------------------------------------------
1 | # schema配置
2 |
3 | ## 配置列表
4 | | 属性名 | 说明 | 类型 | 可选值| 默认值 | 备注
5 | | -- | -- | -- | -- | -- | --
6 | | [title](./title.md) | 表单块(properties)的名称 | string
object | -- | -- | properties中有效;
支持[动态解析](./parse.md)和[组件化](./com-format.md)
7 | | [ui](./ui.md) | 影响块(properties)的布局 | string
object | -- | -- | properties中有效
8 | | [rowHeight](./ui.md) | 项的行高 | number | -- | 上一级 | 不设置时,
继承上一级的ui.rowHeight
9 | | [rowSpace](./ui.md) | 与上一次项(行)之间的间隔 | number | -- | 上一级 | 不设置时,
继承上一级的ui.rowSpace
10 | | [labelWidth](./ui.md) | 项label的宽度 | number | -- | 上一级 | 不设置时,
继承上一级的ui.labelWidth
11 | | [offsetLeft](./ui.md) | 项的左边偏移量 | number | >=0 | 上一级 | 不设置时,
继承上一级的ui.offsetLeft
12 | | [offsetRight](./ui.md) | 项的右边偏移量 | number | >=0 | 上一级 | 不设置时,
继承上一级的ui.offsetRight
13 | | [colon](./ui.md) | label中是否有冒号 | boolean | -- | 上一级 | 不设置时,
继承上一级的ui.colon
14 | | [direction](./ui.md) | 项的排版方向 | string | "h" "v" | 上一级 | 不设置时,
继承上一级的ui.direction
15 | | [hidden](./hidden.md) | 是否隐藏此项 | boolean | -- | false | 支持[动态解析](./parse.md)
16 | | [hdValue](./hidden.md) | `hidden`或`祖先hidden`为true时有效 | -- | -- | -- | 值为`undefined`时:相应的字段不会取出
值为`null`时: 为正常遍历节点
`其余`: 取此值
17 | | [isTmp](./hidden.md) | 临时值 | boolean | -- | false | 表单不输出此项值,但可作为表单内部使用
18 | | [col](./d-col-group.md) | 列数(宽度) | number | 1到24 | 24 | 一行分24列
19 | | [group](./d-col-group.md) | 项与项进行分组 | string | -- | -- | 设置为分组,是`相邻`的;
只对`component`有效,对`properties`是无效的
20 | | [label](./label.md) | label设置 | strin
object | -- | false | false代表隐藏label
支持[动态解析](./parse.md)和[组件化](./com-format.md)
21 | | [component](./component.md) | 配置组件 | string
object | -- | 全局 |
22 | | [unit](./unit.md) | 对项进行补充 如:px| string | -- | -- | 支持[动态解析](./parse.md)和[组件化](./com-format.md)
23 | | [desc](./desc.md) | 对项进行描述 | string | -- | 全局 | 支持[动态解析](./parse.md)和[组件化](./com-format.md)
24 | | [help](./help.md) | 对项设置帮助 | string
object | -- | -- | 支持[动态解析](./parse.md)和[组件化](./com-format.md)
25 | | [value](./value.md) | 组件的值 | -- | -- | -- |
26 | | [isTrim](./value.md) | 是否去掉两边的空格 | boolean | -- | true |
27 | | [format](./format.md) | 数值转换 | array
object | -- | -- | 组件内有效
28 | | [array](./array.md) | 此项为数组 | string
object | -- | -- | 根节点无效
29 | | [rules](./rules.md) | 规则设置 | object | -- | -- |
30 | | layout | 布局 | object
string | "space"
"tabs"
Object | false | `区分大小写`
`space`: 是一个[占位空间](./d-col-group.md)
`tabs`:下一级为[tabs布局](./tabs.md)
31 | | [properties](./properties.md) | 下一级 | object | -- | -- | 下一级,重复上面的配置
32 | | [autoMatch](./auto-match.md) | 一二级的数据自动匹配 | boolean | -- | false | 只在根节点中有效
33 | ### [ui属性](./ui.md)
34 |
35 | 只有在properties中有效;主要是影响头部的样式和body的边距
36 |
37 | | 属性名 | 说明 | 类型 | 可选值| 默认值 | 备注
38 | | -- | -- | -- | -- | -- | --
39 | | showBody | 隐藏/打开切换按钮 | boolean | -- | -- | `不设置`代表没有切换按钮;
`设置`说明有切换按钮,值代表最先是否显示body
40 | | toggleTexts | 切换按钮不同状态显示的文字 | array | -- | ["打开", "隐藏"] | 数组长度必须是2,且内容是字符串
41 | | type | 整个块的布局类型 | string | "bg"
"block"
"bg-block"
""| -- | `block`就是前面有一竖
42 | | hasBorder | 内容区是否有边框 | boolean | -- | false | --
43 | | padding | 内容区的内边距 | number
string
array | -- | -- | `不设置`:根据实际情况调整
`整数`:20
`字符串`:"20px 10px", `数组`:[20, 10]
44 | | rowHeight | 项的行高 | number | -- | 上一级 | 会影响下一级
45 | | rowSpace | 与上一次项(行)之间的间隔 | number | -- | 上一级 | 会影响下一级
46 | | labelWidth | 项label的宽度 | number | -- | 上一级 | 会影响下一级
47 | | offsetLeft | 项的左边偏移量 | number | >=0 | 上一级 | 会影响下一级
48 | | offsetRight | 项的右边偏移量 | number | >=0 | 上一级 | 会影响下一级
49 | | colon | label中是否有冒号 | boolean | -- | 上一级 | 会影响下一级
50 | | direction | 项的排版方向 | string | "h" "v" | 上一级 | 会影响下一级
51 |
52 |
53 | ### layout属性
54 | layout有两种作用:
55 | - [占位空间](./d-col-group.md):此项什么都不显示,类似visibility: hidden;
56 | - [tabs布局](./tabs.md):对properties下的属性进行tabs布局
57 |
58 | 写法:
59 | - `String`: 字符串,值有:`space` | `tabs`
60 | - `Object`: 对象,其详细配置如下:
61 |
62 | | 属性名 | 说明 | 类型 | 可选值| 默认值 | 备注
63 | | -- | -- | -- | -- | -- | --
64 | | name | 类型 | string | space
tabs | -- | --
65 | | type | 布局类型 | string | bg
card
line | card | --
66 | | hasBorder | 内容区是否有边框 | boolean | -- | false | --
67 | | padding | 内容区的内边距 | number/string | -- | -- | `不设置`:根据实际情况调整
`整数`:20
`字符串`:"20px 10px"
`数组`:[20, 10]
68 |
69 | 注:`当name为space时,其余的属性是无效的,不用设置也可`
70 |
--------------------------------------------------------------------------------
/docs/base/title.md:
--------------------------------------------------------------------------------
1 | # 标题/title
2 |
3 | 字段:`title`,就是块(properties)的标题, 它在properties(同级)才有效
4 |
5 | 值类型有:
6 | - `false`: 默认值,没有标题
7 | - `string`: 标题;支持[动态解析](./parse.md)
8 | - `object`: 一个对象,见[组件格式](./com-format.md)
9 |
10 | ### 实例
11 |
12 |
13 |
14 | ```html
15 |
16 |
17 |
94 | ```
95 |
96 |
97 |
98 | ### 组件详解
99 |
100 | | 属性名 | 说明 | 类型 | 可选值| 默认值
101 | | -- | -- | -- | -- | --
102 | | hidden | 控制组件是否隐藏, 支持[动态解析](./parse.md) | boolean | -- | false
103 | | 其它 | 跟[组件格式](./com-format.md)一样 | -- | -- | --
104 |
105 | title和[ui属性](./settings.md#ui属性)都只有在properties中有效
106 |
107 |
--------------------------------------------------------------------------------
/docs/base/unit.md:
--------------------------------------------------------------------------------
1 | # 单位/unit
2 |
3 | 字段:`unit`
4 | 值类型有:
5 | - `string`: 补充的内容;支持[动态解析](./parse.md)
6 | - `object`: 一个对象,见[组件格式](./com-format.md)
7 |
8 |
20 |
21 | ### 实例
22 |
23 |
24 |
25 |
26 | ```html
27 |
28 |
29 |
94 | ```
95 |
96 |
97 |
98 | ### 组件详解
99 |
100 | | 属性名 | 说明 | 类型 | 可选值| 默认值
101 | | -- | -- | -- | -- | --
102 | | hidden | 控制组件是否隐藏, 支持[动态解析](./parse.md) | boolean | -- | false
103 | | 其它 | 跟[组件格式](./com-format.md)一样 | -- | -- | --
104 |
--------------------------------------------------------------------------------
/docs/base/value.md:
--------------------------------------------------------------------------------
1 | # 表单值
2 |
3 | 字段(见`行高亮`):
4 | ```js {7,8}
5 | propName: {
6 | label: "label值",
7 | properties: {
8 | name: {
9 | label: "名称",
10 | component: "el-input",
11 | isTrim: true, // 是否去掉值两边空间
12 | value: "天天" // 默认值。重置时也用到此值
13 | }
14 | // ... 其它项
15 | }
16 | }
17 | ```
18 | ### 实例
19 |
20 |
21 |
22 | ```html
23 |
24 |
25 |
90 | ```
91 |
92 |
93 |
94 | >[pathKey:项组件路径](./explain.md#项组件路径)的组成形式:各个块(properties)中的属性(如name, base)连接起来形式唯一的字符路径
95 |
96 | ### 设值
97 | 有两种方式设置:
98 | - `formValue`: 表单非深度监听此值,若要通过此值设置表单值,必须重新改变此变量的地址(`不推荐`)
99 | - `form.setValue(key, value)`: 表单设置值(`推荐`)
100 |
101 | 如:
102 | ```js
103 | // setValue参数只有一个对象,则一次性设置多个值
104 | // 设置多个值:只设置所要设置的key, 如endTime是不会改变的
105 | form.setValue({ // 多值设置
106 | name: "广告名称",
107 | base: {
108 | backgroundColor: "#f00"
109 | }
110 | });
111 |
112 | // setValue参数有两个,单设置某项的值
113 | form.setValue("base.backgroundColor", "#f00"); // 单值设置
114 | ```
115 |
116 | ### 取值
117 | 取出的值为一个对象,有两种方式取出:
118 |
119 | - `formValue`: v-model双向绑定(Vue机制,非实时)
120 | - [`form.getValue()`](./form.md#表单方法): 表单输出的值([setValue](./form.md#表单方法)过后,可即时取出,实时)
121 | 注:若想取`某个项`的值,可从上值中解析
122 |
123 |
124 | 表单的[根值](./explain.md#根值)
125 | - [`form.getRootData()`](./form.md#表单方法): 表单的根(原始)值([setValue](./form.md#表单方法)过后,可即时取出,实时)
126 |
127 | ### 去掉两边的空格/isTrim
128 | 1. 当isTrim不设置时,系统会根据全局设置的[trimDoms](./install.md#全局设置)来判断组件是否去掉空格;
129 | 2. isTrim触发的时机为change.native或者change,也就是`输入框改变,光标离开输入框时`才会触发;
130 | 3. 直接设置值([setValue](./form.md#表单方法))是不会触发change.native或者change。
--------------------------------------------------------------------------------
/docs/donate.md:
--------------------------------------------------------------------------------
1 | ::: tip 赏一杯
2 | 如果你觉得这个项目对你有所帮助,你可以帮作者买一杯咖啡以表示鼓励
3 | :::
4 |
5 |
6 |
7 |
8 |
9 | Wechat |
10 | Alipay |
11 |
12 |
13 |
14 |
15 | |
16 |
17 |
18 | |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/images/alipay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/docs/images/alipay.png
--------------------------------------------------------------------------------
/docs/images/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/docs/images/wechat.png
--------------------------------------------------------------------------------
/docs/version/overview.md:
--------------------------------------------------------------------------------
1 | # 版本总览
2 |
3 |
4 |
系统的最新版本:
5 |
6 |
7 | ## 文档版本选择
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## 当前版本记录
15 | ### 1.8.4
16 | - 优化底层的实现方法:修复`纯$attrs、$listeners实现的组件`无法双向绑定的问题
17 | ### 1.8.2
18 | - [长度](../base/d-col-group.md)增加`最大/最小`写法.
19 | - 优化[tabs](../base/tabs.md#实例)布局.
20 |
21 | ### 1.8.1
22 | - [组件事件](../base/com-format.md#组件事件)增加`true`写法.
23 |
24 | ### 1.8.0
25 | - 修复[table数组](../base/array.md)的组件不可隐藏的问题;
26 | - 优化表单[getRef](../base/form.md#表单方法)方法.
27 |
28 |
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-easy-form",
3 | "version": "1.8.6",
4 | "private": false,
5 | "description": "",
6 | "homepage": "",
7 | "author": {
8 | "name": "peter.chen",
9 | "email": "",
10 | "url": ""
11 | },
12 | "main": "dist/es-form.umd.min.js",
13 | "scripts": {
14 | "dev": "vue-cli-service serve",
15 | "serve": "vue-cli-service serve",
16 | "build": "vue-cli-service build",
17 | "release": "vue-cli-service build --target lib --name es-form ./src/package/index.js",
18 | "lint": "vue-cli-service lint",
19 | "test:unit": "vue-cli-service test:unit",
20 | "docs:dev": "vuepress dev docs",
21 | "docs:build": "vuepress build docs",
22 | "v:dev": "vuepress dev docs version",
23 | "v:build": "vuepress build docs version"
24 | },
25 | "license": "MIT",
26 | "keywords": [
27 | "vue",
28 | "easy-form",
29 | "es-form",
30 | "vue-easy-form"
31 | ],
32 | "files": [
33 | "dist",
34 | "README.md"
35 | ],
36 | "dependencies": {
37 | "vue": "^2.6.11"
38 | },
39 | "devDependencies": {
40 | "@vue/cli-plugin-babel": "^3.0.1",
41 | "@vue/cli-plugin-eslint": "^3.0.1",
42 | "@vue/cli-plugin-unit-mocha": "^3.0.1",
43 | "@vue/cli-service": "^3.0.1",
44 | "@vue/eslint-config-prettier": "^3.0.1",
45 | "@vue/test-utils": "^1.0.0-beta.20",
46 | "axios": "^0.19.2",
47 | "chai": "^4.1.2",
48 | "element-ui": "^2.13.0",
49 | "node-sass": "^4.9.0",
50 | "sass-loader": "^7.0.1",
51 | "vue-router": "^3.0.1",
52 | "vue-template-compiler": "^2.6.11",
53 | "vuepress": "^1.2.0",
54 | "webpack-api-mocker": "^1.5.15"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | easy form
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
52 |
53 |
120 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/common/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
34 |
35 |
74 |
--------------------------------------------------------------------------------
/src/components/common/main-footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
36 |
37 |
51 |
--------------------------------------------------------------------------------
/src/components/common/nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 | {{ item.name }}
16 |
17 |
23 |
24 | {{ subItem.name }}
25 |
26 |
27 |
28 |
34 |
35 | {{ item.name }}
36 |
37 |
38 |
39 |
40 |
41 |
76 |
77 |
97 |
--------------------------------------------------------------------------------
/src/components/demo-frame/schemas/operation-schema.js:
--------------------------------------------------------------------------------
1 | export default {
2 | ui: {
3 | colon: true,
4 | rowSpace: 10,
5 | labelWidth: 72
6 | },
7 | properties: {
8 | key: {
9 | label: "Key",
10 | component: {
11 | name: "el-input",
12 | props: {
13 | placeholder: "项组件路径/pathKey"
14 | }
15 | },
16 | value: "",
17 | help: {
18 | props: {
19 | content: "点击查看pathKey",
20 | href:
21 | "https://chengaohe45.github.io/vue-easy-form-docs/dist/base/explain.html#项组件路径"
22 | }
23 | }
24 | // desc: "pathKey可以为空;为空则\"值\"要写成一个Object"
25 | },
26 | value: {
27 | label: "Value",
28 | component: {
29 | name: "el-input",
30 | props: {
31 | placeholder:
32 | "es: {{$root.key}} ? '格式:整数(123),字符串(\"123\")': '格式:对象({...})'",
33 | type: "textarea",
34 | rows: 4
35 | }
36 | },
37 | value: "",
38 | rules: {
39 | required: true,
40 | emptyMsg: "值不能为空"
41 | },
42 | desc:
43 | "es: {{$root.key}} ? '对应的值(字符串两边记得加上双引号,如:\"你好\")': (!{{$global}}.fromDoc ? '必须是一个Object(可复制下面的值来试试)' : '必须是一个Object')",
44 | help: {
45 | props: {
46 | content:
47 | "es: !{{$global}}.fromDoc ? '此值会用eval解析, 所以输入要符合格式。
点击可查看值设值' : '此值会用eval解析, 所以输入要符合格式。'",
48 | href:
49 | "es: !{{$global}}.fromDoc ? 'https://chengaohe45.github.io/vue-easy-form-docs/dist/base/value.html#设值' : ''"
50 | }
51 | }
52 | }
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/src/components/help/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
63 |
64 |
96 |
--------------------------------------------------------------------------------
/src/components/register.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import help from "./help/index";
3 | import unit from "./units/unit";
4 | import desc from "./units/desc";
5 | import label from "./units/label";
6 | import title from "./units/title";
7 | import select from "./units/select";
8 |
9 | import slot from "./units/slot";
10 | let all = {
11 | register() {
12 | Vue.component("g-help", help);
13 | Vue.component("g-unit", unit);
14 | Vue.component("g-desc", desc);
15 | Vue.component("g-label", label);
16 | Vue.component("g-title", title);
17 | Vue.component("g-select", select);
18 | Vue.component("g-slot", slot);
19 | }
20 | };
21 |
22 | export default all;
23 |
--------------------------------------------------------------------------------
/src/components/tags/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | {{ tag }}
11 |
12 |
21 |
22 | {{ addText }}
29 |
30 |
31 |
32 |
120 |
121 |
148 |
--------------------------------------------------------------------------------
/src/components/units/README.md:
--------------------------------------------------------------------------------
1 | # units
2 |
3 | 这个文件夹主要放一些独立而通用的小组件,具体功能见每个小组件
4 |
5 |
--------------------------------------------------------------------------------
/src/components/units/align-desc.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ txt }}
4 |
5 |
6 |
7 |
36 |
37 |
43 |
--------------------------------------------------------------------------------
/src/components/units/checkbox-group.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 | {{ item.text }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
68 |
--------------------------------------------------------------------------------
/src/components/units/desc.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
26 |
27 |
45 |
--------------------------------------------------------------------------------
/src/components/units/label.vue:
--------------------------------------------------------------------------------
1 |
2 | 组件写法
5 |
6 |
7 |
31 |
32 |
46 |
--------------------------------------------------------------------------------
/src/components/units/radio-group.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 | {{ item.text }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
68 |
--------------------------------------------------------------------------------
/src/components/units/select.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
61 |
--------------------------------------------------------------------------------
/src/components/units/slot.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 没有插槽
5 |
6 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/src/components/units/title.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ text }}
4 |
5 |
6 |
7 |
21 |
22 |
50 |
--------------------------------------------------------------------------------
/src/components/units/unit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 单位{{ num }}
5 |
6 |
7 |
8 |
32 |
33 |
47 |
--------------------------------------------------------------------------------
/src/libs/constant.js:
--------------------------------------------------------------------------------
1 | const SYSTEM_CHECK_LOGIN = "SYSTEM_CHECK_LOGIN";
2 | const SYSTEM_PATH_RECORDS = "SYSTEM_PATH_RECORDS";
3 | const SYSTEM_LAST_LIST_STAT = "SYSTEM_LAST_LIST_STAT";
4 | const constant = {
5 | SYSTEM_CHECK_LOGIN,
6 | SYSTEM_PATH_RECORDS,
7 | SYSTEM_LAST_LIST_STAT
8 | };
9 |
10 | export default constant;
11 |
--------------------------------------------------------------------------------
/src/libs/storage.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /* -----------------------本地储存模块----------------------------- */
4 | /* 判断是否过期,如果过期则先删除 */
5 | var expiresKey = "__expires__";
6 |
7 | var d = new Date().getTime();
8 | for (var key in localStorage) {
9 | var v = localStorage.getItem(key);
10 | if (v) {
11 | try {
12 | v = window.JSON.parse(v);
13 | } catch (e) {
14 | // console.warn(v);
15 | }
16 | if (
17 | Object.prototype.toString
18 | .call(v)
19 | .toLowerCase()
20 | .indexOf("array") > 0
21 | ) {
22 | var expires = v[0] && v[0][expiresKey];
23 | if (expires && /^\d{13,}$/.test(expires) && expires <= d)
24 | localStorage.removeItem(key);
25 | }
26 | }
27 | }
28 |
29 | var storage = {
30 | /**
31 | * 保存值到本地存储
32 | * @method set
33 | * @param {String} key 需要保存的键名
34 | * @param {Object|String|Array|Boolean} value 需要保存的值
35 | * @param {Number} expires 存储的过期时间 单位:秒
36 | */
37 | set: function(key, value, expires) {
38 | var v = []; /* 这个数据将记录两个值:第一个值为过期时间,其中0为永久性保存;第二个值为用户需要保存的值 */
39 | var expiresObj = {};
40 | if (expires && typeof expires === "number") {
41 | var d = new Date().getTime();
42 | expiresObj[expiresKey] = d + expires * 1000;
43 | } else {
44 | expiresObj[expiresKey] = 0; /* 0 代表永久性保存 */
45 | }
46 | v.push(expiresObj);
47 | v.push(value);
48 | localStorage.setItem(key, window.JSON.stringify(v));
49 | },
50 | /**
51 | * 需要获取的本地存储
52 | * @method get
53 | * @param {String} key 对应的key
54 | * @return {Object|String|Array|Boolean} 返回值
55 | */
56 | get: function(key, defaultValue = "") {
57 | var value = localStorage.getItem(key);
58 | if (value === null || value === undefined) {
59 | value = defaultValue;
60 | return value;
61 | }
62 |
63 | try {
64 | value = window.JSON.parse(value);
65 | } catch (e) {
66 | return value; /* 防止以前的写法:没有经过window.JSON.stringify对setItem的值进行处理 */
67 | }
68 |
69 | if (typeof value !== "object") {
70 | return value;
71 | }
72 |
73 | var expires = value[0] && value[0][expiresKey];
74 | if (typeof expires === "number") {
75 | /* 现在的写法 */
76 | if (expires && /^\d{13,}$/.test(expires)) {
77 | var d = new Date().getTime();
78 | if (expires <= d) {
79 | localStorage.removeItem(key);
80 | return defaultValue;
81 | }
82 | }
83 | return value[1];
84 | } else {
85 | /* 为了兼容之前的业务,这样写不是现在的格式所以直接返回以前的值 */
86 | return value;
87 | }
88 | },
89 | /**
90 | * 删除一个本地存储
91 | * @method remove
92 | * @param {String} key 需要删除的key
93 | */
94 | remove: function(key) {
95 | localStorage.removeItem(key);
96 | }
97 | };
98 |
99 | export default storage;
100 |
--------------------------------------------------------------------------------
/src/libs/uri.js:
--------------------------------------------------------------------------------
1 | var uri = {
2 | query(name, fromHash) {
3 | var search = fromHash ? window.location.hash : location.search;
4 | if (search) {
5 | var regStr = "(^|\\?|&)" + name + "=([^&]*)(\\s|&|$)";
6 | var reg = new RegExp(regStr, "i");
7 | if (reg.test(search)) {
8 | return window.decodeURIComponent(RegExp.$2.replace(/\+/g, " "));
9 | }
10 | }
11 | return "";
12 | },
13 |
14 | get(name) {
15 | var val = this.query(name);
16 | if (!val) {
17 | val = this.query(name, true);
18 | }
19 | return val;
20 | },
21 |
22 | has(name, fromHash) {
23 | var search = fromHash ? window.location.hash : location.search;
24 | if (search) {
25 | var regStr = "(^|\\?|&)" + name + "=([^&]*)(\\s|&|$)";
26 | var reg = new RegExp(regStr, "i");
27 | if (reg.test(search)) {
28 | return true;
29 | }
30 | }
31 | return false;
32 | }
33 | };
34 |
35 | export default uri;
36 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App.vue";
3 | import router from "./router/index";
4 |
5 | // 引入UI样式,各模块按需引入ui组件
6 | import "element-ui/lib/theme-chalk/index.css";
7 | //引入ui组件
8 | import elementUI from "element-ui";
9 | // 引入基本样式
10 | import "@/static/css/index.scss";
11 |
12 | // import esForm from "@/package/index.js";
13 | import esForm from "vue-easy-form"; // 见vue.config.js别名
14 |
15 | import gComponent from "@/components/register.js";
16 |
17 | // 注册全局组件
18 | gComponent.register();
19 | Vue.use(elementUI);
20 | Vue.use(esForm, {
21 | rowHeight: 40,
22 | rowSpace: 20,
23 | labelWidth: 100,
24 | offsetLeft: 0,
25 | offsetRight: 0,
26 | colon: false,
27 | direction: "h",
28 | defaultCom: "el-input", // 如:若用element-ui, 改为el-input
29 | defaultVal: "", // 对defaultCom这个组件的value设置默认值
30 | trimDoms: ["input", "textarea", "el-input"], // 数组,空数组会全部清空
31 | hasConsole: process.env.NODE_ENV != "production" // 推荐写成动态,编译时不用修改
32 | });
33 |
34 | /*
35 | 打印类库的版本
36 | 当处于生产环境时:hash中存在libs_version=就行了,不宜写过多的代码来做查询判断;因为只是个调试信息,不影响功能
37 | */
38 | if (
39 | process.env.NODE_ENV != "production" ||
40 | (location.hash && location.hash.indexOf("libs_version=") >= 1)
41 | ) {
42 | console.log("esForm's version: " + esForm.version);
43 | }
44 |
45 | Vue.config.productionTip = false;
46 |
47 | window.vm = new Vue({
48 | router,
49 | render: h => h(App)
50 | }).$mount("#app");
51 |
52 | // window.onerror = function() {
53 | // alert(1);
54 | // };
55 |
--------------------------------------------------------------------------------
/src/package/README.md:
--------------------------------------------------------------------------------
1 | # vue-easy-form简介
2 |
3 | ## vue-easy-form是一个表单工具,能过配置实现自己所需要的表单效果,集布局、验证、逻辑、取值于一身
4 |
5 | ### 全局配置
6 |
7 |
--------------------------------------------------------------------------------
/src/package/components/btn.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
31 |
--------------------------------------------------------------------------------
/src/package/components/edit-bottom-btns.vue:
--------------------------------------------------------------------------------
1 |
2 |
52 |
53 |
54 |
64 |
65 |
115 |
--------------------------------------------------------------------------------
/src/package/components/edit-btns.vue:
--------------------------------------------------------------------------------
1 |
2 |
66 |
67 |
68 |
73 |
74 |
151 |
--------------------------------------------------------------------------------
/src/package/components/tabs-btn.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
33 |
--------------------------------------------------------------------------------
/src/package/components/tabs-nav-item.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
13 |
14 |
15 |
16 |
17 |
62 |
63 |
70 |
77 |
78 |
79 |
80 |
81 |
82 |
158 |
--------------------------------------------------------------------------------
/src/package/index.js:
--------------------------------------------------------------------------------
1 | // import esBase from "./base";
2 | import esForm from "./index.vue";
3 | import schemaUtils from "./libs/schema-utils";
4 |
5 | // import esRules from "./libs/rules.js";
6 | import esUtils from "./libs/utils";
7 | import esGlobal from "./libs/global.js";
8 | // import esConstant from "./libs/constant";
9 |
10 | const install = function(Vue, globalOpts = {}) {
11 | /* istanbul ignore if */
12 | if (install.installed) return;
13 |
14 | // 保存外部传入来的Vue(如用来判断vnode)
15 | esUtils.initVue(Vue);
16 |
17 | if (Object.keys(globalOpts).length > 0) {
18 | esUtils.mergeGlobal(esGlobal, globalOpts);
19 | // console.log(esGlobal);
20 | }
21 |
22 | // Vue.component(esConstant.HELP_NAME, esHelp);
23 | // Vue.component("es-base", esBase);
24 | Vue.component("es-form", esForm);
25 | };
26 |
27 | const check = function(schema) {
28 | return schemaUtils.check(schema);
29 | };
30 |
31 | /* istanbul ignore if */
32 | if (typeof window !== "undefined" && window.Vue) {
33 | install(window.Vue);
34 | }
35 |
36 | export default {
37 | version: typeof process != "undefined" ? process.env.VUE_APP_VERSION : "??",
38 | install,
39 | esForm,
40 | check
41 | };
42 |
--------------------------------------------------------------------------------
/src/package/layout/object-table.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
41 |
42 |
56 |
--------------------------------------------------------------------------------
/src/package/layout/tabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
81 |
82 |
83 |
112 |
113 |
164 |
--------------------------------------------------------------------------------
/src/package/libs/constant.js:
--------------------------------------------------------------------------------
1 | let constant = {
2 | // HELP_NAME: "es-help", //注册时用的全局名字
3 | UI_MAX_COL: 24, //整修个布局分为多少列,这个值不要随便改, 要跟object.vue的UI_MAX_COL对应
4 | WIDTH_AUTO: "auto",
5 | ARRAY_TABLE: "array-table", // 只支持properities(非叶子)
6 | ARRAY_TABS: "array-tabs",
7 | ARRAY_LEGEND: "array-legend",
8 | ARRAY_ROW: "array",
9 | ARRAY_CARD: "array-card", // 只支持组件(叶子)
10 |
11 | POINT_CENTER_CENTER: "center-center", // tip框与源icon可以居中指向
12 | POINT_ARROW_CENTER: "arrow-center", // tip框偏移,但指向源icon中间
13 | POINT_ARROW_OFFSET: "arrow-offset", // tip框偏移,也不指向源icon中间
14 |
15 | ADJ_NATIVE: "native", // 将原生事件绑定到组件; 本身是区分大小写的;keyup.native为例:原生input.text监听不到,但组件(就只有一个input.text)可以监听到事件,
16 |
17 | INPUT_EVENT: "input",
18 | CHANGE_EVENT: "change", // 应用于数组改变
19 | CLICK_EVENT: "click",
20 |
21 | TAG_INPUT: "input",
22 | TYPE_RADIO: "radio",
23 | TYPE_CHECKBOX: "checkbox",
24 |
25 | KEYUP_NATIVE: "keyup.native",
26 | ENTER_SUBMIT: "@enterSubmit",
27 | ONLY_SUBMIT: "@submit",
28 |
29 | // TYPE_TMP: "tmp", // 表单的临时值,跟组件没有什么区别;只是不会取出;使用场景:快速做标题或编辑时,某些项需要显示但又不需要提交给后台
30 | LAYOUT_SPACE: "space", // 占位符; space不可以乱改,因为其它地方(.vue)有用到
31 | LAYOUT_TABS: "tabs", // properties的子属性是tabs布局; space不可以乱改,因为其它地方(.vue)有用到
32 |
33 | UI_FORM: "_es_form_qwerty_", // 说明界面属于哪种类型
34 | UI_ITEM: "_es_item_qwerrf_",
35 | UI_ARRAY: "_es_array_aadfsd_",
36 |
37 | ES_FUNC_NAME: "__E0S1_2F3U4NC_N4AM5E__",
38 | ES_OPTIONS: "__es__Options__", // 此值要是正规的命名
39 |
40 | PREFIX_STATIC_FUNC: ["s:"], // 对于props里面的属性,以PRE_STATIC_FUNC开头的且是函数,则说明是静态,不解析
41 |
42 | // 原生的表单组件,主要是用来过滤空格
43 | FORM_INPUTS: ["input", "textarea"],
44 | INPUT_CHANGE: "change",
45 |
46 | IDX_CHAIN_KEY: "[i]", // 数组链的代替字符,不可随便改
47 |
48 | COM_TARGET_REF: "__comTarget__"
49 | };
50 |
51 | export default constant;
52 |
--------------------------------------------------------------------------------
/src/package/libs/data-cache.js:
--------------------------------------------------------------------------------
1 | /**
2 | * data-cache.js
3 | *
4 | * Copyright (c) 2020 chengaohe All rights reserved.
5 | *
6 | * 用于记录表单数据
7 | *
8 | */
9 |
10 | // import utils from "./utils";
11 | // import constant from "./constant";
12 |
13 | var formDataMap = {};
14 | // window.formDataMap = formDataMap;
15 | let dataCache = {
16 | __getData(formId) {
17 | if (!formDataMap[formId]) {
18 | formDataMap[formId] = {};
19 | }
20 | return formDataMap[formId];
21 | },
22 |
23 | getRoot(formId, defaultValue) {
24 | var data = this.__getData(formId);
25 | return data.root ? data.root : defaultValue;
26 | },
27 |
28 | setRoot(formId, value) {
29 | var data = this.__getData(formId);
30 | data.root = value;
31 | },
32 |
33 | getGlobal(formId, defaultValue) {
34 | var data = this.__getData(formId);
35 | return data.global ? data.global : defaultValue;
36 | },
37 |
38 | setGlobal(formId, value) {
39 | var data = this.__getData(formId);
40 | data.global = value;
41 | },
42 |
43 | getHiddenFunc(formId) {
44 | var data = this.__getData(formId);
45 | return data.hiddenFunc;
46 | },
47 |
48 | setHiddenFunc(formId, value) {
49 | var data = this.__getData(formId);
50 | data.hiddenFunc = value;
51 | },
52 |
53 | remove(formId) {
54 | if (formDataMap.hasOwnProperty(formId)) {
55 | formDataMap[formId].hiddenFunc = null;
56 | delete formDataMap[formId];
57 | }
58 | }
59 | };
60 | // window.dataCache = dataCache;
61 |
62 | export default dataCache;
63 |
--------------------------------------------------------------------------------
/src/package/libs/drag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 点击拖拽
3 | * @param {*} event mousedown事件
4 | * @param {*} callback 回调函数。mousemove返回偏移量,mouseup返回false
5 | */
6 | function esDrag(event, callback) {
7 | // The mouse position (in window coordinates)
8 | // at which the drag begins
9 |
10 | var cb = callback;
11 |
12 | var startX = event.clientX,
13 | startY = event.clientY;
14 | // var deltaX = startX - origX, deltaY = startY - origY;
15 |
16 | document.addEventListener("mousemove", moveHandler, true);
17 | document.addEventListener("mouseup", upHandler, true);
18 | window.addEventListener("blur", upHandler, true);
19 |
20 | // We've handled this event. Don't let anybody else see it.
21 | if (event.stopPropagation) event.stopPropagation();
22 | // DOM Level 2
23 | else event.cancelBubble = true; // IE
24 |
25 | // Now prevent any default action.
26 | if (event.preventDefault) event.preventDefault();
27 | // DOM Level 2
28 | else event.returnValue = false; // IE
29 |
30 | /**
31 | * This is the handler that captures mousemove events when an element
32 | * is being dragged. It is responsible for moving the element.
33 | **/
34 | function moveHandler(e) {
35 | if (!e) e = window.event; // IE Event Model
36 |
37 | // Move the element to the current mouse position, adjusted as
38 | // necessary by the offset of the initial mouse-click.
39 | // elementToDrag.style.left = (e.clientX - deltaX) + "px";
40 | // elementToDrag.style.top = (e.clientY - deltaY) + "px";
41 |
42 | var offsetX = e.clientX - startX;
43 | var offsetY = e.clientY - startY;
44 |
45 | cb({ offsetX, offsetY });
46 |
47 | // And don't let anyone else see this event.
48 | if (e.stopPropagation) e.stopPropagation();
49 | // DOM Level 2
50 | else e.cancelBubble = true; // IE
51 | }
52 |
53 | /**
54 | * This is the handler that captures the final mouseup event that
55 | * occurs at the end of a drag.
56 | **/
57 | function upHandler(e) {
58 | if (!e) e = window.event; // IE Event Model
59 |
60 | document.removeEventListener("mousemove", moveHandler, true);
61 | document.removeEventListener("mouseup", upHandler, true);
62 | window.removeEventListener("blur", upHandler, true);
63 |
64 | cb(false);
65 |
66 | cb = null;
67 |
68 | // And don't let the event propagate any further.
69 | if (e.stopPropagation) e.stopPropagation();
70 | // DOM Level 2
71 | else e.cancelBubble = true; // IE
72 | }
73 |
74 | event = null;
75 | callback = null;
76 | }
77 |
78 | export default esDrag;
79 |
--------------------------------------------------------------------------------
/src/package/libs/global.js:
--------------------------------------------------------------------------------
1 | // import constant from "./constant";
2 | // import esHelp from "../components/help.vue";
3 |
4 | let global = {
5 | boxRowHeight: 40,
6 | boxRowSpace: 20,
7 | boxLabelWidth: 100,
8 |
9 | rowHeight: 40,
10 | rowSpace: 20,
11 | labelWidth: 100,
12 | colon: false,
13 | direction: "h",
14 | offsetLeft: 0, // 项的左边偏移
15 | offsetRight: 0, // // 项的右边偏移
16 |
17 | defaultCom: "input",
18 | defaultVal: "", // 这个是对defaultCom的补充,当组件为defaultCom时且没有设置默认值,则取此值;注:此值对其它组件不补充
19 | // help: { name: esHelp, props: {} },
20 | trimDoms: ["input", "textarea", "el-input"],
21 | // 经测试:在el-input里,change比change.native先触发
22 | hasConsole: false, // 是否有控制台,默认为false
23 | trimEvent:
24 | "change.native" /* 这个是form的表单项改变时,此事件触发后会去掉左右两边空格;一般都是change, 因为有些类库可以喜欢写changed */
25 | };
26 |
27 | export default global;
28 |
--------------------------------------------------------------------------------
/src/package/libs/schema-rules.js:
--------------------------------------------------------------------------------
1 | import utils from "./utils";
2 | import parse from "./parse";
3 |
4 | let schemaRules = {
5 | isFunc: function(value) {
6 | return utils.isFunc(value);
7 | },
8 |
9 | isEs: function(value) {
10 | return parse.isEsScript(value);
11 | },
12 |
13 | isStr: function(value) {
14 | return utils.isStr(value);
15 | },
16 |
17 | /**
18 | * [数值范围]
19 | * @param {[String]} value [description]
20 | * @return {Boolean} [description]
21 | */
22 | // range: function(value, min, max, isInt) {
23 | // if (utils.isNum(value)) {
24 | // if (isInt) {
25 | // return schemaRules.isInt(value, min, max);
26 | // } else {
27 | // var tmpMin = min;
28 | // var tmpMax = max;
29 | // if (utils.isNum(min) && utils.isNum(min) && min > max) {
30 | // tmpMin = max;
31 | // tmpMax = min;
32 | // }
33 |
34 | // if (utils.isNum(tmpMin) && value < tmpMin) {
35 | // return false;
36 | // } else if (utils.isNum(tmpMax) && value > tmpMax) {
37 | // return false;
38 | // } else {
39 | // return true;
40 | // }
41 | // }
42 | // } else {
43 | // return false;
44 | // }
45 | // },
46 |
47 | /**
48 | * [isInt 是否整数]
49 | * @param {[Number or String]} value [description]
50 | * @param {[Number]} min [最小值(包含此值),因为这个很常用, 不写就不比较]
51 | * @return {Boolean} [description]
52 | */
53 | isInt: function(value, min, max) {
54 | var tmpMin = min;
55 | var tmpMax = max;
56 | if (utils.isNum(min) && utils.isNum(min) && min > max) {
57 | tmpMin = max;
58 | tmpMax = min;
59 | }
60 | if (utils.isNum(value) && /^\d+$/.test(value + "")) {
61 | if (utils.isNum(tmpMin) && value < tmpMin) {
62 | return false;
63 | } else if (utils.isNum(tmpMax) && value > tmpMax) {
64 | return false;
65 | } else {
66 | return true;
67 | }
68 | } else {
69 | return false;
70 | }
71 | }
72 | };
73 |
74 | export default schemaRules;
75 |
--------------------------------------------------------------------------------
/src/package/libs/submit.js:
--------------------------------------------------------------------------------
1 | /* 应用于表单组件函数,不能作为其它用途:因为this代表的是表单 */
2 | let onlySubmit = function() {
3 | this.submit();
4 | };
5 |
6 | /**
7 | * 应用于表单组件函数,不能作为其它用途:因为this代表的是表单, event代表的是keyup事件
8 | * 参数就是组件事件所对就的参数
9 | * @param {*} value 当前组件的值
10 | * @param {*} key 组件的源key
11 | * @param {*} event 事件所携带的事件
12 | */
13 | let enterSubmit = function(options) {
14 | // console.log("value: ", value);
15 | // console.log("key: ", key);
16 | if (options.event && options.event.keyCode === 13) {
17 | this.submit();
18 | }
19 | };
20 |
21 | export { enterSubmit, onlySubmit };
22 |
--------------------------------------------------------------------------------
/src/package/libs/tabs-observer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tabs-observer.js
3 | *
4 | * Copyright (c) 2019 chengaohe All rights reserved.
5 | *
6 | * 用于监听tabs的变化: 这样做的目标是使所有的tabs都来自同上个监听
7 | *
8 | */
9 |
10 | import utils from "./utils";
11 |
12 | let tabsObserver = {
13 | __INTERVAL: 50,
14 | __listeners: [],
15 | __observer: null,
16 | __resizeWinHandler: null,
17 | __throttleTimer: null,
18 |
19 | /**
20 | * 移除某个监听
21 | * @param {*} callback
22 | */
23 | add(callback) {
24 | if (utils.isFunc(callback)) {
25 | if (this.__listeners.length <= 0) {
26 | this.__listeners.push(callback);
27 | this.__start();
28 | } else if (!this.__listeners.includes(callback)) {
29 | this.__listeners.push(callback);
30 | }
31 | } else {
32 | console.warn("tabsObserver.add(callback)中callback不是一个函数");
33 | }
34 | },
35 |
36 | /**
37 | * 移除某个监听
38 | * @param {*} callback
39 | */
40 | remove(callback) {
41 | var index = this.__listeners.indexOf(callback);
42 | if (index >= 0) {
43 | this.__listeners.splice(index, 1);
44 | if (this.__listeners.length <= 0) {
45 | this.__stop();
46 | }
47 | }
48 | },
49 |
50 | /**
51 | * 窗体改变时执行回调:当是resize时entries为undefined
52 | */
53 | __observerHandler(entries) {
54 | this.__listeners.forEach(cbItem => {
55 | cbItem(entries);
56 | });
57 | },
58 |
59 | /**
60 | * 启动监听
61 | */
62 | __start() {
63 | if (this.__observer) {
64 | return true;
65 | // 已经存在,不用重构
66 | }
67 |
68 | // console.log("start...");
69 |
70 | this.__resizeWinHandler = () => {
71 | if (!this.__throttleTimer)
72 | this.__throttleTimer = setTimeout(() => {
73 | this.__throttleTimer = null;
74 | this.__observerHandler();
75 | }, this.__INTERVAL);
76 | };
77 |
78 | window.addEventListener("resize", this.__resizeWinHandler, true);
79 |
80 | let MutationObserver =
81 | window.MutationObserver ||
82 | window.WebKitMutationObserver ||
83 | window.MozMutationObserver;
84 |
85 | this.__observer = new MutationObserver(entries => {
86 | this.__observerHandler(entries);
87 | });
88 |
89 | this.__observer.observe(document.body, {
90 | childList: true,
91 | subtree: true,
92 | attributes: true,
93 | characterData: true
94 | });
95 | },
96 |
97 | /**
98 | * 停止监听
99 | */
100 | __stop() {
101 | // console.log("stop...");
102 | if (this.__observer) {
103 | this.__observer.disconnect();
104 | this.__observer.takeRecords();
105 | this.__observer = null;
106 | }
107 |
108 | if (this.__resizeWinHandler) {
109 | window.removeEventListener("resize", this.__resizeWinHandler, true);
110 | this.__resizeWinHandler = null;
111 | }
112 |
113 | if (this.__throttleTimer) {
114 | clearTimeout(this.__throttleTimer);
115 | this.__throttleTimer = null;
116 | }
117 | }
118 | };
119 |
120 | export default tabsObserver;
121 |
--------------------------------------------------------------------------------
/src/package/mixins/array-del-pop-mixin.js:
--------------------------------------------------------------------------------
1 | // import utils from "../libs/utils";
2 | import popUtils from "../libs/pop-utils";
3 | export default {
4 | data() {
5 | return {
6 | showPop: false,
7 | popDom: null,
8 | clickDocHandler: null,
9 | scrollWinHandler: null,
10 | placement: "bottom",
11 |
12 | popPosition: {
13 | left: 0,
14 | top: 0,
15 | zindex: 1
16 | },
17 |
18 | popArrow: {
19 | direction: "",
20 | left: 0,
21 | top: 0
22 | },
23 |
24 | popInfo: {
25 | popBorderRadius: 4,
26 | viewSpace: 2, // 距离判断边预留的空间
27 | popArrowWH: 6, // 箭头的宽度和高度
28 | betweenSpace: 3 // pop与icon的空间
29 | }
30 | };
31 | },
32 |
33 | props: {
34 | hasDelete: {
35 | type: Boolean,
36 | required: false,
37 | default: false
38 | },
39 | hasDelWarn: {
40 | type: Boolean,
41 | required: false,
42 | default: true
43 | },
44 | index: {
45 | type: [Number, String],
46 | required: false,
47 | default: -1 // 设置为-1;低于索引值
48 | },
49 | delMsg: {
50 | type: Object,
51 | required: false,
52 | default: () => {
53 | return {};
54 | }
55 | },
56 | delWarnBtns: {
57 | type: Array,
58 | required: false,
59 | default: () => {
60 | return [];
61 | }
62 | },
63 | info: {
64 | type: Object,
65 | required: true,
66 | default: () => {
67 | return {};
68 | }
69 | }
70 | },
71 |
72 | computed: {
73 | canPop() {
74 | return this.hasDelWarn;
75 | }
76 | },
77 |
78 | created() {
79 | if (this.canPop) {
80 | this.$data.clickDocHandler = event => {
81 | if (!this.isFromDelBtn(event.target)) {
82 | this.$data.showPop = false;
83 | this.cancelDocListen();
84 | } else {
85 | //...
86 | }
87 | };
88 |
89 | this.$data.scrollWinHandler = () => {
90 | this.adjustPop();
91 | };
92 | }
93 | },
94 |
95 | methods: {
96 | showPopHandler() {
97 | // 有警告
98 | this.$data.showPop = !this.$data.showPop;
99 | if (this.$data.showPop) {
100 | this.addPopDom();
101 | this.setDocListen();
102 | this.$data.popPosition.zindex = popUtils.getMaxZIndex() + 1;
103 | // console.log("this.$data.popPosition.zindex = ", this.$data.popPosition.zindex);
104 | this.$nextTick(() => {
105 | this.adjustPop();
106 | });
107 | } else {
108 | this.cancelDocListen();
109 | }
110 | },
111 |
112 | clickDeletBtn() {
113 | if (
114 | this.canPop &&
115 | this.hasDelWarn &&
116 | (!this.delMsg.hidden && (this.delMsg.name || this.delMsg.text))
117 | ) {
118 | this.showPopHandler();
119 | } else {
120 | // 没有警告
121 | this.$emit("delItem", this.index);
122 | }
123 | },
124 |
125 | clickPopConfirm() {
126 | this.$data.showPop = false;
127 | this.$emit("delItem", this.index);
128 | this.cancelDocListen();
129 | },
130 |
131 | closePop() {
132 | this.$data.showPop = false;
133 | this.cancelDocListen();
134 | },
135 |
136 | setDocListen() {
137 | window.addEventListener("scroll", this.$data.scrollWinHandler, true);
138 | document.addEventListener("click", this.$data.clickDocHandler);
139 | },
140 |
141 | cancelDocListen() {
142 | document.removeEventListener("click", this.$data.clickDocHandler);
143 | window.removeEventListener("scroll", this.$data.scrollWinHandler, true);
144 | },
145 |
146 | isFromDelBtn(target) {
147 | var myDelBtn = this.$refs["delBtn"];
148 | if (!this.$refs["delBtn"]) {
149 | return false;
150 | }
151 | myDelBtn = myDelBtn.$el ? myDelBtn.$el : myDelBtn;
152 | while (target) {
153 | if (target == myDelBtn) {
154 | return true;
155 | }
156 | target = target.parentNode;
157 | }
158 | return false;
159 | },
160 |
161 | adjustPop() {
162 | var pop = this.$refs["pop"];
163 | var icon = this.$refs["delBtn"].$el
164 | ? this.$refs["delBtn"].$el
165 | : this.$refs["delBtn"];
166 | var popRect = pop.getBoundingClientRect();
167 | var iconRect = icon.getBoundingClientRect();
168 |
169 | var uiInfo = popUtils.getPopUiInfo(
170 | popRect,
171 | iconRect,
172 | this.$data.popInfo,
173 | this.$data.placement
174 | );
175 | if (uiInfo) {
176 | this.$data.popPosition = { left: uiInfo.popLeft, top: uiInfo.popTop };
177 | this.$data.popArrow = {
178 | direction: uiInfo.arrowDirection,
179 | left: uiInfo.arrowLeft,
180 | top: uiInfo.arrowTop
181 | };
182 | } else {
183 | this.closePop();
184 | }
185 | },
186 |
187 | addPopDom() {
188 | if (!this.$data.popDom) {
189 | this.$data.popDom = this.$refs.pop;
190 | document.body.appendChild(this.$data.popDom);
191 | }
192 | },
193 |
194 | removePopDom() {
195 | if (this.$data.popDom) {
196 | document.body.removeChild(this.$data.popDom);
197 | this.$data.popDom = null;
198 | }
199 | }
200 | },
201 |
202 | destroyed() {
203 | if (this.$data.scrollWinHandler || this.$data.clickDocHandler) {
204 | this.$data.showPop = false;
205 | this.cancelDocListen();
206 |
207 | this.$data.clickDocHandler = null;
208 | this.$data.scrollWinHandler = null;
209 | }
210 | this.removePopDom();
211 | }
212 | };
213 |
--------------------------------------------------------------------------------
/src/package/mixins/array-edit-item-mixin.js:
--------------------------------------------------------------------------------
1 | export default {
2 | data() {
3 | return {};
4 | },
5 |
6 | props: {
7 | canDelete: {
8 | type: Boolean,
9 | required: false,
10 | default: true
11 | },
12 | hasSort: {
13 | type: Boolean,
14 | required: false,
15 | default: false
16 | },
17 | isFirst: {
18 | type: Boolean,
19 | required: false,
20 | default: false
21 | },
22 | isLast: {
23 | type: Boolean,
24 | required: false,
25 | default: false
26 | },
27 | fixed: {
28 | // 固定行(前几)
29 | type: Number,
30 | required: false,
31 | default: 0
32 | }
33 | },
34 |
35 | methods: {
36 | upItem() {
37 | if (!this.isFirst) {
38 | this.$emit("upItem", this.index);
39 | }
40 | },
41 |
42 | downItem() {
43 | if (!this.isLast) {
44 | this.$emit("downItem", this.index);
45 | }
46 | }
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/src/package/mixins/item-mixin.js:
--------------------------------------------------------------------------------
1 | export default {
2 | props: {
3 | schema: {
4 | type: Object,
5 | default() {
6 | return {};
7 | }
8 | }
9 | }
10 | };
11 |
--------------------------------------------------------------------------------
/src/package/static/css/index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import "mixins.scss";
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/package/static/css/mixins.scss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @mixin display-flex {
5 | display: -webkit-box;
6 | display: -ms-flexbox;
7 | display: flex;
8 | }
9 |
10 | @mixin inline-flex {
11 | display: -webkit-inline-box;
12 | display: -ms-inline-flexbox;
13 | display: inline-flex;
14 | }
15 |
16 | @mixin direction-h {
17 | -webkit-box-orient: horizontal;
18 | -webkit-box-direction: normal;
19 | -ms-flex-direction: row;
20 | flex-direction: row;
21 | }
22 |
23 | @mixin direction-v {
24 | -webkit-box-orient: vertical;
25 | -webkit-box-direction: normal;
26 | -ms-flex-direction: column;
27 | flex-direction: column;
28 | }
29 |
30 | @mixin display-center {
31 | @include display-flex;
32 | align-items: center;
33 | justify-content: center;
34 | }
35 |
36 | @mixin inline-center {
37 | @include inline-flex;
38 | align-items: center;
39 | justify-content: center;
40 | }
41 |
42 |
43 | @mixin flex-fixed {
44 | flex-grow: 0;
45 | flex-shrink: 0;
46 | flex-basis: auto;
47 | }
48 |
49 | @mixin flex-full {
50 | -webkit-box-flex: 1;
51 | -ms-flex: 1;
52 | flex: 1;
53 | -ms-flex-preferred-size: auto;
54 | flex-basis: auto;
55 | }
56 |
57 | @mixin border-box {
58 | -wekit-box-sizing: border-box;
59 | box-sizing: border-box;
60 | }
61 |
62 | @mixin clear {
63 | &:before,
64 | &:after{ content: '.'; display: block; overflow: hidden; visibility: hidden; font-size: 0; line-height: 0; width: 0; height: 0;}
65 | &:after{ clear: both; }
66 | }
67 |
68 | @mixin triangle($borderColor, $borderWidth, $direction, $test: 1) {
69 | margin: 0;
70 | padding: 0;
71 | width: 0;
72 | height: 0;
73 | font-size: 0;
74 | border-style: solid;
75 | @if $direction == "top" {
76 | border-width: 0 $borderWidth $borderWidth $borderWidth;
77 | border-color: transparent transparent $borderColor transparent;
78 | } @else if $direction == "right" {
79 | border-width: $borderWidth 0 $borderWidth $borderWidth;
80 | border-color: transparent transparent transparent $borderColor;
81 | } @else if $direction == "bottom" {
82 | border-width: $borderWidth $borderWidth 0 $borderWidth;
83 | border-color: $borderColor transparent transparent transparent;
84 | } @else { /* left */
85 | border-width: $borderWidth $borderWidth $borderWidth 0;
86 | border-color: transparent $borderColor transparent transparent;
87 | }
88 | }
89 |
90 | @mixin arrow($borderColor, $borderWidth, $direction, $bgColor) {
91 | @include triangle($borderColor, $borderWidth, $direction);
92 | position: relative;
93 | &:before {
94 | content: "";
95 | display: block;
96 | @include triangle($bgColor, $borderWidth, $direction);
97 | margin: 0;
98 | // padding: 0;
99 | position: absolute;
100 | @if $direction == "top" {
101 | top: 1px;
102 | left: -$borderWidth;
103 | } @else if $direction == "right" {
104 | top: -$borderWidth;
105 | left: -$borderWidth - 1;
106 | } @else if $direction == "bottom" {
107 | top: -$borderWidth - 1;
108 | left: -$borderWidth;
109 | } @else { /* left */
110 | top: -$borderWidth;
111 | left: 1px;
112 | }
113 | }
114 | }
115 |
116 | @mixin border-top-radius($radius) {
117 | border-top-right-radius: $radius;
118 | border-top-left-radius: $radius;
119 | }
120 | @mixin border-right-radius($radius) {
121 | border-bottom-right-radius: $radius;
122 | border-top-right-radius: $radius;
123 | }
124 | @mixin border-bottom-radius($radius) {
125 | border-bottom-right-radius: $radius;
126 | border-bottom-left-radius: $radius;
127 | }
128 | @mixin border-left-radius($radius) {
129 | border-bottom-left-radius: $radius;
130 | border-top-left-radius: $radius;
131 | }
132 |
133 | $g_borderColor: #dcdfe6;
134 | $g_bgColor: #f5f7fa;
135 | $g_bgColor2: #d6d7da;
136 | $g_borderRadius: 4px;
137 | $g_errorColor: #f64a4a;
138 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Router from "vue-router";
3 |
4 | import navRoute from "./nav-route";
5 |
6 | // 路由配置从方法调用,变成了对象传递
7 | const routes = navRoute.getPages();
8 |
9 | Vue.use(Router);
10 |
11 | export default new Router({
12 | routes
13 | });
14 |
--------------------------------------------------------------------------------
/src/static/css/index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import "mixins.scss";
3 |
4 |
5 | button, input, select, textarea {
6 | font-family: inherit;
7 | font-size: inherit;
8 | line-height: inherit;
9 | color: inherit;
10 | }
11 |
12 | ol, li {
13 | margin: 0;
14 | padding: 0;
15 | list-style: none;
16 | }
17 |
18 | html, body {
19 | /*font-family: "Microsoft YaHei", "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","\5FAE\8F6F\96C5\9ED1",Arial,sans-serif;*/
20 | /*font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,SimSun,sans-serif;*/
21 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
22 | margin: 0;
23 | padding: 0;
24 | height: 100%;
25 | // overflow: hidden;
26 | background: #fff;
27 | min-width: 900px;
28 | color: #333;
29 | }
30 |
31 | .g-full-height {
32 | height: 100%;
33 | }
34 |
35 | .g-center {
36 | @include display-center;
37 | }
38 |
39 | .g-custom-loading .el-loading-spinner {
40 | width: 100px;
41 | height: 100px;
42 | margin-top: -40px;
43 | margin-left: -40px;
44 | top: 50%;
45 | left: 50%;
46 | border-radius: 4px;
47 | background: rgba(0, 0, 0, 0.3);
48 | @include display-center;
49 | @include direction-v;
50 |
51 | }
52 |
53 | .g-hidden-box {
54 | width: 0;
55 | height: 0;
56 | overflow: hidden;
57 | }
58 |
59 | .g-header {
60 | margin: 0;
61 | padding: 0;
62 | display: block;
63 | /*height: 60px;*/
64 | background-color: rgb(64, 158, 255);
65 | color: #fff;
66 | overflow: hidden;
67 | position: relative;
68 | }
69 |
70 | .g-title {
71 | margin: 0;
72 | padding: 0 0 0 10px;
73 | line-height: 60px;
74 | text-align: left;
75 | font-size: 25px;
76 | }
77 |
78 |
79 | .g-nav-aside {
80 | margin: 0;
81 | padding: 0;
82 | display: block;
83 | background-color: #fff;
84 | text-align: left;
85 | /*width: $g_navAsideWidth;*/
86 | @include border-box;
87 | border-right: 1px solid #e6e6e6;
88 | }
89 |
90 | .g-full-main {
91 | margin: 0;
92 | padding: 0;
93 | }
94 |
95 | .g-main-container {
96 | height: 100%;
97 | overflow: hidden;
98 | }
99 |
100 |
101 | .g-main-box {
102 | margin: 0;
103 | padding: 0;
104 | height: 100%;
105 | overflow: auto;
106 | text-align: left;
107 | }
108 |
109 | .g-form-box {
110 | padding-top: 30px;
111 | max-width: 700px;
112 | margin-right: auto;
113 | }
114 |
115 | .g-main-box .g-body-box {
116 | margin: 0;
117 | padding: 0px $g_bodyPaddingLeft 0px $g_bodyPaddingLeft;
118 | }
119 |
120 | .g-single {
121 | white-space: nowrap;
122 | line-height: 20px;
123 | }
124 |
125 | .g-interval-row {
126 | background-color: #fcfcfc;
127 |
128 | &:hover {
129 | background-color: #eff2f7;
130 | }
131 | }
132 |
133 | .g-footer-page-wrap {
134 | padding-top: 10px;
135 | }
136 |
137 | .g-footer-btn-wrap {
138 | padding-top: 10px;
139 | }
140 |
141 | .g-main-table {
142 | width: 100%;
143 | height: 100%;
144 | }
145 |
146 | .g-table-header {
147 | background-color: #f5f7fa !important;
148 | }
149 |
150 | .g-table-header th {
151 | padding-top: 8px !important;
152 | padding-bottom: 8px !important;
153 | background: #f5f7fa;
154 | }
155 |
156 | .g-table-cell {
157 | padding-top: 5px !important;
158 | padding-bottom: 5px !important;
159 | }
160 |
161 | .g-edit-dialog .el-dialog {
162 | max-width: 800px;
163 | min-width: 600px;
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/src/static/css/mixins.scss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | @mixin display-flex {
5 | display: -webkit-box;
6 | display: -ms-flexbox;
7 | display: flex;
8 | }
9 |
10 | @mixin direction-h {
11 | -webkit-box-orient: horizontal;
12 | -webkit-box-direction: normal;
13 | -ms-flex-direction: row;
14 | flex-direction: row;
15 | }
16 |
17 | @mixin direction-v {
18 | -webkit-box-orient: vertical;
19 | -webkit-box-direction: normal;
20 | -ms-flex-direction: column;
21 | flex-direction: column;
22 | }
23 |
24 | @mixin display-center {
25 | @include display-flex;
26 | align-items:center;
27 | justify-content: center;
28 | }
29 |
30 | @mixin flex-fixed {
31 | flex-grow: 0;
32 | flex-shrink: 0;
33 | flex-basis: auto;
34 | }
35 |
36 | @mixin flex-full {
37 | -webkit-box-flex: 1;
38 | -ms-flex: 1;
39 | flex: 1;
40 | -ms-flex-preferred-size: auto;
41 | flex-basis: auto;
42 | }
43 |
44 | @mixin border-box {
45 | -wekit-box-sizing: border-box;
46 | box-sizing: border-box;
47 | }
48 |
49 | @mixin clear {
50 | &:before,
51 | &:after{ content: '.'; display: block; overflow: hidden; visibility: hidden; font-size: 0; line-height: 0; width: 0; height: 0;}
52 | &:after{ clear: both; }
53 | }
54 |
55 | @mixin triangle($borderColor, $borderWidth, $direction, $test: 1) {
56 | margin: 0;
57 | padding: 0;
58 | width: 0;
59 | height: 0;
60 | font-size: 0;
61 | border-style: solid;
62 | @if $direction == "top" {
63 | border-width: 0 $borderWidth $borderWidth $borderWidth;
64 | border-color: transparent transparent $borderColor transparent;
65 | } @else if $direction == "right" {
66 | border-width: $borderWidth 0 $borderWidth $borderWidth;
67 | border-color: transparent transparent transparent $borderColor;
68 | } @else if $direction == "bottom" {
69 | border-width: $borderWidth $borderWidth 0 $borderWidth;
70 | border-color: $borderColor transparent transparent transparent;
71 | } @else { /* left */
72 | border-width: $borderWidth $borderWidth $borderWidth 0;
73 | border-color: transparent $borderColor transparent transparent;
74 | }
75 | }
76 |
77 | @mixin arrow($borderColor, $borderWidth, $direction, $bgColor) {
78 | @include triangle($borderColor, $borderWidth, $direction);
79 | position: relative;
80 | &:before {
81 | content: "";
82 | display: block;
83 | @include triangle($bgColor, $borderWidth, $direction);
84 | margin: 0;
85 | // padding: 0;
86 | position: absolute;
87 | @if $direction == "top" {
88 | top: 1px;
89 | left: -$borderWidth;
90 | } @else if $direction == "right" {
91 | top: -$borderWidth;
92 | left: -$borderWidth - 1;
93 | } @else if $direction == "bottom" {
94 | top: -$borderWidth - 1;
95 | left: -$borderWidth;
96 | } @else { /* left */
97 | top: -$borderWidth;
98 | left: 1px;
99 | }
100 | }
101 | }
102 |
103 | $g_bodyPaddingLeft: 20px;
104 |
105 | $g_navAsideWidth: 220px;
106 |
107 |
--------------------------------------------------------------------------------
/src/views/README.md:
--------------------------------------------------------------------------------
1 | # 页面文件的命名规则和存在路径,虽不是固定,但力求统一
2 |
3 | ## 所有的模块都存放在views文件夹下,views下面的每一个文件夹代表一种模块(如news, home等),模块文件夹再分页面, modals, components
4 |
5 | ### 页面
6 | ```
7 | list.vue rule-list.vue maker.vue rule-maker.vue
8 | ```
9 |
10 | ### modal编辑内容(某个模块下),存放在modals文件夹
11 | ```
12 | maker.vue rule-maker.vue(原理,同上)
13 | ```
14 |
15 | ### 组件(某个模块下),存放在components文件夹
16 | ```
17 | 无要求,最好根据功能自由命名
18 | ```
19 |
--------------------------------------------------------------------------------
/src/views/demo/array-card.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 字段array
10 |
11 |
12 |
13 |
14 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/views/demo/array-legend.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 字段array
10 |
11 |
12 |
13 |
14 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/views/demo/array-row.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 字段array
10 |
11 |
12 |
13 |
14 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/src/views/demo/array-table.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 字段array
10 |
11 |
12 |
13 |
14 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/src/views/demo/array-tabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | 字段array
10 |
11 |
12 |
13 |
14 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/views/demo/component.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
101 |
102 |
115 |
--------------------------------------------------------------------------------
/src/views/demo/desc.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/views/demo/help.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/src/views/demo/properties.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/src/views/demo/scopedSlots.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
141 |
142 |
155 |
--------------------------------------------------------------------------------
/src/views/demo/simple.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 只需求配置label、component、value即可;(注:esForm框架并不依赖于element-ui,只是编写示例时结合element-ui编写)
7 |
8 |
9 |
10 |
11 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/views/demo/standard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 当项设置为null、undefined、false时,说明此项是不显示的
5 |
6 |
7 |
8 |
9 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/src/views/demo/tabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
182 |
183 |
184 |
--------------------------------------------------------------------------------
/src/views/demo/title.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/src/views/demo/unit.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
93 |
94 |
102 |
--------------------------------------------------------------------------------
/src/views/home/components/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/src/views/home/components/README.md
--------------------------------------------------------------------------------
/src/views/home/components/native.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
40 |
--------------------------------------------------------------------------------
/src/views/notfound/components/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chengaohe45/vue-easy-form/8cee81eea170316a51ae0d77e3dbb84d6f3076f1/src/views/notfound/components/README.md
--------------------------------------------------------------------------------
/src/views/notfound/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 404,页面不存在
4 |
5 |
6 |
7 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | mocha: true
4 | },
5 | rules: {
6 | "import/no-extraneous-dependencies": "off"
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/tests/unit/libs/utils.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import Vue from "vue";
3 |
4 | import utils from "@/package/libs/utils.js";
5 |
6 | // 初始化工具组件,传这个Vue:用注册时的Vue,这样虚拟节点才能判断正确
7 | utils.initVue(Vue);
8 |
9 | describe("utils.js", () => {
10 | it("base type check", () => {
11 | expect(utils.isBool(true)).to.equal(true);
12 | expect(utils.isBool(null)).to.equal(false);
13 |
14 | expect(utils.isNum(1.1)).to.equal(true);
15 | expect(utils.isNum("1.1")).to.equal(false);
16 |
17 | expect(utils.isStr("true")).to.equal(true);
18 | expect(utils.isStr(true)).to.equal(false);
19 |
20 | expect(utils.isFunc(() => {})).to.equal(true);
21 | expect(utils.isArr(true)).to.equal(false);
22 |
23 | expect(utils.isDate(new Date())).to.equal(true);
24 | expect(utils.isDate(123)).to.equal(false);
25 |
26 | expect(utils.isObj({})).to.equal(true);
27 | expect(utils.isObj("{}")).to.equal(false);
28 | });
29 |
30 | it("deepCopy", () => {
31 | var obj = {
32 | a: 1,
33 | b: {
34 | b1: 21,
35 | b2: 23
36 | }
37 | };
38 | obj.c = obj.b; // 同一个地址
39 |
40 | obj.d = { d1: obj.b, d2: 42 }; // 进入循环
41 | obj.b.b1 = obj.d;
42 | expect(utils.deepCopy(obj)).to.have.property("c");
43 | var tmpObj = utils.deepCopy(obj);
44 | expect(tmpObj.b).to.equal(tmpObj.c);
45 | expect(tmpObj.b).to.equal(tmpObj.d.d1);
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | // const apiMocker = require("webpack-api-mocker");
2 | const path = require("path");
3 | // const mocker = path.resolve(__dirname, "./mock/data.js");
4 |
5 | // vue.config.js 配置说明 (这个file是peter我加的)
6 | // 这里只列一部分,具体配置惨考文档啊
7 |
8 | process.env.VUE_APP_VERSION = require("./package.json").version;
9 |
10 | const prodEnv = "production";
11 |
12 | let isRelease = false;
13 | if (process.env.NODE_ENV === prodEnv) {
14 | let argvs = process.argv;
15 | if (argvs && argvs.length > 0) {
16 | let paramIndex = argvs.indexOf("--target");
17 | let paramValIndex = argvs.indexOf("lib");
18 | console.log(paramIndex, paramValIndex);
19 | if (paramIndex + 1 == paramValIndex) {
20 | isRelease = true;
21 | }
22 | }
23 | }
24 |
25 | module.exports = {
26 | // baseUrl type:{string} default:'/'
27 | // 将部署应用程序的基本URL
28 | // 将部署应用程序的基本URL
29 | // 默认情况下,Vue CLI假设您的应用程序将部署在域的根目录下。
30 | // https://www.my-app.com/。如果应用程序部署在子路径上,则需要使用此选项指定子路径。例如,如果您的应用程序部署在https://www.foobar.com/my-app/,集baseUrl到'/my-app/'.
31 |
32 | // baseUrl: process.env.NODE_ENV === 'production' ? '/online/' : '/test/',
33 |
34 | // baseUrl: "",
35 | publicPath: "",
36 |
37 | // outputDir: 在npm run build时 生成文件的目录 type:string, default:'dist'
38 |
39 | // outputDir: 'dist',
40 |
41 | outputDir: isRelease ? "dist" : "../vue-easy-form-docs/demo",
42 |
43 | // pages:{ type:Object,Default:undfind }
44 | /*
45 | 构建多页面模式的应用程序.每个“页面”都应该有一个相应的JavaScript条目文件。该值应该是一
46 | 个对象,其中键是条目的名称,而该值要么是指定其条目、模板和文件名的对象,要么是指定其条目
47 | 的字符串,
48 | 注意:请保证pages里配置的路径和文件名 在你的文档目录都存在 否则启动服务会报错的
49 | */
50 |
51 | configureWebpack: config => {
52 | if (isRelease) {
53 | if (!config.output) {
54 | config.output = {};
55 | }
56 | config.output.library = "esForm";
57 | config.output.libraryExport = "default";
58 | config.externals = {
59 | ...config.externals,
60 | vue: {
61 | root: "Vue",
62 | commonjs2: "vue",
63 | commonjs: "vue",
64 | amd: "vue"
65 | },
66 | "element-ui": {
67 | root: "ELEMENT",
68 | commonjs2: "ELEMENT",
69 | commonjs: "ELEMENT",
70 | amd: "ELEMENT"
71 | },
72 | axios: {
73 | root: "axios",
74 | commonjs2: "axios",
75 | commonjs: "axios",
76 | amd: "axios"
77 | }
78 | };
79 | }
80 |
81 | var otherAlias = {};
82 | otherAlias["vue-easy-form"] = path.resolve(
83 | __dirname,
84 | "./src/package/index.js"
85 | );
86 | if (!config.resolve.alias) {
87 | config.resolve.alias = {};
88 | }
89 | Object.assign(config.resolve.alias, otherAlias);
90 | },
91 | css: {
92 | extract: false // 是否内联css
93 | },
94 | // lintOnSave:{ type:Boolean default:true } 问你是否使用eslint
95 | lintOnSave: process.env.NODE_ENV !== prodEnv ? true : false, // eslint-loader 是否在保存的时候检查
96 | // productionSourceMap:{ type:Bollean,default:true } 生产源映射
97 | // 如果您不需要生产时的源映射,那么将此设置为false可以加速生产构建
98 | productionSourceMap: false,
99 | // devServer:{type:Object} 3个属性host,port,https
100 | // 它支持webPack-dev-server的所有选项
101 |
102 | devServer: {
103 | port: 8086, // 端口号
104 | // // host: 'localhost', //不要写这个东西,要写就写127.0.0.1
105 | // // https: false, // https:{type:Boolean}
106 | open: true //配置自动启动浏览器
107 | // before: (app) => {
108 | // apiMocker(app, mocker, {
109 | // proxy: {
110 | // // '/api/*': 'http://domain.com.com' //当没有mock数据,只符合此规则,则转换
111 | // },
112 | // changeHost: true
113 | // });
114 | // },
115 | // proxy: 'http://localhost:4000' // 配置跨域处理,只有一个代理
116 | // proxy: {
117 | // '/api': {
118 | // target: '/mock/data.js',
119 | // ws: true,
120 | // changeOrigin: true
121 | // },
122 | // '/foo': {
123 | // target: ''
124 | // }
125 | // }, // 配置多个代理
126 | }
127 | };
128 |
--------------------------------------------------------------------------------