├── static
├── .gitkeep
├── label.1.json
├── label.17.json
├── label.3.json
├── label.12.json
├── label.19.json
├── label.5.json
├── label.14.json
├── label.8.json
└── label.json
├── config
├── prod.env.js
├── dev.env.js
├── proxyConfig.js
└── index.js
├── .gitattributes
├── .editorconfig
├── .gitignore
├── src
├── components
│ ├── custom_form
│ │ ├── config
│ │ │ └── trigger.js
│ │ ├── control
│ │ │ ├── Hr.js
│ │ │ ├── P.js
│ │ │ ├── Title.js
│ │ │ ├── Input.js
│ │ │ ├── Uploads.js
│ │ │ ├── CheckBox.js
│ │ │ ├── Radio.js
│ │ │ ├── Text.js
│ │ │ ├── DatePicker.js
│ │ │ ├── Select.js
│ │ │ ├── Cascader.js
│ │ │ └── Address.js
│ │ ├── index.js
│ │ ├── ItemIcon.js
│ │ ├── FormList.js
│ │ ├── Preview.js
│ │ ├── Render.js
│ │ └── components
│ │ │ └── Uploads
│ │ │ └── upload.vue
│ ├── preview.vue
│ ├── render.vue
│ └── index.vue
├── router
│ └── index.js
├── App.vue
└── main.js
├── .babelrc
├── .postcssrc.js
├── index.html
├── package.json
└── README.md
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"'
4 | }
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=Vue
2 | *.css linguist-language=Vue
3 | *.html linguist-language=Vue
--------------------------------------------------------------------------------
/static/label.1.json:
--------------------------------------------------------------------------------
1 | {"items":[{"label_value":null,"label_name":""}],"status":"success","code":200,"name":"name"}
--------------------------------------------------------------------------------
/static/label.17.json:
--------------------------------------------------------------------------------
1 | {"items":[{"label_value":null,"label_name":null}],"status":"success","code":200,"name":"case"}
--------------------------------------------------------------------------------
/static/label.3.json:
--------------------------------------------------------------------------------
1 | { "items": [{ "label_value": null, "label_name": "" }], "status": "success", "code": 200, "name": "phone" }
2 |
--------------------------------------------------------------------------------
/static/label.12.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [{
3 | "label_value": null,
4 | "label_name": null
5 | }],
6 | "status": "success",
7 | "code": 200,
8 | "name": "photo"
9 | }
10 |
--------------------------------------------------------------------------------
/static/label.19.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [{
3 | "label_value": null,
4 | "label_name": null
5 | }],
6 | "status": "success",
7 | "code": 200,
8 | "name": "address"
9 | }
10 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"'
7 | })
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | /dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | .vscode
11 | *.suo
12 | *.ntvs*
13 | *.njsproj
14 | *.sln
15 | .history
16 |
--------------------------------------------------------------------------------
/src/components/custom_form/config/trigger.js:
--------------------------------------------------------------------------------
1 | export default {
2 | input: 'blur,change',
3 | select: 'change',
4 | radio: 'change',
5 | checkbox: 'change',
6 | cascader: 'input',
7 | uploads: 'handleUploadsValue',
8 | textarea: 'blur,change',
9 | }
10 |
--------------------------------------------------------------------------------
/static/label.5.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [{
3 | "label_value": "1",
4 | "label_name": "男"
5 | },
6 | {
7 | "label_value": "2",
8 | "label_name": "女"
9 | }
10 | ],
11 | "status": "success",
12 | "code": 200,
13 | "name": "sex"
14 | }
--------------------------------------------------------------------------------
/static/label.14.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [{
3 | "label_value": "1",
4 | "label_name": "是"
5 | },
6 | {
7 | "label_value": "2",
8 | "label_name": "否"
9 | }
10 | ],
11 | "status": "success",
12 | "code": 200,
13 | "name": "is_case"
14 | }
15 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins": ["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/config/proxyConfig.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | proxy: {
3 | '/apis': { //将www.exaple.com印射为/apis
4 | target: 'http://workflow.test', // 接口域名
5 | changeOrigin: true, //是否跨域
6 | pathRewrite: {
7 | '^/apis': '' //需要rewrite的,
8 | }
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-formbuilder
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/static/label.8.json:
--------------------------------------------------------------------------------
1 | {
2 | "items": [{
3 | "label_value": "1",
4 | "label_name": "电话投诉"
5 | },
6 | {
7 | "label_value": "2",
8 | "label_name": "书信投诉"
9 | },
10 | {
11 | "label_value": "3",
12 | "label_name": "网络投诉"
13 | }
14 | ],
15 | "status": "success",
16 | "code": 200,
17 | "name": "complaint"
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Hr.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h('hr', {
4 | style: {
5 | 'margin-bottom': _self.obj.marginBottom + 'px',
6 | 'margin-top': _self.obj.marginTop + 'px',
7 | }
8 | })
9 | ]
10 | }
11 |
12 | export const hrConf = {
13 | // 是否可配置
14 | config: true,
15 | marginTop: 0,
16 | marginBottom: 24
17 | }
18 |
--------------------------------------------------------------------------------
/src/components/preview.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
19 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/P.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h('p', {
4 | style: {
5 | 'margin-bottom': _self.obj.marginBottom + 'px',
6 | 'margin-top': _self.obj.marginTop + 'px',
7 | 'color': _self.obj.color || "#000"
8 | },
9 | domProps: {
10 | innerHTML: _self.obj.label || "文本标签"
11 | }
12 | })
13 | ]
14 | }
15 |
16 | export const pConf = {
17 | config: true,
18 | label: '文本标签',
19 | color: '#000',
20 | marginTop: 0,
21 | marginBottom: 24
22 | }
23 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import index from '@/components/index'
4 | import render from '@/components/render'
5 | import preview from '@/components/preview'
6 |
7 | Vue.use(Router)
8 |
9 | export default new Router({
10 | routes: [{
11 | path: '/',
12 | name: 'index',
13 | component: index
14 | }, {
15 | path: '/render',
16 | name: 'render',
17 | component: render
18 | }, {
19 | path: '/preview',
20 | name: 'preview',
21 | component: preview
22 | }]
23 | })
24 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Title.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h('h' + (_self.obj.level || 3), {
4 | style: {
5 | 'margin-bottom': _self.obj.marginBottom + 'px',
6 | 'margin-top': _self.obj.marginTop + 'px',
7 | },
8 | domProps: {
9 | innerHTML: _self.obj.label || "Title"
10 | }
11 | })
12 | ]
13 | }
14 |
15 | export let titleConf = {
16 | // 是否可配置
17 | config: true,
18 | // 控件文本显示内容
19 | label: '标题',
20 | // h标签等级(1-6)
21 | level: 3,
22 | hasLine: true,
23 | marginTop: 0,
24 | marginBottom: 24
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/custom_form/index.js:
--------------------------------------------------------------------------------
1 | import render from "./Render";
2 | import uploadCustom from './components/Uploads/upload';
3 | import preview from './Preview';
4 |
5 | const customForm = {
6 | render,
7 | uploadCustom,
8 | preview
9 | };
10 |
11 | const install = function(Vue, opts = {}) {
12 | Vue.component(render.name, render);
13 | Vue.component(uploadCustom.name, uploadCustom);
14 | Vue.component(preview.name, preview);
15 | };
16 |
17 | if (typeof window !== 'undefined' && window.Vue) {
18 | install(window.Vue);
19 | }
20 |
21 | export default Object.assign(customForm, { install });
22 |
--------------------------------------------------------------------------------
/static/label.json:
--------------------------------------------------------------------------------
1 | { "items": [{ "id": 1, "label": "姓名", "type": "input", "parent_name": null }, { "id": 3, "label": "电话", "type": "input", "parent_name": null }, { "id": 5, "label": "性别", "type": "radio", "parent_name": null }, { "id": 8, "label": "投诉方式", "type": "select", "parent_name": null }, { "id": 12, "label": "上传图片附件", "type": "uploads", "parent_name": null }, { "id": 14, "label": "案件资料是否收齐", "type": "select", "parent_name": null }, { "id": 17, "label": "标识缺少的资料", "type": "input", "parent_name": "is_case" }, { "id": 19, "label": "地址", "type": "address", "parent_name": null }], "status": "success", "code": 200 }
2 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
36 |
--------------------------------------------------------------------------------
/src/components/custom_form/ItemIcon.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | let icons = [];
3 | // 配置按钮
4 | if (!!_self.obj.config) {
5 | icons.push(h('Icon', {
6 | props: {
7 | type: 'gear-a',
8 | },
9 | nativeOn: {
10 | click() {
11 | _self.$emit('handleConfEle', _self.index);
12 | }
13 | }
14 | }));
15 | }
16 | // 删除按钮
17 | icons.push(h('Icon', {
18 | props: {
19 | type: 'minus-round'
20 | },
21 | nativeOn: {
22 | click() {
23 | _self.$emit('handleRemoveEle', _self.index);
24 | }
25 | }
26 | }));
27 | const item_icon = h('div', {
28 | class: {
29 | 'item-icon': true
30 | }
31 | }, icons);
32 | return item_icon;
33 | }
34 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // The Vue build version to load with the `import` command
2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
3 | import Vue from 'vue'
4 | import App from './App'
5 | import router from './router'
6 | import iview from 'iview';
7 | import 'iview/dist/styles/iview.css'
8 | import cascaderMulti from 'cascader-multi';
9 | import iviewArea from 'iview-area';
10 | import custom_form from './components/custom_form';
11 |
12 | import axios from 'axios';
13 |
14 | Vue.config.productionTip = false
15 | Vue.use(iview);
16 | Vue.use(cascaderMulti);
17 | Vue.use(custom_form);
18 | Vue.use(iviewArea);
19 |
20 | // axios.defaults.baseURL = '/apis';
21 | Vue.prototype.$http = axios;
22 |
23 | /* eslint-disable no-new */
24 | new Vue({
25 | el: '#app',
26 | router,
27 | components: { App },
28 | template: ''
29 | })
30 |
--------------------------------------------------------------------------------
/src/components/custom_form/FormList.js:
--------------------------------------------------------------------------------
1 | import { inputConf } from "./control/Input";
2 | import { selectConf } from "./control/Select";
3 | import { radioConf } from "./control/Radio";
4 | import { checkBoxConf } from "./control/CheckBox";
5 | import { cascaderConf } from "./control/Cascader";
6 | import { textConf } from "./control/Text";
7 | import { titleConf } from "./control/Title";
8 | import { hrConf } from "./control/Hr";
9 | import { pConf } from "./control/P";
10 | import { uploadsConf } from './control/Uploads';
11 | import { datePickerConf } from './control/DatePicker'
12 | import { addressConf } from './control/Address';
13 |
14 | const formList = {
15 | title: titleConf,
16 | hr: hrConf,
17 | p: pConf,
18 | input: inputConf,
19 | select: selectConf,
20 | radio: radioConf,
21 | checkbox: checkBoxConf,
22 | datepicker: datePickerConf,
23 | cascader: cascaderConf,
24 | address: addressConf,
25 | uploads: uploadsConf,
26 | text: textConf,
27 | };
28 | let list_arr = [];
29 | for (let i in formList) {
30 | list_arr.push({
31 | ele: i,
32 | obj: formList[i]
33 | });
34 | }
35 | export default list_arr;
36 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Input.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h("Input", {
4 | props: {
5 | placeholder: _self.obj.placeholder || "这是一个输入框",
6 | maxlength: parseInt(_self.obj.maxLength) || 20,
7 | value: _self.obj.value || ""
8 | },
9 | on: {
10 | "on-change": function(val) {
11 | if (!_self.obj.name) {
12 | return false;
13 | }
14 | _self.obj.value = event.currentTarget.value;
15 | _self.$emit('handleChangeVal', val.currentTarget.value)
16 | }
17 | }
18 | })
19 | ];
20 | };
21 |
22 |
23 | export let inputConf = {
24 | // 对应数据库内类型
25 | type: 'input',
26 | // 是否可配置
27 | config: true,
28 | // 控件左侧label内容
29 | label: '输入框',
30 | placeholder: '',
31 | // 是否显示行内元素
32 | inlineBlock: false,
33 | // 是否必填
34 | require: true,
35 | // 最大长度
36 | maxLength: 20,
37 | // 选项内数据
38 | items: [{ "label_value": null, "label_name": "" }],
39 | value: '',
40 | // 表单name
41 | name: '',
42 | // 验证错误提示信息
43 | ruleError: '该字段不能为空',
44 | // 是否关联字段
45 | relation: false,
46 | // 关联字段name
47 | relation_name: '',
48 | // 关联字段value
49 | relation_value: '',
50 | // 是否被渲染
51 | visibility: true
52 | }
53 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Uploads.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h('uploadCustom', {
4 | props: {
5 | multiple: _self.obj.multiple || false,
6 | type: 'drag', //支持拖拽
7 | action: _self.obj.action || "/imageupload",
8 | 'max-size': _self.obj.maxSize || 2048,
9 | defaultList: _self.obj.value,
10 | name: 'photo'
11 | },
12 | on: {
13 | handleUploadsValue(arr) {
14 | if (!_self.obj.name) {
15 | return false;
16 | }
17 | _self.obj.value = arr;
18 | _self.$emit('handleChangeVal', arr)
19 | }
20 | }
21 | })
22 | ]
23 | }
24 |
25 |
26 | export const uploadsConf = {
27 | // 对应数据库内类型
28 | type: 'uploads',
29 | // 是否可配置
30 | config: true,
31 | // 上传地址
32 | action: 'http://workflow.test/imageupload',
33 | // 是否必填
34 | require: true,
35 | // 控件左侧label内容
36 | label: '上传控件',
37 | // 上传文件最大限制
38 | maxSize: 2048,
39 | // 绑定的值
40 | value: [],
41 | // 表单name
42 | name: '',
43 | // 验证错误提示信息
44 | ruleError: '请上传图片',
45 | // 是否关联字段
46 | relation: false,
47 | // 关联字段name
48 | relation_name: '',
49 | // 关联字段value
50 | relation_value: '',
51 | // 是否被渲染
52 | visibility: true
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/CheckBox.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h("CheckboxGroup", {
4 | props: {
5 | value: _self.obj.value || []
6 | },
7 | on: {
8 | 'on-change' (arr) {
9 | if (!_self.obj.name) {
10 | return false;
11 | }
12 | _self.obj.value = arr;
13 | _self.$emit('handleChangeVal', arr)
14 | }
15 | }
16 | }, _self.obj.items.map(v => {
17 | return h("Checkbox", {
18 | props: {
19 | label: v.label_value
20 | }
21 | }, v.label_name)
22 | }))
23 | ];
24 | };
25 |
26 | export let checkBoxConf = {
27 | // 对应数据库内类型
28 | type: 'checkbox',
29 | // 是否可配置
30 | config: true,
31 | // 控件左侧label内容
32 | label: '多选框',
33 | // 是否显示行内元素
34 | inlineBlock: false,
35 | // 是否必填
36 | require: true,
37 | // 绑定的值
38 | value: [],
39 | // 选项内数据
40 | items: [{ "label_value": "1", "label_name": "单选框1" }, { "label_value": "2", "label_name": "单选框2" }],
41 | // 表单name
42 | name: '',
43 | // 验证错误提示信息
44 | ruleError: '该选项不能为空',
45 | // 是否关联字段
46 | relation: false,
47 | // 关联字段name
48 | relation_name: '',
49 | // 关联字段value
50 | relation_value: '',
51 | // 是否被渲染
52 | visibility: true
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Radio.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h("RadioGroup", {
4 | props: {
5 | value: _self.obj.value || "-1"
6 | },
7 | on: {
8 | 'on-change' (value) {
9 | if (!_self.obj.name) {
10 | return false;
11 | }
12 | _self.obj = Object.assign(_self.obj, {
13 | value
14 | });
15 | _self.$emit('handleChangeVal', value)
16 | }
17 | }
18 | }, _self.obj.items.map(v => {
19 | return h("Radio", {
20 | props: {
21 | label: v.label_value
22 | }
23 | }, v.label_name)
24 | }))
25 | ];
26 | };
27 |
28 | export let radioConf = {
29 | // 对应数据库内类型
30 | type: 'radio',
31 | // 是否可配置
32 | config: true,
33 | // 控件左侧label内容
34 | label: '单选框',
35 | // 是否显示行内元素
36 | inlineBlock: false,
37 | // 是否必填
38 | require: true,
39 | // 绑定的值
40 | value: '',
41 | // 选项内数据
42 | items: [{ "label_value": "1", "label_name": "单选框1" }, { "label_value": "2", "label_name": "单选框2" }],
43 | // 表单name
44 | name: '',
45 | // 验证错误提示信息
46 | ruleError: '请选择',
47 | // 是否关联字段
48 | relation: false,
49 | // 关联字段name
50 | relation_name: '',
51 | // 关联字段value
52 | relation_value: '',
53 | // 是否被渲染
54 | visibility: true
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Text.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h("Input", {
4 | attrs: {
5 | type: "textarea",
6 | placeholder: _self.obj.placeholder || "这是一个文本域",
7 | value: _self.obj.value,
8 | maxlength: _self.obj.maxLength || 200
9 | },
10 | props: {
11 | rows: _self.obj.maxRows || 5
12 | },
13 | on: {
14 | "on-change" (event) {
15 | if (!_self.obj.name) {
16 | return false;
17 | }
18 | _self.obj.value = event.currentTarget.value;
19 | _self.$emit('handleChangeVal', event.currentTarget.value)
20 | }
21 | }
22 | })
23 | ];
24 | };
25 |
26 | export let textConf = {
27 | // 对应数据库内类型
28 | type: 'textarea',
29 | // 是否可配置
30 | config: true,
31 | // 控件左侧label内容
32 | label: '文本域',
33 | placeholder: '',
34 | // 是否显示行内元素
35 | inlineBlock: false,
36 | // 最大长度
37 | maxLength: 200,
38 | // 是否必填
39 | require: true,
40 | // 文本域行高
41 | maxRows: 5,
42 | // 绑定的值
43 | value: "",
44 | // 表单name
45 | name: '',
46 | // 验证错误提示信息
47 | ruleError: '该字段不能为空',
48 | // 是否关联字段
49 | relation: false,
50 | // 关联字段name
51 | relation_name: '',
52 | // 关联字段value
53 | relation_value: '',
54 | // 是否被渲染
55 | visibility: true
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/DatePicker.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | export default (_self, h) => {
3 | return [
4 | h('DatePicker', {
5 | props: {
6 | placeholder: _self.obj.placeholder || (_self.obj.name ? "" : "请选择日期"),
7 | type: (!_self.obj.format || _self.obj.format == 'yyyy年MM月dd日') ? 'date' : 'datetime',
8 | format: _self.obj.format || 'yyyy年MM月dd日',
9 | value: _self.obj.value
10 | },
11 | on: {
12 | "on-change" (arr) {
13 | if (!_self.obj.name) {
14 | return false;
15 | }
16 | _self.obj.value = arr;
17 | _self.$emit('handleChangeVal', arr)
18 | }
19 | }
20 | })
21 | ]
22 | }
23 |
24 |
25 | export let datePickerConf = {
26 | // 对应数据库内类型
27 | type: 'datepicker',
28 | // 是否可配置
29 | config: true,
30 | // 控件左侧label内容
31 | label: '时间选择',
32 | placeholder: '请选择日期',
33 | // 是否显示行内元素
34 | inlineBlock: false,
35 | // 是否必填
36 | require: true,
37 | // 表单name
38 | name: '',
39 | // 绑定的值
40 | value: "",
41 | // 验证错误提示信息
42 | ruleError: '选项不能为空',
43 | // 是否关联字段
44 | relation: false,
45 | // 关联字段name
46 | relation_name: '',
47 | // 关联字段value
48 | relation_value: '',
49 | // 是否被渲染
50 | visibility: true,
51 | // 是否需要时分
52 | format: 'yyyy年MM月dd日'
53 | }
54 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Select.js:
--------------------------------------------------------------------------------
1 | export default (_self, h) => {
2 | return [
3 | h(
4 | "Select", {
5 | props: {
6 | placeholder: _self.obj.placeholder || "这是一个下拉选项框",
7 | value: _self.obj.value || ''
8 | },
9 | on: {
10 | 'on-change' (value) {
11 | if (!_self.obj.name) {
12 | return false;
13 | }
14 | _self.obj.value = value;
15 | _self.$emit('handleChangeVal', value)
16 | }
17 | }
18 | },
19 | _self.obj.items.map(v => {
20 | return h(
21 | "Option", {
22 | props: {
23 | value: "" + v.label_value
24 | },
25 | },
26 | v.label_name
27 | );
28 | })
29 | )
30 | ];
31 | };
32 |
33 | export let selectConf = {
34 | // 对应数据库内类型
35 | type: 'select',
36 | // 是否可配置
37 | config: true,
38 | // 控件左侧label内容
39 | label: '选择框',
40 | placeholder: '',
41 | // 是否显示行内元素
42 | inlineBlock: false,
43 | // 是否必填
44 | require: true,
45 | // 选项内数据
46 | items: Array.apply(null, { length: 5 })
47 | .map((k, v) => {
48 | return {
49 | label_value: v + 1,
50 | label_name: "选项" + (v + 1),
51 | }
52 | }),
53 | // 绑定的值
54 | value: '',
55 | // 表单name
56 | name: '',
57 | // 验证错误提示信息
58 | ruleError: '请选择',
59 | // 是否关联字段
60 | relation: false,
61 | // 关联字段name
62 | relation_name: '',
63 | // 关联字段value
64 | relation_value: '',
65 | // 是否被渲染
66 | visibility: true
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Cascader.js:
--------------------------------------------------------------------------------
1 | const temp_data = (size, parentIndex, parentName) => {
2 | if (parentIndex >= 3) {
3 | return [];
4 | }
5 | return Array.apply(null, { length: size })
6 | .map((k, v) => {
7 | const name = parentName + (v + 1) + "-";
8 | return {
9 | value: name.substring(0, name.length - 1),
10 | label: name.substring(0, name.length - 1),
11 | children: temp_data(size, parentIndex + 1, name)
12 | }
13 | });
14 | }
15 |
16 | export default (_self, h) => {
17 | return [
18 | h("cascaderMulti", {
19 | props: {
20 | placeholder: _self.obj.placeholder || "这是一个级联选择器",
21 | data: temp_data(3, 0, '数据'),
22 | multiple: _self.obj.multiple || false,
23 | separate: _self.obj.multiple ? "," : "/",
24 | filterable: true,
25 | value: _self.obj.value || []
26 | },
27 | on: {
28 | "on-change" (arr) {
29 | if (!_self.obj.name) {
30 | return false;
31 | }
32 | _self.obj.value = arr;
33 | _self.$emit('handleChangeVal', arr)
34 | }
35 | }
36 | })
37 | ];
38 | };
39 |
40 | export let cascaderConf = {
41 | // 对应数据库内类型
42 | type: 'cascader',
43 | // 是否可配置
44 | config: true,
45 | // 控件左侧label内容
46 | label: '级联选择',
47 | placeholder: '',
48 | // 是否显示行内元素
49 | inlineBlock: false,
50 | // 是否必填
51 | require: true,
52 | // 是否多选
53 | multiple: false,
54 | // 表单name
55 | name: '',
56 | // 绑定的值
57 | value: [],
58 | // 验证错误提示信息
59 | ruleError: '该选项不能为空',
60 | // 是否关联字段
61 | relation: false,
62 | // 关联字段name
63 | relation_name: '',
64 | // 关联字段value
65 | relation_value: '',
66 | // 是否被渲染
67 | visibility: true
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/render.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
51 |
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-formbuilder",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "mrabit ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js"
11 | },
12 | "dependencies": {
13 | "axios": "^0.18.0",
14 | "cascader-multi": "^2.0.1",
15 | "iview": "^2.12.0",
16 | "iview-area": "^1.5.17",
17 | "moment": "^2.22.0",
18 | "vue": "^2.5.2",
19 | "vue-router": "^3.0.1",
20 | "vuedraggable": "^2.16.0"
21 | },
22 | "devDependencies": {
23 | "ajv": "^6.4.0",
24 | "autoprefixer": "^7.1.2",
25 | "babel-core": "^6.22.1",
26 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
27 | "babel-loader": "^7.1.1",
28 | "babel-plugin-syntax-jsx": "^6.18.0",
29 | "babel-plugin-transform-runtime": "^6.22.0",
30 | "babel-plugin-transform-vue-jsx": "^3.5.0",
31 | "babel-preset-env": "^1.3.2",
32 | "babel-preset-stage-2": "^6.22.0",
33 | "chalk": "^2.0.1",
34 | "copy-webpack-plugin": "^4.0.1",
35 | "css-loader": "^0.28.0",
36 | "extract-text-webpack-plugin": "^3.0.0",
37 | "file-loader": "^1.1.4",
38 | "friendly-errors-webpack-plugin": "^1.6.1",
39 | "html-webpack-plugin": "^2.30.1",
40 | "iview-loader": "^1.1.0",
41 | "node-notifier": "^5.1.2",
42 | "optimize-css-assets-webpack-plugin": "^3.2.0",
43 | "ora": "^1.2.0",
44 | "portfinder": "^1.0.13",
45 | "postcss-import": "^11.0.0",
46 | "postcss-loader": "^2.0.8",
47 | "postcss-url": "^7.2.1",
48 | "rimraf": "^2.6.0",
49 | "semver": "^5.3.0",
50 | "shelljs": "^0.7.6",
51 | "uglifyjs-webpack-plugin": "^1.1.1",
52 | "url-loader": "^0.5.8",
53 | "vue-loader": "^13.3.0",
54 | "vue-style-loader": "^3.0.1",
55 | "vue-template-compiler": "^2.5.2",
56 | "webpack": "^3.6.0",
57 | "webpack-bundle-analyzer": "^2.9.0",
58 | "webpack-dev-server": "^2.9.1",
59 | "webpack-merge": "^4.1.0"
60 | },
61 | "engines": {
62 | "node": ">= 6.0.0",
63 | "npm": ">= 3.0.0"
64 | },
65 | "browserslist": [
66 | "> 1%",
67 | "last 2 versions",
68 | "not ie <= 8"
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 | var proxyConfig = require('./proxyConfig')
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | // proxyTable: {},
14 | proxyTable: proxyConfig.proxy,
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 |
24 | /**
25 | * Source Maps
26 | */
27 |
28 | // https://webpack.js.org/configuration/devtool/#development
29 | devtool: 'cheap-module-eval-source-map',
30 |
31 | // If you have problems debugging vue-files in devtools,
32 | // set this to false - it *may* help
33 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
34 | cacheBusting: true,
35 |
36 | cssSourceMap: true
37 | },
38 |
39 | build: {
40 | // Template for index.html
41 | index: path.resolve(__dirname, '../dist/index.html'),
42 |
43 | // Paths
44 | assetsRoot: path.resolve(__dirname, '../dist'),
45 | assetsSubDirectory: 'static',
46 | assetsPublicPath: '/',
47 |
48 | /**
49 | * Source Maps
50 | */
51 |
52 | productionSourceMap: true,
53 | // https://webpack.js.org/configuration/devtool/#production
54 | devtool: '#source-map',
55 |
56 | // Gzip off by default as many popular static hosts such as
57 | // Surge or Netlify already gzip all static assets for you.
58 | // Before setting to `true`, make sure to:
59 | // npm install --save-dev compression-webpack-plugin
60 | productionGzip: false,
61 | productionGzipExtensions: ['js', 'css'],
62 |
63 | // Run the build command with an extra argument to
64 | // View the bundle analyzer report after build finishes:
65 | // `npm run build --report`
66 | // Set to `true` or `false` to always turn it on or off
67 | bundleAnalyzerReport: process.env.npm_config_report
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/components/custom_form/control/Address.js:
--------------------------------------------------------------------------------
1 | import area from '../config/area'
2 | export default (_self, h) => {
3 | let control = [
4 | h('Cascader', {
5 | class: {
6 | 'ivu-input-wrapper ': !_self.obj.details_address
7 | },
8 | style: {
9 | width: _self.obj.details_address ? '200px' : '100%',
10 | display: 'inline-block'
11 | },
12 | props: {
13 | placeholder: _self.obj.placeholder || (_self.obj.name ? "" : "请选择详细地址"),
14 | data: area,
15 | value: _self.obj.value || [],
16 | filterable: false,
17 | 'change-on-select': true,
18 | // trigger: "hover"
19 | },
20 | on: {
21 | "on-change" (arr) {
22 | if (!_self.obj.name) {
23 | return false;
24 | }
25 | _self.obj.value = arr;
26 | _self.$emit('handleChangeVal', arr);
27 | }
28 | }
29 | }),
30 | h("Input", {
31 | props: {
32 | placeholder: _self.obj.placeholder || "请输入详细地址",
33 | ref: 'details_address',
34 | value: (_self.obj.value[3] || {})
35 | .name
36 | },
37 | style: {
38 | width: 'auto',
39 | display: !_self.obj.name ? 'none' : 'inline-block',
40 | 'margin-left': '5px',
41 | 'min-width': '300px'
42 | },
43 | on: {
44 | "on-change": function(val) {
45 | if (!_self.obj.name) {
46 | return false;
47 | }
48 | let temp_data = _self.obj.value.slice(0, 3);
49 | _self.obj.value = temp_data.concat(val.currentTarget.value);
50 | _self.$emit('handleChangeVal', _self.obj.value)
51 | }
52 | }
53 | })
54 | ];
55 |
56 | if (!_self.obj.details_address) {
57 | control.splice(1, 1);
58 | }
59 | return control;
60 | }
61 | export let addressConf = {
62 | // 对应数据库内类型
63 | type: 'address',
64 | // 是否可配置
65 | config: true,
66 | // 控件左侧label内容
67 | label: '详细地址',
68 | placeholder: '请输入详细地址',
69 | // 是否显示行内元素
70 | inlineBlock: false,
71 | // 是否必填
72 | require: true,
73 | // 是否多选
74 | multiple: false,
75 | // 表单name
76 | name: '',
77 | // 绑定的值
78 | value: [],
79 | // 验证错误提示信息
80 | ruleError: '请选择并输入详细地址',
81 | // 是否关联字段
82 | relation: false,
83 | // 关联字段name
84 | relation_name: '',
85 | // 关联字段value
86 | relation_value: '',
87 | // 是否被渲染
88 | visibility: true,
89 | // 是否需要详细地址
90 | details_address: true
91 | }
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 基于Vue + Vue.Draggable实现自定义表单控件
2 |
3 | > 新项目需要用到工作流设定 + 自定义表单控件,这里列出了自定义表单控件的代码实现,可实现自定义表单控件,可拖拽排序,自定义属性
4 |
5 | > 项目 UI 组件库为 [iView](https://www.iviewui.com/docs/guide/install), [Element UI](http://element-cn.eleme.io/#/zh-CN) 可根据项目内代码进行适当修改达到适用
6 |
7 | > Element版本Demo: [https://yunpan.360.cn/surl_ymAY4CPrMNx](https://yunpan.360.cn/surl_ymAY4CPrMNx) (提取码:0e00)
8 |
9 | ## 效果预览
10 |
11 | 
12 |
13 | 
14 |
15 | 
16 |
17 | ## 运行使用
18 |
19 | ``` bash
20 | # install dependencies
21 | npm install
22 |
23 | # serve with hot reload at localhost:8080
24 | npm run dev
25 |
26 | ```
27 |
28 | ### 文件目录
29 |
30 | ```
31 | .
32 | ├── README.md
33 | ├── build
34 | ├── config
35 | ├── dist
36 | ├── index.html
37 | ├── package.json
38 | ├── src
39 | │ ├── App.vue
40 | │ ├── assets
41 | │ ├── components
42 | │ │ ├── custom_form //自定义表单组件
43 | │ │ │ ├── FormList.js //表单列表
44 | │ │ │ ├── ItemIcon.js //表单图标配置
45 | │ │ │ ├── Render.js //表单列表渲染
46 | │ │ │ ├── components //表单公用组件
47 | │ │ │ │ └── Uploads //上传组件
48 | │ │ │ │ └── upload.vue
49 | │ │ │ ├── config //配置文件
50 | │ │ │ │ ├── area.js //地区配置
51 | │ │ │ │ └── trigger.js //表单验证触发事件
52 | │ │ │ ├── control //表单控件列表
53 | │ │ │ ├── Address.js //地区选择
54 | │ │ │ ├── Cascader.js //多级联动
55 | │ │ │ ├── CheckBox.js //多选框
56 | │ │ │ ├── DatePicker.js //时间选择器
57 | │ │ │ ├── Hr.js //hr标签
58 | │ │ │ ├── Input.js //输入框
59 | │ │ │ ├── P.js //p标签
60 | │ │ │ ├── Radio.js //单选框
61 | │ │ │ ├── Select.js //下拉选择框
62 | │ │ │ ├── Text.js //文本域
63 | │ │ │ ├── Title.js //标题
64 | │ │ │ └── Uploads.js //上传控件
65 | │ │ │ └── index.js //控件注册
66 | │ │ ├── index.vue //自定义表单页面
67 | │ │ └── render.vue //表单渲染,数据回填页面
68 | │ ├── main.js //入口文件
69 | │ └── router //路由配置
70 | │ └── index.js
71 | └── static //静态数据模版
72 | ├── label.1.json
73 | ├── label.12.json
74 | ├── label.14.json
75 | ├── label.17.json
76 | ├── label.19.json
77 | ├── label.3.json
78 | ├── label.5.json
79 | ├── label.8.json
80 | └── label.json
81 | ```
82 |
83 | 相关插件:
84 | - [Vue.Draggable](https://github.com/SortableJS/Vue.Draggable)
85 | - [Vue.js](https://vuejs.org/)
86 | - [iView](https://www.iviewui.com/docs/guide/install)
87 |
--------------------------------------------------------------------------------
/src/components/custom_form/Preview.js:
--------------------------------------------------------------------------------
1 | import title from './control/Title';
2 | import hr from './control/Hr';
3 | import p from './control/P';
4 |
5 | import area from './config/area'
6 |
7 | const form_item = {
8 | title,
9 | hr,
10 | p
11 | };
12 |
13 | // 获取地区控件值
14 | function getAddressValue(address, area, code = 0, str = "") {
15 | // 最后一步
16 | if (code == address.length - 1 && code == 3) return str + address[code];
17 | for (let i in area) {
18 | if (area[i].value == address[code]) {
19 | str = str + getAddressValue(address, area[i].children, code + 1, area[i].label);
20 | break;
21 | }
22 | }
23 | return str;
24 | }
25 |
26 | function getCascaderValue(address, area, code = 0, str = "") {
27 | // 最后一步
28 | for (let i in area) {
29 | if (area[i].value == address[code]) {
30 | str = (str ? str + " > " : "") + getCascaderValue(address, area[i].children, code + 1, area[i].label);
31 | break;
32 | }
33 | }
34 | return str;
35 | }
36 |
37 | function getSelectValue(value, items) {
38 | return items.filter(v => {
39 | return v.label_value == value;
40 | })
41 | }
42 |
43 | function getCheckBoxValue(value, items) {
44 | return items.filter(v => {
45 | return value.indexOf(v.label_value) >= 0;
46 | })
47 | }
48 |
49 | // 获取控件值
50 | const getObjValue = (ele, obj) => {
51 | // 当控件为'input', 'text', 'datepicker',返回本身value
52 | const arr = [
53 | 'input', 'text', 'datepicker'
54 | ]
55 | if (arr.indexOf(ele) >= 0 || !obj.value) {
56 | return obj.value;
57 | }
58 |
59 | if (ele === "address") {
60 | return getAddressValue(obj.value, area);
61 | }
62 |
63 | if (ele === "cascader") {
64 | return getCascaderValue(obj.value, obj.items);
65 | }
66 |
67 | const items = obj.items;
68 | const value = obj.value;
69 | if (ele == "select" || ele == "radio") {
70 | return (getSelectValue(value, items)[0] || {})
71 | .label_name;;
72 | }
73 |
74 | if (ele == "checkbox") {
75 | let str = "";
76 | getCheckBoxValue(value, items)
77 | .forEach(v => {
78 | str += (v.label_name + ",");
79 | })
80 | return str.substring(0, str.length - 1);
81 | }
82 |
83 | return (items[value] || {})
84 | .label_name || "";
85 | }
86 |
87 | export default {
88 | name: 'preview',
89 | render(h) {
90 | // 关联的组件判断是否展示
91 | if (!this.obj.visibility) {
92 | return h('span');
93 | }
94 | // 非 Title Hr P
95 | if (['title', 'hr', 'p'].indexOf(this.ele.toLowerCase()) < 0) {
96 | let control;
97 | if (this.ele.toLowerCase() != "uploads") {
98 | control = h('span', getObjValue(this.ele.toLowerCase(), this.obj));
99 | } else {
100 | control = h('ul', { class: { 'pull-left': true }, style: { 'list-style': 'none' } },
101 | this.obj.value.map(v => {
102 | return h('li', [h('a', {
103 | attrs: {
104 | target: '_blank',
105 | href: v.url
106 | }
107 | }, v.file_name)]);
108 | })
109 | )
110 | }
111 | return h('div', {
112 | class: {
113 | 'clearfix': true
114 | },
115 | style: {
116 | // 是否显示行内元素
117 | display: this.obj.inlineBlock ? 'inline-block' : 'block',
118 | // 行内元素width为30%
119 | width: this.obj.inlineBlock ? '33%' : 'auto',
120 | 'margin-bottom': '24px'
121 | }
122 | }, [
123 | h('label', { class: { 'text-right': true, 'pull-left': this.ele.toLowerCase() == "uploads" } }, this.obj.label + ":"),
124 | control,
125 | ])
126 | } else {
127 | // 获取当前控件渲染
128 | const arr = (form_item[this.ele.toLowerCase()] && form_item[this.ele.toLowerCase()](this, h)) || [];
129 | return arr[0];
130 | }
131 | },
132 | props: {
133 | // 当前控件的类型
134 | ele: {
135 | type: String,
136 | default: "input"
137 | },
138 | // 当前控件的配置
139 | obj: {
140 | type: Object,
141 | default () {
142 | return {};
143 | }
144 | },
145 | labelWidth: {
146 | type: Number,
147 | default: 100
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/components/custom_form/Render.js:
--------------------------------------------------------------------------------
1 | import ItemIcon from './ItemIcon';
2 | import input from './control/Input';
3 | import checkbox from './control/CheckBox';
4 | import radio from './control/Radio';
5 | import select from './control/Select';
6 | import text from './control/Text';
7 | import cascader from './control/Cascader';
8 | import title from './control/Title';
9 | import hr from './control/Hr';
10 | import p from './control/P';
11 | import uploads from './control/Uploads';
12 | import datepicker from './control/DatePicker';
13 | import address from './control/Address';
14 |
15 | import trigger from './config/trigger';
16 |
17 | const form_item = {
18 | title,
19 | hr,
20 | p,
21 | input,
22 | select,
23 | radio,
24 | checkbox,
25 | datepicker,
26 | cascader,
27 | address,
28 | uploads,
29 | text,
30 | };
31 |
32 | const displayControl = (_self, sortableItem, name, value) => {
33 | // 默认不显示
34 | let display = false;
35 | for (let i in sortableItem) {
36 | // 循环出sortableItem内被关联字段并且其状态为显示并且其值与用户预设被关联字段值匹配
37 | // 不匹配,进行下一次判断
38 | if (sortableItem[i].obj.name != name || !sortableItem[i].obj.visibility) {
39 | continue;
40 | }
41 | // checkbox的value为数组, 判断是否存在 非数组直接比对字符串
42 | if ((Array.isArray(sortableItem[i].obj.value) && sortableItem[i].obj.value.indexOf(value) >= 0) ||
43 | sortableItem[i].obj.value == value) {
44 | display = true;
45 | // name唯一,已匹配则不必循环之后数据
46 | break;
47 | }
48 | }
49 | return display;
50 | }
51 |
52 | export default {
53 | name: 'renders',
54 | render(h) {
55 | var $this = this;
56 | // 获取当前控件渲染
57 | const arr = (form_item[this.ele.toLowerCase()] && form_item[this.ele.toLowerCase()](this, h)) || [];
58 | // 拥有绑定的值,需回填到控件
59 | this.$set(this.obj, 'value', typeof this.value !== "undefined" ? this.value : this.obj.value);
60 | // 显示配置按钮并且控件允许被配置
61 | const item_icon = this.configIcon && this.obj.config ? ItemIcon(this, h) : [];
62 | // 已被绑定name,且require为必填,视为校验字段
63 | const validate = !!this.obj.name && !!this.obj.require;
64 | // 非 Title Hr P 需要FormItem
65 | if (['title', 'hr', 'p'].indexOf((this.ele.toLowerCase())) < 0) {
66 | // 关联的组件判断是否展示
67 | if (this.obj.relation && !displayControl(this, this.sortableItem, this.obj.relation_name, this.obj.relation_value)) {
68 | // 隐藏该控件并设置该控件标记为隐藏
69 | this.$emit('changeVisibility', this.index, false);
70 | return h("span");
71 | }
72 | // 设置该控件标记为显示
73 | this.$emit('changeVisibility', this.index, true);
74 | let FormItem = {
75 | class: {
76 | 'items': true,
77 | 'sortable-items-required': validate
78 | },
79 | props: {
80 | label: (this.obj.label || this.ele) + ':',
81 | // 指定验证name
82 | prop: this.obj.name || 'temp',
83 | // 验证规则
84 | rules: {
85 | required: validate,
86 | message: this.obj.ruleError || '该项为必填项',
87 | trigger: trigger[this.obj.type],
88 | validator: (rule, value, callback) => {
89 | // 没有配置按钮并且允许验证
90 | if (!this.configIcon && validate && (Array.isArray(value) ? !value.length : !value)) {
91 | callback(new Error('该项为必填项'));
92 | } else {
93 | callback();
94 | }
95 | }
96 | },
97 | },
98 | style: {
99 | // 是否显示行内元素
100 | display: this.obj.inlineBlock ? 'inline-block' : 'block',
101 | // 行内元素width为30%
102 | width: this.obj.inlineBlock ? '33%' : 'auto',
103 | }
104 | };
105 | return h(
106 | "FormItem", FormItem,
107 | arr.concat(item_icon)
108 | );
109 | } else {
110 | return h(
111 | "div", {
112 | style: {
113 | 'position': 'relative'
114 | },
115 | class: {
116 | items: true
117 | },
118 | },
119 | arr.concat(item_icon)
120 | );
121 | }
122 | },
123 | props: {
124 | // 当前控件的类型
125 | ele: {
126 | type: String,
127 | default: "input"
128 | },
129 | // 当前控件的配置
130 | obj: {
131 | type: Object,
132 | default () {
133 | return {};
134 | }
135 | },
136 | // 当前控件的index,config 和 delete 会用到
137 | index: {
138 | type: Number,
139 | default: 0
140 | },
141 | // 整个表单的数据
142 | data: {
143 | type: Object,
144 | default () {
145 | return {}
146 | }
147 | },
148 | // 是否显示配置按钮
149 | configIcon: {
150 | type: Boolean,
151 | default: false
152 | },
153 | // 当前控件绑定的值 方便数据回填
154 | value: [String, Number, Array],
155 | // 当前被clone控件列表
156 | sortableItem: {
157 | type: Array,
158 | default () {
159 | return [];
160 | }
161 | }
162 | }
163 | }
--------------------------------------------------------------------------------
/src/components/custom_form/components/Uploads/upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
点击或者拖拽文件到此处进行上传
7 |
8 |
9 |
27 |
35 |
36 |
37 |
142 |
178 |
--------------------------------------------------------------------------------
/src/components/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
14 |
27 |
28 |
29 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
306 |
--------------------------------------------------------------------------------