Browse Source

测试线提交

lilinfeng 3 months ago
parent
commit
2130a06903
72 changed files with 14035 additions and 20 deletions
  1. 2 2
      .env.production
  2. 8 0
      src/api/governmentCloud/knowledgeBase/knowledgeBase.js
  3. 1 1
      src/api/governmentCloud/processConfiguration/processConfiguration.js
  4. 39 0
      src/api/governmentCloud/questionnaireInvestigation/questionnaireInvestigation.js
  5. 106 1
      src/components/Generator/index/RightComponents/CMember.vue
  6. 1 1
      src/components/Generator/index/RightComponents/CSelect.vue
  7. 9 1
      src/components/Generator/render/render.js
  8. 111 0
      src/components/Question/QuestionnaireCheckbox/index.vue
  9. 75 0
      src/components/Question/QuestionnaireInput/index.vue
  10. 112 0
      src/components/Question/QuestionnaireRadio/index.vue
  11. 83 0
      src/components/Question/QuestionnaireTextarea/index.vue
  12. 69 0
      src/components/Question/QuestionnaireTopTip/index.vue
  13. 18 0
      src/components/index.js
  14. 4 4
      src/utils/apiUrl.js
  15. 2 2
      src/views/governmentCloud/computerroommanagement/computerRoomQR/index.vue
  16. 252 0
      src/views/governmentCloud/knowledgeBase/Form.vue
  17. 42 6
      src/views/governmentCloud/knowledgeBase/index.vue
  18. 9 2
      src/views/governmentCloud/processConfiguration/ruleForm.vue
  19. 330 0
      src/views/governmentCloud/questionnaireInvestigation/Allocation.vue
  20. 200 0
      src/views/governmentCloud/questionnaireInvestigation/IssuedForm.vue
  21. 211 0
      src/views/governmentCloud/questionnaireInvestigation/Statistics.vue
  22. 47 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Amount/index.vue
  23. 219 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/BillRule/index.vue
  24. 141 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/ComplexHeader/index.vue
  25. 97 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/DicSelect/index.vue
  26. 37 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Iframe/index.vue
  27. 64 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/JnpfDateRangePicker/index.vue
  28. 95 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/JnpfNumber/index.vue
  29. 532 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Location/index.vue
  30. 193 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/MaskConfigDialog/index.vue
  31. 69 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/NumRange/index.vue
  32. 92 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Sign/index.vue
  33. 48 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/UserSelect/index.vue
  34. 110 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/comConfig.js
  35. 1988 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/config.js
  36. 18 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/css.js
  37. 35 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/drawingDefalut.js
  38. 508 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/html.js
  39. 405 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/itsmConfig.js
  40. 256 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/js.js
  41. 27 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/ruleTrigger.js
  42. 510 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/DraggableItem.vue
  43. 347 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/DraggableItemApp.vue
  44. 223 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/FieldDialog.vue
  45. 121 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/FormScript.vue
  46. 1158 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/Home.vue
  47. 89 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/JsonDrawer.vue
  48. 129 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/BatchEditing.vue
  49. 76 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QCheckbox.vue
  50. 76 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QRadio.vue
  51. 30 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTextarea.vue
  52. 42 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTip.vue
  53. 37 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTitle.vue
  54. 141 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/dynamicMixin.js
  55. 43 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/handelFlidMixin.js
  56. 70 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/mixin.js
  57. 29 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/tpl.vue
  58. 749 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightPanel.vue
  59. 86 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/ScriptDemo.vue
  60. 65 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/StyleScript.vue
  61. 108 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/questionnaireComponentConfig.js
  62. 1077 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/Parser.vue
  63. 324 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/example/Index.vue
  64. 3 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/index.js
  65. 71 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/preview/index.vue
  66. 116 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/render/render.js
  67. 581 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/styles/home.scss
  68. 136 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/styles/index.scss
  69. 54 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/db.js
  70. 512 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/index.js
  71. 154 0
      src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/useTextMask.js
  72. 213 0
      src/views/governmentCloud/questionnaireInvestigation/index.vue

+ 2 - 2
.env.production

@@ -2,7 +2,7 @@
 ENV = 'production'
 
 # VUE_APP_BASE_API = 'http://192.168.103.102:58080'
-# VUE_APP_BASE_API = 'http://192.168.103.104:58080'
+VUE_APP_BASE_API = 'http://192.168.103.104:58080'
 # VUE_APP_BASE_API = 'https://test.api.shidaiyun.net'
-VUE_APP_BASE_API =  "https://api.shidaiyun.net"
+# VUE_APP_BASE_API =  "https://api.shidaiyun.net"
 

+ 8 - 0
src/api/governmentCloud/knowledgeBase/knowledgeBase.js

@@ -66,3 +66,11 @@ export function delKnowledge(id) {
     method: 'DELETE'
   })
 }
+
+// 审核知识信息
+export function audit(id) {
+  return request({
+    url: `/api/web/knowledge/audit?id=${id}`,
+    method: 'GET'
+  })
+}

+ 1 - 1
src/api/governmentCloud/processConfiguration/processConfiguration.js

@@ -156,7 +156,7 @@ export const saveProcessAdvanced = (data) => {
 // 获取我方保存的规则数据
 export const getRuleByProcessDefKey = (data) => {
   return request({
-    url: `/api/activiti/zhy/getRuleByProcessDefKey`,
+    url: `/api/itsm/CloudOperationMaintenance/getRuleByProcessDefKey`,
     method: "GET",
     data: data
   });

+ 39 - 0
src/api/governmentCloud/questionnaireInvestigation/questionnaireInvestigation.js

@@ -0,0 +1,39 @@
+import request from "@/utils/request";
+
+// 获取问卷列表
+export const getQuestionnaireList = data => {
+  return request({
+    url: `/api/Questionnaire/Questionnaire/getList`,
+    method: "POST",
+    data
+  });
+};
+
+// 新增保存问卷
+export const addBaseQUestionnaire = data => {
+  return request({
+    url: `/api/BaseQuestionnaire/BaseQuestionnaire`,
+    method: "POST",
+    data
+  });
+};
+
+
+// 下发问卷
+export const IssuedQuestionnaire = data => {
+  return request({
+    url: `/api/BaseQuestionnaire/BaseQuestionnaire/push`,
+    method: "POST",
+    data
+  });
+};
+
+// 获取问卷
+export const getQuestionnaireDetail = id => {
+  return request({
+    url: `/api/BaseQuestionnaire/BaseQuestionnaire/detail/${id}`,
+    method: "GET",
+    data: null
+  });
+};
+

+ 106 - 1
src/components/Generator/index/RightComponents/CMember.vue

@@ -4,6 +4,7 @@
             <el-radio-group v-model="activeData.__config__.valueType" size="small"
                 style="text-align:center" @change="valueTypeChange">
                 <el-radio label="1">发起相关</el-radio>
+                <el-radio label="2">正常</el-radio>
             </el-radio-group>
         </el-form-item>
 
@@ -22,6 +23,105 @@
 
         </template>
 
+        <template v-if="activeData.__config__.valueType == '2'">
+            <el-divider>数据选项</el-divider>
+            <el-form-item label="数据来源" label-width="90px">
+                <el-radio-group v-model="activeData.__config__.isApi" size="small"
+                    style="text-align:center" @change="dataTypeChange">
+                    <el-radio label="1">API</el-radio>
+                    <el-radio label="2">数据字典</el-radio>
+                </el-radio-group>
+            </el-form-item>
+            <template v-if="activeData.__config__.isApi == '1'">
+                <el-form-item label="API路径" label-width="90px">
+                    <el-row class="jnpf-el-row">
+                        <el-col :span="18">
+                            <el-input v-model="activeData.__config__.fieldApi"
+                                placeholder="请输入动态API接口路径">
+                            </el-input>
+                        </el-col>
+                        <el-col :span="6">
+                            <el-button @click="getApiOption(activeData.__config__.fieldApi)">
+                                确认</el-button>
+                        </el-col>
+                    </el-row>
+
+                </el-form-item>
+                <el-form-item label="存储字段">
+                    <el-autocomplete class="inline-input" v-model="activeData.props.value"
+                        :fetch-suggestions="querySearch" placeholder="请输入存储字段" style="width:100%"
+                        @select="handleSelect($event,'value')">
+                        <template slot-scope="{ item }">
+                            <div class="sale-order-popper-item">
+                                <span>{{ item.defaultValue}}</span>
+                            </div>
+                        </template>
+                    </el-autocomplete>
+                </el-form-item>
+                <el-form-item label="显示字段">
+                    <el-autocomplete class="inline-input" v-model="activeData.props.label"
+                        :fetch-suggestions="querySearch" placeholder="请输入显示字段" style="width:100%"
+                        @select="handleSelect($event,'label')">
+                        <template slot-scope="{ item }">
+                            <div class="sale-order-popper-item">
+                                <span>{{ item.defaultValue}}</span>
+                            </div>
+                        </template>
+                    </el-autocomplete>
+                </el-form-item>
+
+            </template>
+            <template v-if="activeData.__config__.isApi == '2'">
+                <el-form-item label="数据字典">
+                    <el-row class="jnpf-el-row">
+                        <el-col :span="18">
+                            <JnpfTreeSelect :options="dicOptions"
+                                v-model="activeData.__config__.fieldApi" placeholder="请选择数据字典"
+                                lastLevel clearable @change="dictionaryTypeChange"
+                                @selectChange="selectChange">
+                            </JnpfTreeSelect>
+                        </el-col>
+                        <el-col :span="6">
+                            <el-button @click="goDictionary()">
+                                添加</el-button>
+                        </el-col>
+                    </el-row>
+                </el-form-item>
+                <el-form-item label="存储字段">
+                    <el-autocomplete class="inline-input" v-model="activeData.props.value"
+                        :fetch-suggestions="querySearch" placeholder="请输入存储字段" style="width:100%"
+                        @select="handleSelect($event,'value')">
+                        <template slot-scope="{ item }">
+                            <div class="sale-order-popper-item">
+                                <span>{{ item.defaultValue}}</span>
+                            </div>
+                        </template>
+                    </el-autocomplete>
+                </el-form-item>
+                <el-form-item label="显示字段">
+                    <el-autocomplete class="inline-input" v-model="activeData.props.label"
+                        :fetch-suggestions="querySearch" placeholder="请输入显示字段" style="width:100%"
+                        @select="handleSelect($event,'label')">
+                        <template slot-scope="{ item }">
+                            <div class="sale-order-popper-item">
+                                <span>{{ item.defaultValue}}</span>
+                            </div>
+                        </template>
+                    </el-autocomplete>
+                </el-form-item>
+
+            </template>
+            <el-form-item label="默认值">
+                <el-select v-model="activeData.__config__.defaultValue" placeholder="选择默认值"
+                    clearable :multiple="activeData.multiple" filterable>
+                    <el-option v-for="(item,i) in activeData.options" :key="i"
+                        :label="item[activeData.props.label]" :value="item[activeData.props.value]">
+                    </el-option>
+                </el-select>
+            </el-form-item>
+            <el-divider />
+        </template>
+
     </el-row>
 </template>
 
@@ -74,7 +174,8 @@ export default {
                 this.activeData.__config__.fieldApi = null
             } else {
                 if (this.activeData.multiple) {
-                    this.activeData.__config__.defaultValue = [];
+                    delete this.activeData.__config__.defaultValue
+                    this.activeData.__config__.defaultValue = null;
                 } else {
                     this.activeData.__config__.defaultValue = '';
                 }
@@ -105,6 +206,10 @@ export default {
         dataTypeChange(val) {
             this.activeData.__config__.defaultValue = ''
             this.activeData.options = []
+            this.activeData.props = {
+                value: null,
+                label: null
+            }
             this.activeData.props.value = 'id'
             this.activeData.props.label = 'fullName'
             this.activeData.__config__.fieldApi = ''

+ 1 - 1
src/components/Generator/index/RightComponents/CSelect.vue

@@ -187,7 +187,7 @@ export default {
                 this.activeData.__config__.fieldApi = null
             } else {
                 if (this.activeData.multiple) {
-                    this.activeData.__config__.defaultValue = [];
+                    this.activeData.__config__.defaultValue = null;
                 } else {
                     this.activeData.__config__.defaultValue = '';
                 }

+ 9 - 1
src/components/Generator/render/render.js

@@ -91,6 +91,9 @@ export default {
       type: Object,
       default: () => { }
     },
+    index: {
+      type: Number,
+    },
     relations: Object,
   },
   render(h) {
@@ -105,12 +108,17 @@ export default {
     buildDataObject.call(this, confClone, dataObject, this.formData)
 
 
-
     // 单独给table组件做特殊处理
     if (this.conf.__config__.jnpfKey == 'custom-table') {
       dataObject.props.columns = this.conf.children
     }
 
+    // 单独给问卷做特殊处理
+    if (this.conf.__config__.jnpfKey.includes('question')) {
+      dataObject.props.index = this.index
+      dataObject.props.conf = this.conf
+    }
+
     return h(this.conf.__config__.tag, dataObject, children)
   }
 }

+ 111 - 0
src/components/Question/QuestionnaireCheckbox/index.vue

@@ -0,0 +1,111 @@
+<template>
+    <div class="">
+        <div class="f1 f">
+            <el-tag type="info">{{ conf.__config__.label }}</el-tag>
+        </div>
+        <div class="f2 f">
+            <span class="index" v-if="index - 1 < 10"> 0{{ index - 1 }}</span>
+            <span class="index" v-else>{{ index - 1 }}</span>
+            <span>{{ conf.title }}</span>
+        </div>
+        <div class="f3 f">
+            <el-input placeholder="请输入题目描述" v-model="conf.describe"></el-input>
+        </div>
+        <div class="f4">
+            <draggable class="drag-wrapper" :list="conf.options" :animation="340"
+                group="optionsGroup" handle=".option-drag">
+                <div v-for="item,index in conf.options" :key="index" class="option-child">
+                    <div class="select-line-icon option-drag">
+                        <i class="icon-ym icon-ym-darg" />
+                    </div>
+                    <div class="cicle"></div>
+                    {{item.fullName}}
+                </div>
+            </draggable>
+
+        </div>
+    </div>
+
+</template>
+
+<script>
+
+import draggable from 'vuedraggable'
+
+
+export default {
+    name: 'QuestionnaireRadio',
+    components: {
+        draggable
+    },
+    props: {
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        index: {
+            type: [Number],
+            required: true
+        },
+        addonBefore: {
+            type: String,
+            default: ''
+        },
+        addonAfter: {
+            type: String,
+            default: ''
+        },
+        detailed: {
+            type: Boolean,
+            default: false
+        },
+        useMask: {
+            type: Boolean,
+            default: false
+        },
+        maskConfig: {
+            type: Object,
+            default: () => { }
+        },
+        showOverflow: {
+            type: Boolean,
+            default: false
+        },
+        conf: {
+            type: Object,
+            default: () => { }
+        }
+    },
+    mounted() {
+        console.log(this.conf)
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.f {
+    margin-bottom: 10px;
+}
+.f2 {
+    display: flex;
+    align-items: center;
+    color: #000;
+    font-size: 20px;
+    .index {
+        margin-right: 10px;
+        font-weight: bold;
+    }
+}
+.f4 {
+    .option-child {
+        display: flex;
+        align-items: center;
+        .cicle {
+            width: 15px;
+            height: 15px;
+            border: 2px solid #000;
+            margin: 0 10px;
+        }
+    }
+}
+</style>

+ 75 - 0
src/components/Question/QuestionnaireInput/index.vue

@@ -0,0 +1,75 @@
+<template>
+    <div contenteditable="false" class="title">{{ value }}</div>
+</template>
+
+<script>
+import { useTextMask } from '@/components/Generator/utils/useTextMask';
+export default {
+    name: 'QuestionnaireInput',
+    props: {
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        addonBefore: {
+            type: String,
+            default: ''
+        },
+        addonAfter: {
+            type: String,
+            default: ''
+        },
+        detailed: {
+            type: Boolean,
+            default: false
+        },
+        useMask: {
+            type: Boolean,
+            default: false
+        },
+        maskConfig: {
+            type: Object,
+            default: () => { }
+        },
+        showOverflow: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {
+            innerValue: '',
+            maskedValue: ''
+        };
+    },
+    watch: {
+        value: {
+            handler(val) {
+                this.setValue(val);
+            },
+            immediate: true
+        },
+    },
+    methods: {
+        setValue(value) {
+            this.innerValue = value;
+            if (!this.useMask) return (this.maskedValue = value);
+            const { getMaskedText } = useTextMask(this.maskConfig);
+            this.maskedValue = getMaskedText(value);
+        },
+        onChange(value) {
+            this.$emit('input', value);
+        }
+    }
+};
+</script>
+<style lang="scss" scoped>
+.title {
+    font-size: 40px;
+    margin: 0 auto;
+    font-weight: bold;
+    padding: 20px;
+    text-align: center;
+    color: #000;
+}
+</style>

+ 112 - 0
src/components/Question/QuestionnaireRadio/index.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="">
+        <div class="f1 f">
+            <el-tag type="info">{{ conf.__config__.label }}</el-tag>
+        </div>
+        <div class="f2 f">
+            <span class="index" v-if="index - 1 < 10"> 0{{ index - 1 }}</span>
+            <span class="index" v-else>{{ index - 1 }}</span>
+            <span>{{ conf.title }}</span>
+        </div>
+        <div class="f3 f">
+            <el-input placeholder="请输入题目描述" v-model="conf.describe"></el-input>
+        </div>
+        <div class="f4">
+            <draggable class="drag-wrapper" :list="conf.options" :animation="340"
+                group="optionsGroup" handle=".option-drag">
+                <div v-for="item,index in conf.options" :key="index" class="option-child">
+                    <div class="select-line-icon option-drag">
+                        <i class="icon-ym icon-ym-darg" />
+                    </div>
+                    <div class="cicle"></div>
+                    {{item.fullName}}
+                </div>
+            </draggable>
+
+        </div>
+    </div>
+
+</template>
+
+<script>
+
+import draggable from 'vuedraggable'
+
+
+export default {
+    name: 'QuestionnaireRadio',
+    components: {
+        draggable
+    },
+    props: {
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        index: {
+            type: [Number],
+            required: true
+        },
+        addonBefore: {
+            type: String,
+            default: ''
+        },
+        addonAfter: {
+            type: String,
+            default: ''
+        },
+        detailed: {
+            type: Boolean,
+            default: false
+        },
+        useMask: {
+            type: Boolean,
+            default: false
+        },
+        maskConfig: {
+            type: Object,
+            default: () => { }
+        },
+        showOverflow: {
+            type: Boolean,
+            default: false
+        },
+        conf: {
+            type: Object,
+            default: () => { }
+        }
+    },
+    mounted() {
+        console.log(this.conf)
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.f {
+    margin-bottom: 10px;
+}
+.f2 {
+    display: flex;
+    align-items: center;
+    color: #000;
+    font-size: 20px;
+    .index {
+        margin-right: 10px;
+        font-weight: bold;
+    }
+}
+.f4 {
+    .option-child {
+        display: flex;
+        align-items: center;
+        .cicle {
+            width: 15px;
+            height: 15px;
+            border-radius: 50%;
+            border: 2px solid #000;
+            margin: 0 10px;
+        }
+    }
+}
+</style>

+ 83 - 0
src/components/Question/QuestionnaireTextarea/index.vue

@@ -0,0 +1,83 @@
+<template>
+    <div class="">
+        <div class="f1 f">
+            <el-tag type="info">{{ conf.__config__.label }}</el-tag>
+        </div>
+        <div class="f2 f">
+            <span class="index" v-if="index - 1 < 10"> 0{{ index - 1 }}</span>
+            <span class="index" v-else>{{ index - 1 }}</span>
+            <span>{{ conf.title }}</span>
+        </div>
+        <div class="f3 f">
+            <el-input placeholder="请输入题目描述" v-model="conf.describe"></el-input>
+        </div>
+        <div class="f4">
+            <el-input placeholder="请输入" disabled type="textarea" rows="5"
+                v-model="conf.__config__.defaultValue"></el-input>
+        </div>
+    </div>
+
+</template>
+
+<script>
+export default {
+    name: 'QuestionnaireTextarea',
+    props: {
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        index: {
+            type: [Number],
+            required: true
+        },
+        addonBefore: {
+            type: String,
+            default: ''
+        },
+        addonAfter: {
+            type: String,
+            default: ''
+        },
+        detailed: {
+            type: Boolean,
+            default: false
+        },
+        useMask: {
+            type: Boolean,
+            default: false
+        },
+        maskConfig: {
+            type: Object,
+            default: () => { }
+        },
+        showOverflow: {
+            type: Boolean,
+            default: false
+        },
+        conf: {
+            type: Object,
+            default: () => { }
+        }
+    },
+    mounted() {
+        console.log(this.conf)
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.f {
+    margin-bottom: 10px;
+}
+.f2 {
+    display: flex;
+    align-items: center;
+    color: #000;
+    font-size: 20px;
+    .index {
+        margin-right: 10px;
+        font-weight: bold;
+    }
+}
+</style>

+ 69 - 0
src/components/Question/QuestionnaireTopTip/index.vue

@@ -0,0 +1,69 @@
+<template>
+    <div>
+        {{ value }}
+    </div>
+</template>
+
+<script>
+import { useTextMask } from '@/components/Generator/utils/useTextMask';
+export default {
+    name: 'QuestionnaireTopTip',
+    props: {
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        addonBefore: {
+            type: String,
+            default: ''
+        },
+        addonAfter: {
+            type: String,
+            default: ''
+        },
+        detailed: {
+            type: Boolean,
+            default: false
+        },
+        useMask: {
+            type: Boolean,
+            default: false
+        },
+        maskConfig: {
+            type: Object,
+            default: () => { }
+        },
+        showOverflow: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {
+            innerValue: '',
+            maskedValue: ''
+        };
+    },
+    watch: {
+        value: {
+            handler(val) {
+                this.setValue(val);
+            },
+            immediate: true
+        },
+    },
+    methods: {
+        setValue(value) {
+            this.innerValue = value;
+            if (!this.useMask) return (this.maskedValue = value);
+            const { getMaskedText } = useTextMask(this.maskConfig);
+            this.maskedValue = getMaskedText(value);
+        },
+        onChange(value) {
+            this.$emit('input', value);
+        }
+    }
+};
+</script>
+<style lang="scss" scoped>
+</style>

+ 18 - 0
src/components/index.js

@@ -57,6 +57,17 @@ import JnpfOpenData from '@/components/Jnpf/OpenData'
 // 自定义组件 custom-table
 import CustomTable from '@/components/ITSM/CustomTable'
 
+
+//自定义问卷组件
+
+import QuestionnaireInput from '@/components/Question/QuestionnaireInput'
+import QuestionnaireTopTip from '@/components/Question/QuestionnaireTopTip'
+import QuestionnaireTextarea from '@/components/Question/QuestionnaireTextarea'
+import QuestionnaireRadio from '@/components/Question/QuestionnaireRadio'
+import QuestionnaireCheckbox from '@/components/Question/QuestionnaireCheckbox'
+
+
+
 import NumRange from '@/components/Generator/components/NumRange'
 import DicSelect from '@/components/Generator/components/DicSelect'
 import BillRule from '@/components/Generator/components/BillRule'
@@ -133,5 +144,12 @@ export default {
     // 自定义组件 custom-table
     Vue.component('CustomTable', CustomTable)
 
+
+    // 自定义问卷组件 
+    Vue.component('QuestionnaireInput', QuestionnaireInput)
+    Vue.component('QuestionnaireTopTip', QuestionnaireTopTip)
+    Vue.component('QuestionnaireTextarea', QuestionnaireTextarea)
+    Vue.component('QuestionnaireRadio', QuestionnaireRadio)
+    Vue.component('QuestionnaireCheckbox', QuestionnaireCheckbox)
   }
 }

+ 4 - 4
src/utils/apiUrl.js

@@ -1,11 +1,11 @@
 module.exports = {
   // 开发环境接口配置
   // APIURl: "http://localhost:30000"
-  // APIURl: "http://10.21.12.74:30000"
-  APIURl: "http://10.0.0.112:30000"
-  // APIURl: "https://test.api.shidaiyun.net",
+  // APIURl: "http://10.21.12.137:30000",
+  // APIURl: "http://192.168.103.104:58080",
+  APIURl: "https://test.api.shidaiyun.net",
   // APIURl: "https://api.shidaiyun.net",
-  // minioPath: process.env.NODE_ENV === 'development' ? 'https://oss.shidaiyun.net/' : 'http://192.168.103.105:18000/'
+  minioPath: process.env.NODE_ENV === 'development' ? 'https://oss.shidaiyun.net/' : 'http://192.168.103.105:18000/'
   // minioPath: 'https://oss.shidaiyun.net/'
 };
 

+ 2 - 2
src/views/governmentCloud/computerroommanagement/computerRoomQR/index.vue

@@ -165,7 +165,7 @@ export default {
         exportQR() {
             if (this.selectedData && this.selectedData.length) {
                 this.listLoading = true;
-                let ids = ['123']
+                let ids = []
                 this.selectedData.forEach(item => {
                     ids.push(item.id)
                 })
@@ -177,7 +177,7 @@ export default {
                         'content-type': 'application/json',
                     },
                     responseType: 'blob',
-                    data: ['123', '21323']
+                    data: ids
                 }).then(res => {
                     console.log(res)
                     if (res.code !== 400) {

+ 252 - 0
src/views/governmentCloud/knowledgeBase/Form.vue

@@ -0,0 +1,252 @@
+<template>
+    <el-dialog :title="!dataForm.id ? '新建' :'编辑'" :close-on-click-modal="false" append-to-body
+        :visible.sync="visible" class="JNPF-dialog JNPF-dialog_center" lock-scroll width="1000px">
+        <el-row :gutter="15" class="">
+            <el-form ref="formRef" :model="dataForm" :rules="dataRule" size="small"
+                label-width="100px" label-position="right">
+                <template v-if="!loading">
+                    <!-- 具体表单 -->
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="标题" align="left" prop="title">
+                            <JnpfInput v-model="dataForm.title" placeholder="请输入" clearable
+                                :style='{"width":"100%"}'>
+                            </JnpfInput>
+                        </jnpf-form-tip-item>
+                    </el-col>
+
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="知识库" align="left" prop="knowledgeBaseId">
+                            <JnpfTreeSelect :options="treeOptions"
+                                v-model="dataForm.knowledgeBaseId" placeholder="请选择数据字典" lastLevel
+                                clearable @change="knowledgeBaseChange" :props="treeSelectProps"
+                                @selectChange="selectChange">
+                            </JnpfTreeSelect>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="标签" align="left" prop="label">
+                            <el-select v-model="dataForm.label" multiple filterable allow-create
+                                default-first-option placeholder="请选择标签">
+                                <el-option v-for="item in labelOptions" :key="item.value"
+                                    :label="item.label" :value="item.value">
+                                </el-option>
+                            </el-select>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="内容" align="left" prop="content">
+                            <JnpfEditor v-model="dataForm.content" ref="myQuillEditor" />
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <!-- 表单结束 -->
+                </template>
+            </el-form>
+
+        </el-row>
+        <span slot="footer" class="dialog-footer">
+            <el-button @click="visible = false"> 取 消</el-button>
+            <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading"> 确
+                定</el-button>
+        </span>
+    </el-dialog>
+</template>
+
+
+<script>
+import request from '@/utils/request'
+import { mapGetters } from "vuex";
+
+
+import {
+    getKnowledgeBase,
+} from '@/api/governmentCloud/knowledgeBase/knowledgeBase'
+
+export default {
+    components: {},
+    props: [],
+    data() {
+        return {
+            dataFormSubmitType: 0,
+            continueBtnLoading: false,
+            index: 0,
+            visible: false,
+            loading: false,
+            btnLoading: false,
+            dataValueAll: {},
+            addTableConf: {
+            },
+            //可选范围默认值
+            ableAll: {
+            },
+            tableRows: {
+            },
+            Vmodel: "",
+            currVmodel: "",
+            dataForm: {
+                knowledgeBaseId: '',
+                title: '',
+                label: [],
+                content: '',
+            },
+            tableRequiredData: {},
+            dataRule:
+            {
+                title: [
+                    {
+                        required: true,
+                        message: '请输入标题',
+                        trigger: 'blur'
+                    },
+                ],
+                label: [
+                    {
+                        required: true,
+                        message: '请选择标签',
+                        trigger: 'change'
+                    },
+                ],
+                content: [
+                    {
+                        required: true,
+                        message: '请输入内容',
+                        trigger: 'change'
+                    },
+                ],
+                knowledgeBaseId: [
+                    {
+                        required: true,
+                        message: '请选择知识库',
+                        trigger: 'change'
+                    },
+                ],
+            },
+            treeOptions: [],
+            labelOptions: [],
+            treeSelectProps: {
+                value: 'id',             // ID字段名
+                label: 'name',       // 显示名称
+                children: 'children',    // 子级字段名
+            }
+        }
+    },
+    computed: {
+        ...mapGetters(['userInfo']),
+
+
+    },
+    watch: {},
+    created() {
+        this.dataAll()
+        this.dataValueAll = JSON.parse(JSON.stringify(this.dataForm))
+    },
+    mounted() { },
+    methods: {
+        knowledgeBaseChange(e) {
+            console.log(e)
+        },
+        selectChange(e) {
+
+        },
+        getInfo(id) {
+            request({
+                url: '/api/SystemInterface/SystemInterface/' + id,
+                method: 'get'
+            }).then(res => {
+                this.dataInfo(res.data)
+            });
+        },
+        goBack() {
+            this.visible = false
+            this.$emit('refreshDataList', true)
+        },
+        dataAll() {
+            this.getKnowledgeBase();
+        },
+        getKnowledgeBase() {
+            getKnowledgeBase().then(res => {
+                this.treeOptions = res.data
+            })
+        },
+        clearData() {
+            this.dataForm = JSON.parse(JSON.stringify(this.dataValueAll))
+        },
+        init(id) {
+            this.loading = true
+            console.log(this.dataValueAll)
+            console.log(id)
+            this.clearData()
+            if (id) {
+                this.dataForm.id = id
+            }
+            this.$nextTick(() => {
+                if (this.dataForm.id) {
+                    request({
+                        url: '/api/web/knowledge/' + this.dataForm.id,
+                        method: 'get'
+                    }).then(res => {
+                        this.dataInfo(res.data)
+                        this.loading = false
+                        this.visible = true;
+                    });
+                } else {
+                    this.visible = true;
+                    this.loading = false
+                }
+            });
+        },
+
+        // 表单提交
+        dataFormSubmit(type) {
+            this.dataFormSubmitType = type ? type : 0
+            this.$refs['formRef'].validate((valid) => {
+                if (valid) {
+                    this.request()
+                }
+            })
+        },
+        request() {
+            let _data = this.dataList()
+            this.btnLoading = true
+
+
+            _data.label = JSON.stringify(_data.label)
+
+            request({
+                url: '/api/web/knowledge/save',
+                method: 'POST',
+                data: _data
+            }).then((res) => {
+                this.$message({
+                    message: res.msg,
+                    type: 'success',
+                    duration: 1000,
+                    onClose: () => {
+                        this.visible = false
+                        this.btnLoading = false
+                        this.clearData()
+                        this.$emit('refresh', true)
+
+                    }
+                })
+            }).catch(() => {
+                this.btnLoading = false
+                this.continueBtnLoading = false
+            })
+
+        },
+        dataList() {
+            var _data = JSON.parse(JSON.stringify(this.dataForm));
+            return _data;
+        },
+        dataInfo(dataAll) {
+            this.dataForm.content = dataAll.content
+            this.dataForm.label = JSON.parse(dataAll.label)
+            this.dataForm.knowledgeBaseId = dataAll.knowledgeBaseId
+            this.dataForm.title = dataAll.title
+            this.isEdit = true
+            this.dataAll()
+        },
+    },
+}
+
+</script>

+ 42 - 6
src/views/governmentCloud/knowledgeBase/index.vue

@@ -55,7 +55,7 @@
                 <div class="JNPF-common-head" style="justify-content: space-between;">
                     <div>
                         <el-button type="primary" icon="el-icon-plus"
-                            @click="addFormVisible = true">新增</el-button>
+                            @click="showFrom()">新增</el-button>
                     </div>
                     <div class="JNPF-common-head-right">
                         <el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
@@ -67,19 +67,32 @@
                 <JNPF-table v-loading="listLoading" :data="tableData" row-key="id"
                     v-if="refreshTable">
                     <el-table-column prop="title" label="标题" />
-                    <el-table-column prop="publishTime" label="发布时间" />
+
                     <el-table-column label="标签">
                         <template slot-scope="scope">
                             <el-tag v-for="item in scope.row.labelList"
                                 :key="item">{{ item }}</el-tag>
                         </template>
                     </el-table-column>
-                    <el-table-column label="操作" width="100">
+                    <el-table-column prop="creatorTime" label="创建时间">
+                        <template slot-scope="scope">
+                            <span>{{ scope.row.creatorTime || '无' }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="publishTime" label="发布时间">
+                        <template slot-scope="scope">
+                            <span>{{ scope.row.publishTime || '未发布' }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column label="操作" width="200">
                         <template slot-scope="scope">
+                            <el-button type="text" @click="showFrom(scope.row.id)">编辑</el-button>
                             <el-button type="text"
                                 @click="handleDetail(scope.row.id)">详情</el-button>
                             <el-button style="color: #F56C6C;" type="text"
                                 @click="handleDel(scope.row.id)">删除</el-button>
+                            <el-button type="text" v-if="scope.row.audit"
+                                @click="audit(scope.row.id)">审核</el-button>
                         </template>
                     </el-table-column>
                 </JNPF-table>
@@ -89,6 +102,7 @@
         </div>
         <TypeList v-if="drawer" ref="TypeList" @refreshDataList="initData" />
         <Detail v-if="drawerDetail" ref="Detail" />
+        <Form v-if="formVisible" @refresh="getKnowledgeList" ref="JNPFForm" />
     </div>
 </template>
 
@@ -96,16 +110,19 @@
 import {
     getKnowledgeBase,
     getKnowledgeList,
-    delKnowledge
+    delKnowledge,
+    audit
 } from '@/api/governmentCloud/knowledgeBase/knowledgeBase'
 import TypeList from './components/index'
 import Detail from './Detail'
+import Form from './Form.vue'
 
 export default {
     name: 'knowledgeBase',
     components: {
         TypeList,
-        Detail
+        Detail,
+        Form
     },
     data() {
         return {
@@ -130,7 +147,9 @@ export default {
             refreshTable: true,
             refreshTree: true,
             filterText: '',
-            drawerDetail: false
+            drawerDetail: false,
+            formVisible: false,
+
         }
     },
     watch: {
@@ -142,6 +161,23 @@ export default {
         this.initData(true)
     },
     methods: {
+        audit(id) {
+            audit(id).then(res => {
+                if (res.code == 200) {
+                    this.$message.success(res.msg)
+                    this.getKnowledgeList()
+                } else {
+                    this.$message.error(res.msg)
+                }
+            })
+        },
+        showFrom(id) {
+            this.formVisible = true
+            this.$nextTick(() => {
+                this.$refs.JNPFForm.init(id || null)
+            })
+
+        },
         search() {
             this.getKnowledgeList()
         },

+ 9 - 2
src/views/governmentCloud/processConfiguration/ruleForm.vue

@@ -59,7 +59,8 @@ export default {
         init(orderDetail) {
             this.orderDetail = orderDetail
             let params = {
-                processDefKey: this.orderDetail.itsmProKey
+                processDefKey: this.orderDetail.itsmProKey,
+                version: this.orderDetail.version
             }
             getRuleByProcessDefKey(params).then(res => {
                 if (res.code == 200) {
@@ -96,11 +97,15 @@ export default {
         // 表单提交
         dataFormSubmit() {
             if (this.jsonStr) {
+                console.log(JSON.parse(this.jsonStr))
                 let params = {
                     version: this.orderDetail.version,
                     preNum: this.orderDetail.itsmProKey,
                     processAdvanced: this.jsonStr,
-                    id: this.ruleId
+                }
+
+                if (this.ruleId) {
+                    params.id = this.ruleId
                 }
                 this.btnLoading = true
                 saveProcessAdvanced(params).then(res => {
@@ -111,6 +116,8 @@ export default {
                         this.visible = false
                     } else {
                         this.$message.error(res.msg)
+                        this.btnLoading = false
+
                     }
                 })
             } else {

+ 330 - 0
src/views/governmentCloud/questionnaireInvestigation/Allocation.vue

@@ -0,0 +1,330 @@
+<template>
+    <el-dialog :visible.sync="visible" fullscreen lock-scroll class="JNPF-full-dialog"
+        :show-close="false" :modal="false">
+        <div class="main">
+            <div class="top">
+                <div class="top-title" v-if="visible">
+                    <div style="opacity: 0.5;">问卷配置</div> /
+                    <div style="font-size: 20px;font-weight: bold;">
+                        {{itsmDrawingList[0].__config__.defaultValue}}
+                    </div>
+                </div>
+                <div>
+                    <el-button type="primary" @click="saveForm">
+                        保存
+                    </el-button>
+                    <el-button @click="closeDialog">
+                        取消
+                    </el-button>
+                </div>
+            </div>
+            <div class="content" v-if="showContent">
+                <Generator ref="generator" :conf="formData" :modelType="dataForm.type"
+                    :webType="dataForm.webType" :dbType="dbType" :formInfo='dataForm'
+                    @drawingListChange="drawingListChange" :isQuestionnaire="true"
+                    :itsmDrawingList="itsmDrawingList" />
+            </div>
+
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+
+import Generator from './components/Generator/index/Home'
+import mixin from '@/mixins/generator/form'
+
+import {
+    addBaseQUestionnaire, getQuestionnaireDetail
+} from "@/api/governmentCloud/questionnaireInvestigation/questionnaireInvestigation";
+
+import {
+    questionnairetopComponents, questionnaireinputComponents, questionnaireselectComponents
+} from './components/Generator/index/questionnaireComponentConfig'
+import {
+    deepClone
+} from '@/components/Generator/utils'
+export default {
+    mixins: [mixin],
+    components: { Generator },
+    data() {
+        return {
+            visible: false,
+            showContent: false,
+            itsmDrawingList: [
+                {
+                    __config__: {
+                        jnpfKey: "question-title",
+                        tag: "QuestionnaireInput",
+                        tagIcon: "icon-ym icon-ym-generator-input",
+                        required: false,
+                        noShow: false,
+                        isDisplay: true,
+                        showLabel: true,
+                        tableAlign: "left",
+                        tableFixed: "none",
+                        layout: "questionTopItem",
+                        id: '001',
+                        formId: '001',
+                        renderKey: "001",
+                        span: 24,
+                        dragDisabled: false,
+                        valueType: null,
+                        defaultValue: '默认问卷',
+                        parentDefault: null
+                    },
+                    __vModel__: "title",
+                    disabled: false,
+                    placeholder: null
+                },
+                {
+                    __config__: {
+                        jnpfKey: "question-tip",
+                        tag: "QuestionnaireTopTip",
+                        tagIcon: "icon-ym icon-ym-generator-input",
+                        required: false,
+                        noShow: false,
+                        isDisplay: true,
+                        showLabel: true,
+                        tableAlign: "left",
+                        tableFixed: "none",
+                        layout: "questionTopItem",
+                        id: '002',
+                        formId: '002',
+                        renderKey: "002",
+                        span: 24,
+                        dragDisabled: false,
+                        valueType: null,
+                        defaultValue: '为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,我们非常重视每位用户的宝贵意见,期待您的参与!现在我们就马上开始吧!',
+                        parentDefault: null
+                    },
+                    __vModel__: "top-tip",
+                    disabled: false,
+                    placeholder: null
+                }
+            ],
+            nowList: {
+                fields: null
+            },
+            questionnaireId: null
+
+        }
+    },
+    created() {
+        console.log(this.formData)
+    },
+    methods: {
+        init(id) {
+            this.visible = true
+            //编辑
+            if (id) {
+                this.questionnaireId = id
+                getQuestionnaireDetail(id).then(res => {
+                    if (res.code == 200) {
+
+                        let resData = res.data
+
+                        this.itsmDrawingList.forEach(item => {
+                            if (item.__vModel__ == 'title') {
+                                item.__config__.defaultValue = resData.topic
+                            } else if (item.__vModel__ == 'top-tip') {
+                                item.__config__.defaultValue = resData.description
+                            }
+                        })
+
+
+                        if (resData.questionnaireTopic) {
+                            resData.questionnaireTopic.forEach((item, index) => {
+                                let obj = null
+                                if (item.type == 'TEXTAREA') {
+                                    obj = deepClone(questionnaireinputComponents.find(n => n.__config__.jnpfKey == 'question-textarea'))
+                                    obj.__config__.defaultValue = item.content
+                                    obj.title = item.title
+                                    obj.description = item.description
+                                    obj.__config__.required = item.required
+                                    obj.describe = item.description
+                                    obj.__config__.renderKey = new Date() + index
+                                    obj.__config__.formId = 100 + index
+                                } else if (item.type == 'RADIO') {
+                                    obj = deepClone(questionnaireselectComponents.find(n => n.__config__.jnpfKey == 'question-radio'))
+                                    obj.__config__.defaultValue = item.content
+                                    obj.title = item.title
+                                    obj.description = item.description
+                                    obj.__config__.required = item.required
+                                    obj.describe = item.description
+                                    obj.__config__.renderKey = new Date() + index
+                                    obj.__config__.formId = 100 + index
+                                    obj.options = []
+                                    item.questionnaireOption.forEach((item2, index2) => {
+                                        let obj2 = {
+                                            id: null,
+                                            fullName: null
+                                        }
+                                        obj2.id = item2.code,
+                                            obj2.fullName = item2.optionContent
+                                        obj.options.push(obj2)
+                                    })
+                                } else if (item.type == 'CHECKBOX') {
+                                    obj = deepClone(questionnaireselectComponents.find(n => n.__config__.jnpfKey == 'question-checkbox'))
+                                    obj.__config__.defaultValue = item.content
+                                    obj.title = item.title
+                                    obj.description = item.description
+                                    obj.__config__.required = item.required
+                                    obj.describe = item.description
+                                    obj.__config__.renderKey = new Date() + index
+                                    obj.__config__.formId = 100 + index
+                                    obj.options = []
+                                    item.questionnaireOption.forEach((item2, index2) => {
+                                        let obj2 = {
+                                            id: null,
+                                            fullName: null
+                                        }
+                                        obj2.id = item2.code,
+                                            obj2.fullName = item2.optionContent
+                                        obj.options.push(obj2)
+                                    })
+                                }
+                                if (obj) this.itsmDrawingList.push(obj)
+                            })
+                        }
+                        this.showContent = true
+                    } else {
+                        this.$message({
+                            type: 'error',
+                            message: res.msg,
+                            duration: 1500,
+                            onClose: () => {
+                                this.closeDialog()
+                            }
+                        })
+                    }
+                })
+            } else {
+                this.showContent = true
+            }
+        },
+        getPlaceholder(n) {
+            if (['custom-radio', 'custom-checkbox', 'custom-select', 'custom-group', 'custom-datePicker', 'custom-timePicker', 'custom-member'].includes(n.__config__.jnpfKey)) {
+                n.placeholder = '请选择' + n.__config__.label
+            } else if (['custom-uploadFile'].includes(n.__config__.jnpfKey)) {
+                n.placeholder = '请上传' + n.__config__.label
+            } else {
+                n.placeholder = '请输入' + n.__config__.label
+            }
+        },
+        drawingListChange(list) {
+            this.nowList.fields = JSON.parse(JSON.stringify(list))
+        },
+
+        //关闭配置页面
+        closeDialog() {
+            this.$emit("close");
+        },
+        getType(type) {
+            let typeObj = {
+                TEXTAREA: 'question-textarea',
+                RADIO: 'question-radio',
+                CHECKBOX: 'question-checkbox',
+            }
+            let str = ''
+            for (let key in typeObj) {
+                if (typeObj[key] == type) {
+                    str = key
+                }
+            }
+            return str
+        },
+        //保存配置
+        saveForm() {
+            console.log(this.nowList)
+            let params = {
+                topic: this.nowList.fields[0].__config__.defaultValue,
+                description: this.nowList.fields[1].__config__.defaultValue,
+                questionnaireTopic: []
+            }
+
+            this.nowList.fields.splice(0, 2)
+            this.nowList.fields.forEach((field, index) => {
+                let obj = {
+                    questionNumber: index + 1,
+                    type: this.getType(field.__config__.jnpfKey),
+                    required: field.__config__.required,
+                    title: field.title,
+                    description: field.describe,
+                }
+                if (field.options) {
+                    obj.questionnaireOption = field.options
+
+                    obj.questionnaireOption.forEach((item, index) => {
+                        item.optionContent = item.fullName
+                        item.ordination = index + 1
+                        item.code = item.id
+                        delete item.fullName
+                        delete item.id
+                    })
+
+                }
+                params.questionnaireTopic.push(obj)
+            })
+
+
+            if (this.questionnaireId) params.id = this.questionnaireId
+
+
+            addBaseQUestionnaire(params).then(res => {
+                if (res.code == 200) {
+                    this.$message({
+                        message: '问卷保存成功',
+                        type: 'success',
+                        duration: 1000,
+                        onClose: () => {
+                            this.closeDialog()
+                        }
+                    })
+                } else {
+                    this.$message({
+                        message: res.msg,
+                        type: 'error',
+                        duration: 1000
+                    })
+                }
+            })
+
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.main {
+    background-color: #f0f2f5;
+    padding: 0;
+    min-height: 100vh;
+    .top {
+        background-color: #fff;
+        width: 100%;
+        height: 7%;
+        padding: 20px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        .top-title {
+            margin-left: 0px;
+            display: flex;
+            font-size: 18px;
+            align-items: center;
+            div {
+                margin: 0 10px;
+            }
+        }
+    }
+    .content {
+        padding: 20px;
+        width: 100%;
+        height: 93%;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+    }
+}
+</style>

+ 200 - 0
src/views/governmentCloud/questionnaireInvestigation/IssuedForm.vue

@@ -0,0 +1,200 @@
+<template>
+    <el-dialog :title="'下发'" :close-on-click-modal="false" append-to-body :visible.sync="visible"
+        class="JNPF-dialog JNPF-dialog_center" lock-scroll width="1000px">
+        <el-row :gutter="15" class="">
+            <el-form ref="formRef" :model="dataForm" :rules="dataRule" size="small"
+                label-width="140px" label-position="right">
+                <template v-if="!loading">
+                    <!-- 具体表单 -->
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="是否匿名" align="left" prop="anonymity">
+                            <el-switch v-model="dataForm.anonymity"></el-switch>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="是否延迟下发" align="left" prop="delay">
+                            <el-switch v-model="delay"></el-switch>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24" v-if="delay">
+                        <jnpf-form-tip-item label="开始时间" align="left" prop="startTime">
+                            <el-date-picker v-model="dataForm.startTime" type="datetime"
+                                placeholder="选择开始时间" default-time="12:00:00"
+                                value-format="timestamp">
+                            </el-date-picker>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="结束时间" align="left" prop="endTime">
+                            <el-date-picker v-model="dataForm.endTime" type="datetime"
+                                placeholder="选择结束时间" default-time="12:00:00"
+                                value-format="timestamp">
+                            </el-date-picker>
+                        </jnpf-form-tip-item>
+                    </el-col>
+                    <el-col :span="24">
+                        <jnpf-form-tip-item label="人员" align="left" prop="userIds">
+                            <JnpfUserSelect v-model="dataForm.userIds" class="item"
+                                placeholder="请选择人员" clearable :ableIds="ableIds" :multiple="true" />
+                        </jnpf-form-tip-item>
+                    </el-col>
+
+                    <!-- 表单结束 -->
+                </template>
+            </el-form>
+
+        </el-row>
+        <span slot="footer" class="dialog-footer">
+            <el-button @click="visible = false"> 取 消</el-button>
+            <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading"> 确
+                定</el-button>
+        </span>
+    </el-dialog>
+</template>
+
+
+<script>
+import request from '@/utils/request'
+import { mapGetters } from "vuex";
+import {
+    IssuedQuestionnaire
+} from "@/api/governmentCloud/questionnaireInvestigation/questionnaireInvestigation";
+
+
+export default {
+    components: {},
+    props: [],
+    data() {
+        return {
+            continueBtnLoading: false,
+            index: 0,
+            visible: false,
+            loading: false,
+            btnLoading: false,
+            dataValueAll: {},
+            delay: false,
+            ableIds: [],
+            dataForm: {
+                anonymity: true,
+                startTime: '',
+                endTime: '',
+                userIds: [],
+                questionnaireId: null
+            },
+
+            dataRule:
+            {
+                anonymity: [
+                    {
+                        required: true,
+                        message: '请选择是否匿名',
+                        trigger: 'blur'
+                    },
+                ],
+                startTime: [
+                    {
+                        required: true,
+                        message: '请选择开始时间',
+                        trigger: 'change'
+                    },
+                ],
+                endTime: [
+                    {
+                        required: true,
+                        message: '请选择结束时间',
+                        trigger: 'change'
+                    },
+                ],
+                userIds: [
+                    {
+                        required: true,
+                        message: '请选择人员',
+                        trigger: 'change'
+                    },
+                ]
+            },
+            labelOptions: [],
+            treeSelectProps: {
+                value: 'id',             // ID字段名
+                label: 'name',       // 显示名称
+                children: 'children',    // 子级字段名
+            }
+        }
+    },
+    computed: {
+        ...mapGetters(['userInfo']),
+
+
+    },
+    watch: {},
+    created() {
+        this.dataAll()
+        this.dataValueAll = JSON.parse(JSON.stringify(this.dataForm))
+    },
+    mounted() { },
+    methods: {
+        dataAll() {
+        },
+        clearData() {
+            this.dataForm = JSON.parse(JSON.stringify(this.dataValueAll))
+        },
+        init(id) {
+            this.loading = true
+            this.clearData()
+            if (id) {
+                this.dataForm.questionnaireId = id
+            }
+            this.loading = false
+            this.visible = true;
+        },
+
+        // 表单提交
+        dataFormSubmit() {
+            this.$refs['formRef'].validate((valid) => {
+                if (valid) {
+                    this.request()
+                }
+            })
+        },
+        request() {
+            let _data = this.dataList()
+
+            if (!this.delay) {
+                delete _data.startTime
+            }
+            this.btnLoading = true
+            IssuedQuestionnaire(_data).then((res) => {
+                this.$message({
+                    message: '下发成功',
+                    type: 'success',
+                    duration: 1000,
+                    onClose: () => {
+                        this.visible = false
+                        this.btnLoading = false
+                        this.clearData()
+                        this.$emit('close')
+
+                    }
+                })
+            }).catch(() => {
+                this.btnLoading = false
+                this.continueBtnLoading = false
+            })
+
+        },
+        dataList() {
+            var _data = JSON.parse(JSON.stringify(this.dataForm));
+            return _data;
+        },
+        dataInfo(dataAll) {
+            this.dataForm.content = dataAll.content
+            this.dataForm.label = JSON.parse(dataAll.label)
+            this.dataForm.knowledgeBaseId = dataAll.knowledgeBaseId
+            this.dataForm.title = dataAll.title
+            this.isEdit = true
+            this.dataAll()
+        },
+    },
+}
+
+</script>

+ 211 - 0
src/views/governmentCloud/questionnaireInvestigation/Statistics.vue

@@ -0,0 +1,211 @@
+<template>
+    <el-dialog :visible.sync="visible" fullscreen lock-scroll class="JNPF-full-dialog"
+        :show-close="false" :modal="false">
+
+        <div class="JNPF-common-layout">
+            <div class="JNPF-common-layout-center">
+                <div class="top">
+                    <div class="top-title" v-if="visible">
+                        <div style="opacity: 0.5;">问卷统计</div> /
+                        <div style="font-size: 20px;font-weight: bold;margin-right: 30px;">
+                            {{questionnaire.name}}
+                        </div>
+                        <el-tag style="margin-right: 20px;">匿名</el-tag>
+                        <div>
+                            <span>下发数量:</span>
+                            <span>100</span>
+                        </div>
+                        <div>
+                            <span>收回数量:</span>
+                            <span>100</span>
+                        </div>
+                        <div>
+                            <span>结束倒计时:</span>
+                            <span> {{ countSecond }}</span>
+                        </div>
+                    </div>
+                    <div>
+                        <el-button @click="closeDialog">
+                            返回
+                        </el-button>
+                    </div>
+                </div>
+                <div class="JNPF-common-layout-main JNPF-flex-main">
+                    <JNPF-table v-loading="listLoading" :data="tableData">
+                        <el-table-column prop="num" label="题号" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column prop="title" label="题目" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column prop="" label="题目类型" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column prop="" label="选项" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column prop="" label="各选项数量" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column prop="" label="百分比" align="left" show-overflow-tooltip>
+                        </el-table-column>
+                        <el-table-column label="操作" fixed="right" width="150">
+                            <template slot-scope="scope">
+                                <el-button type="text" @click="openChart(scope.row)">预览
+                                </el-button>
+                                <el-button type="text" @click="openStatistics(scope.row.id)">详情
+                                </el-button>
+                            </template>
+                        </el-table-column>
+                    </JNPF-table>
+                    <pagination :total="total" :page.sync="query.currentPage"
+                        :limit.sync="query.pageSize" @pagination="getList" />
+                </div>
+            </div>
+        </div>
+        <div class="dialogChart">
+            <el-dialog title="收货地址" :visible.sync="dialogChartVisible" :modal="false">
+                <div class="" style="height: 300px"></div>
+            </el-dialog>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+
+import mixin from '@/mixins/generator/form'
+import dayjs from 'dayjs'
+
+import {
+    getSuggestionsList
+} from "@/api/governmentCloud/suggestions/suggestions";
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+
+export default {
+    mixins: [mixin],
+    components: {},
+    data() {
+        return {
+            visible: false,
+            showContent: false,
+            questionnaire: {
+                name: '默认问卷'
+            },
+            countSecond: null,
+            endDate: 1725089581000,
+            listLoading: false,
+            tableData: [{
+                title: '123',
+                num: 1
+            }],
+            total: 0,
+            query: {
+                currentPage: 1,
+                pageSize: 20,
+                dataType: "0"
+            },
+            dialogChartVisible: false
+        }
+    },
+    async created() {
+
+
+    },
+    methods: {
+        getDate(time) {
+            let str = time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '无数据'
+            return str
+        },
+        // 倒计时
+        countTime() {
+            let now = new Date().getTime(); // 获取当前时间
+            let endDate = new Date(this.endDate); // 设置截止时间
+            let end = Number(endDate);
+            //let endDate = new Date(this.assistActivityEndTime); // this.assistActivityEndTime需要倒计时的日期
+            let leftTime = end - now; // 倒计时时间差
+            // 定义变量 d,h,m,s保存倒计时的时间
+            if (leftTime >= 0) {
+                // 天
+                this.day = Math.floor(leftTime / 1000 / 60 / 60 / 24);
+                // let dayh = this.day * 24
+                // 时
+                let h = Math.floor((leftTime / 1000 / 60 / 60) % 24);
+                h = h;
+                this.hour = h < 10 ? "0" + h : h;
+                // 分
+                let m = Math.floor((leftTime / 1000 / 60) % 60);
+                this.min = m < 10 ? "0" + m : m;
+                // 秒
+                let s = Math.floor((leftTime / 1000) % 60);
+                this.second = s < 10 ? "0" + s : s;
+                this.countSecond = this.day + '天' + this.hour + '时' + this.min + '分' + this.second + '秒'
+            } else {
+                this.day = 0;
+                this.hour = "00";
+                this.min = "00";
+                this.second = "00";
+                this.countSecond = this.hour + ':' + this.min + ':' + this.second
+
+            }
+            // 等于0的时候不调用
+            if (
+                Number(this.day) === 0 &&
+                Number(this.hour) === 0 &&
+                Number(this.min) === 0 &&
+                Number(this.second) === 0
+            ) {
+                this.$route.query.count = 4;
+                // 执行飞行任务
+
+                return;
+            } else {
+                // 递归每秒调用countTime方法,显示动态时间效果,
+                setTimeout(this.countTime, 1000);
+            }
+        },
+        async init(id) {
+            this.visible = true
+            this.getList()
+            this.countTime()
+        },
+        //返回列表页面
+        closeDialog() {
+            this.$emit("close");
+        },
+        getList() {
+            this.listLoading = true;
+            setTimeout(() => {
+                this.listLoading = false
+            }, 1000)
+        },
+        openChart() {
+            this.dialogChartVisible = true
+        }
+    },
+}
+</script>
+
+<style lang="scss" scoped>
+.JNPF-common-layout {
+    padding: 20px;
+    background-color: #fff;
+}
+.top {
+    background-color: #fff;
+    width: 100%;
+    margin-bottom: 20px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    .top-title {
+        margin-left: 0px;
+        display: flex;
+        font-size: 18px;
+        align-items: center;
+        div {
+            margin: 0 10px;
+        }
+    }
+}
+.dialogChart {
+    ::v-deep .el-dialog__header {
+        padding: 20px 20px 10px !important;
+    }
+}
+</style>

+ 47 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Amount/index.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="JNPFAmount">
+    <el-input-number v-model="innerValue" v-bind="$attrs" v-on="$listeners"></el-input-number>
+    <div class="explain-text" v-if="showChinese">大写:{{chinese}}</div>
+  </div>
+</template>
+<script>
+import { getAmountChinese } from '@/components/Generator/utils'
+export default {
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  props: ["value", "showChinese"],
+  name: 'JNPFAmount',
+  data() {
+    return {
+      innerValue: this.value
+    }
+  },
+  computed: {
+    chinese() {
+      return this.showChinese ? getAmountChinese(this.innerValue) : ''
+    }
+  },
+  watch: {
+    innerValue(val) {
+      val && this.$emit('change', +val.toFixed(2))
+    },
+    value(val) {
+      this.innerValue = val
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.explain-text {
+  font-size: 12px;
+  color: #aaa;
+  line-height: 20px;
+}
+.JNPFAmount {
+  .el-input-number {
+    width: 100%;
+  }
+}
+</style>

+ 219 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/BillRule/index.vue

@@ -0,0 +1,219 @@
+<template>
+  <div class="popupSelect-container">
+    <div @click="openDialog">
+      <el-input placeholder="请选择模板" readonly :validate-event="false" v-model="title"
+        @mouseenter.native="inputHovering = true" @mouseleave.native="inputHovering = false">
+        <template slot="suffix">
+          <i v-show="!showClose"
+            :class="['el-select__caret', 'el-input__icon', 'el-icon-arrow-up']"></i>
+          <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close"
+            @click.stop="clear"></i>
+        </template>
+        <el-button @click.stop="addOrUpdateHandle()" slot="append">
+          添加</el-button>
+      </el-input>
+    </div>
+    <el-dialog title="模板" :close-on-click-modal="false" :visible.sync="visible"
+      class="JNPF-dialog JNPF-dialog_center JNPF-dialog-tree-select" lock-scroll append-to-body
+      width="1000px">
+      <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-left">
+          <el-scrollbar class="JNPF-common-el-tree-scrollbar" v-loading="treeLoading">
+            <el-tree ref="treeBox" :data="treeData" :props="defaultProps" default-expand-all
+              highlight-current :expand-on-click-node="false" node-key="id"
+              @node-click="handleNodeClick" class="JNPF-common-el-tree" />
+          </el-scrollbar>
+        </div>
+        <div class="JNPF-common-layout-center">
+          <el-row class="JNPF-common-search-box" :gutter="16">
+            <el-form @submit.native.prevent>
+              <el-col :span="8">
+                <el-form-item label="关键词">
+                  <el-input v-model="keyword" placeholder="请输入关键词查询" clearable
+                    @keyup.enter.native="search()" class="search-input" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item>
+                  <el-button type="primary" icon="el-icon-search" @click="search()">查询</el-button>
+                  <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button>
+                </el-form-item>
+              </el-col>
+            </el-form>
+            <div class="JNPF-common-search-box-right">
+              <el-tooltip effect="dark" content="刷新" placement="top">
+                <el-link icon="icon-ym icon-ym-Refresh JNPF-common-head-icon" :underline="false"
+                  @click="initData()" />
+              </el-tooltip>
+            </div>
+          </el-row>
+          <div class="JNPF-common-layout-main JNPF-flex-main">
+            <JNPF-table v-loading="listLoading" :data="list" :border="false" @row-click="rowClick"
+              :hasNO="false">
+              <el-table-column width="35">
+                <template slot-scope="scope">
+                  <el-radio :label="scope.row.enCode" v-model="checked">&nbsp;</el-radio>
+                </template>
+              </el-table-column>
+              <el-table-column type="index" width="50" label="序号" align="center" />
+              <el-table-column prop="fullName" label="业务名称" />
+              <el-table-column prop="enCode" label="业务编码" />
+              <el-table-column prop="startNumber" label="流水起始" />
+            </JNPF-table>
+            <pagination :total="total" :page.sync="listQuery.currentPage"
+              :limit.sync="listQuery.pageSize" @pagination="initData" />
+          </div>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
+        <el-button type="primary" @click="select()">{{$t('common.confirmButton')}}</el-button>
+      </span>
+    </el-dialog>
+    <Form ref="Form" @refreshDataList="initData" />
+  </div>
+</template>
+
+<script>
+import {
+  getBillRuleSelector
+} from '@/api/system/billRule'
+import Form from '@/views/system/billRule/Form.vue'
+export default {
+  components: {
+    Form
+  },
+  props: {
+    value: {
+      default: ''
+    },
+    title: {
+      default: ''
+    },
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    }
+  },
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  data() {
+    return {
+      list: [],
+      innerValue: '',
+      listQuery: {
+        currentPage: 1,
+        pageSize: 20,
+        sort: 'desc',
+        sidx: ''
+      },
+      keyword: '',
+      total: 0,
+      checked: '',
+      checkedRow: {},
+      listLoading: false,
+      defaultProps: {
+        id: 'id',
+        label: 'fullName'
+      },
+      query: {
+        categoryId: '',
+        keyword: '',
+      },
+      treeLoading: false,
+      treeData: [],
+      inputHovering: false,
+      visible: false,
+      tableName: '',
+      categoryId: '',
+      categoryList: [],
+      billRuleFormVisible: false,
+    }
+  },
+  computed: {
+    showClose() {
+      let hasValue = this.value !== undefined && this.value !== null && this.value !== '';
+      let criteria = this.clearable &&
+        !this.disabled &&
+        this.inputHovering &&
+        hasValue;
+      return criteria;
+    }
+  },
+  created() {
+    this.$store.dispatch('base/getDictionaryData', { sort: 'businessType' }).then((res) => {
+      this.treeData = [{ id: '', fullName: '业务分类', children: res }]
+      this.categoryId = this.treeData[0].id
+      this.categoryList = res
+      this.reset()
+    })
+  },
+  methods: {
+    initData() {
+      this.listLoading = true
+      let query = {
+        keyword: this.keyword,
+        ...this.listQuery,
+        categoryId: this.categoryId
+      }
+      getBillRuleSelector(query).then(res => {
+        this.list = res.data.list
+        this.total = res.data.pagination.total
+        this.listLoading = false
+      }).catch(() => { this.listLoading = false })
+    },
+    handleNodeClick(data) {
+      this.categoryId = data.id
+      this.reset()
+    },
+    reset() {
+      this.keyword = ''
+      this.search()
+    },
+    search() {
+      this.listQuery.currentPage = 1
+      this.listQuery.pageSize = 20
+      this.listQuery.sort = 'desc'
+      this.initData()
+    },
+    openDialog() {
+      if (this.disabled) return
+      this.checked = this.value
+      this.visible = true
+      this.categoryId = ''
+      this.$nextTick(() => {
+        this.$refs.treeBox.setCurrentKey(null)
+        this.reset()
+      })
+    },
+    clear() {
+      this.checked = ''
+      this.checkedRow = {}
+      this.$emit('input', this.checked)
+      this.$emit('change', this.checked, this.checkedRow)
+    },
+    select() {
+      if (!this.checked) return
+      this.$emit('input', this.checked)
+      this.$emit('change', this.checked, this.checkedRow)
+      this.visible = false
+    },
+    rowClick(row) {
+      this.checked = row.enCode
+      this.checkedRow = row
+    },
+    addOrUpdateHandle(id) {
+      this.billRuleFormVisible = true
+      this.$nextTick(() => {
+        this.$refs.Form.init(id, this.categoryList)
+      })
+    },
+  }
+}
+</script>

+ 141 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/ComplexHeader/index.vue

@@ -0,0 +1,141 @@
+
+<template>
+  <el-dialog title="复杂表头配置" :close-on-click-modal="false" :visible.sync="visible"
+    class="JNPF-dialog JNPF-dialog_center dialog_height" lock-scroll width="1000px" append-to-body>
+    <JNPF-table :data="list" ref="dragTable" :hasNO="false" row-key="id">
+      <el-table-column align="center" label="拖动" width="50">
+        <template>
+          <i class="drag-handler icon-ym icon-ym-darg" style="cursor: move;font-size:20px"
+            title='点击拖动' />
+        </template>
+      </el-table-column>
+      <el-table-column prop="fullName" label="表头列名">
+        <template slot-scope="scope">
+          <el-input v-model="scope.row.fullName" placeholder="请输入名称" />
+        </template>
+      </el-table-column>
+      <el-table-column prop="childColumns" label="子列">
+        <template slot-scope="scope">
+          <el-select v-model="scope.row.childColumns" multiple placeholder="请选择子列" v-if="type==1">
+            <el-option v-for="(item,i) in getColumnOptions(scope.$index)" :key="i"
+              :label="item.fullName" :value="item.id">
+            </el-option>
+          </el-select>
+          <el-select v-model="scope.row.childColumns" multiple placeholder="请选择子列" v-else>
+            <el-option v-for="(item,i) in getColumnOptions(scope.$index)" :key="i"
+              :label="item.__config__.label" :value="item.__vModel__">
+            </el-option>
+          </el-select>
+        </template>
+      </el-table-column>
+      <el-table-column prop="align" label="对齐方式">
+        <template slot-scope="scope">
+          <el-select v-model="scope.row.align" placeholder="请选择" filterable>
+            <el-option v-for="item in alignOptions" :key="item.value" :label="item.fullName"
+              :value="item.value">
+            </el-option>
+          </el-select>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="50">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" class="JNPF-table-delBtn"
+            @click="handleDel(scope.$index)">删除</el-button>
+        </template>
+      </el-table-column>
+    </JNPF-table>
+    <div class="table-actions" @click="addHandle()">
+      <el-button type="text" icon="el-icon-plus">添加</el-button>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="closeDialog(1)">{{$t('common.confirmButton')}}</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import Sortable from 'sortablejs'
+export default {
+  props: ['type'],
+  components: {},
+  data() {
+    return {
+      visible: false,
+      list: [],
+      options: [],
+      alignOptions: [
+        {
+          value: 'left',
+          fullName: '左对齐'
+        }, {
+          value: 'center',
+          fullName: '居中对齐'
+        }, {
+          value: 'right',
+          fullName: '右对齐'
+        }],
+    }
+  },
+  methods: {
+    init(data, option) {
+      this.list = data ? JSON.parse(JSON.stringify(data)) : []
+      this.options = option ? JSON.parse(JSON.stringify(option)) : []
+      this.visible = true
+      this.$nextTick(() => {
+        this.setSort()
+      });
+    },
+    closeDialog() {
+      if (this.type == 1) this.$store.commit("generator/SET_HEADER_DATA", JSON.parse(JSON.stringify(this.list)))
+      this.$emit('closeForm', this.list)
+      this.visible = false
+    },
+    getColumnOptions(index) {
+      let childColumns = []
+      for (let i = 0; i < this.list.length; i++) {
+        const e = this.list[i];
+        for (let j = 0; j < e.childColumns.length; j++) {
+          const element = e.childColumns[j];
+          if (i != index) childColumns.push(element)
+        }
+      }
+      return this.options.filter(o => !childColumns.includes(o.__vModel__))
+    },
+    handleDel(index) {
+      this.list.splice(index, 1);
+    },
+    addHandle() {
+      let id = 'complex' + this.jnpf.idGenerator()
+      let item = { fullName: "表头列名" + id, childColumns: [], align: 'center', id: id }
+      this.list.push(item)
+    },
+    setSort() {
+      const el = this.$refs.dragTable.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
+      this.sortable = Sortable.create(el, {
+        ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
+        setData: function (dataTransfer) {
+          dataTransfer.setData('Text', '')
+        },
+        onEnd: evt => {
+          const targetRow = this.list.splice(evt.oldIndex, 1)[0]
+          this.list.splice(evt.newIndex, 0, targetRow)
+        },
+        handle: '.drag-handler',
+      })
+    },
+  }
+}
+</script>
+<style lang="scss" scoped>
+.JNPF-dialog_center {
+  >>> .el-dialog__body {
+    height: 70vh;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    padding: 0px 3px 2px !important;
+    // padding-bottom: 20px !important;
+  }
+}
+</style>

+ 97 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/DicSelect/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <div class="comSelect-container">
+    <el-row class="jnpf-el-row">
+      <el-col :span="18">
+        <JnpfTreeSelect :options="treeData" v-model="innerValue" :placeholder="placeholder"
+          clearable :disabled="disabled" v-on="$listeners" lastLevel>
+        </JnpfTreeSelect>
+      </el-col>
+      <el-col :span="6">
+        <el-button @click="goDictionary()">
+          添加</el-button>
+      </el-col>
+    </el-row>
+    <div>
+      <el-dialog :visible.sync="dicVisible" append-to-body
+        class="JNPF-dialog JNPF-dialog_center JNPF-dialog-tree-select" lock-scroll width="80%"
+        @close="closeDic()">
+        <dicIndex ref="dicIndex"></dicIndex>
+      </el-dialog>
+    </div>
+  </div>
+</template>
+
+<script>
+import dicIndex from '@/views/systemData/dictionary/index.vue';
+export default {
+  components: {
+    dicIndex
+  },
+  name: 'dicSelect',
+  props: ["value", "disabled", "placeholder"],
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  data() {
+    return {
+      treeData: [],
+      innerValue: this.value,
+      dicVisible: false,
+    }
+  },
+  methods: {
+    async getData() {
+      this.treeData = await this.$store.dispatch('generator/getDicTree')
+    },
+    async getNewData() {
+      this.$store.commit('generator/SET_DIC_TREE', [])
+      this.treeData = await this.$store.dispatch('generator/getDicTree')
+    },
+    closeDic() {
+      this.getNewData()
+    },
+    goDictionary() {
+      this.dicVisible = true
+      this.$nextTick(() => {
+        this.$refs.dicIndex.initData()
+      })
+    },
+  },
+  created() {
+    this.getData()
+  },
+  watch: {
+    innerValue(val) {
+      val && this.$emit('change', val)
+    },
+    value(val) {
+      this.innerValue = val
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.comSelect-container {
+  .el-select {
+    width: 100%;
+  }
+}
+.jnpf-el-row {
+  >>> .el-input__inner {
+    border-radius: 4px 0 0 4px !important;
+  }
+
+  >>> .el-button {
+    border-left: 0;
+    background-color: #f5f7fa;
+    font-size: 13px;
+    color: #909399;
+    border-radius: 0 4px 4px 0;
+    line-height: 12px;
+  }
+  >>> .el-button:hover {
+    border-color: #dcdfe6;
+  }
+}
+</style>

+ 37 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Iframe/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <iframe :style="getStyle" :src="href" scrolling="yes" frameborder="0" />
+</template>
+<script>
+export default {
+  name: 'JnpfIframe',
+  props: {
+    href: {
+      type: String,
+      default: ''
+    },
+    height: {
+      type: Number,
+      default: 300
+    },
+    borderType: {
+      type: String,
+      default: 'solid'
+    },
+    borderColor: {
+      type: String,
+      default: '#E2E0E0'
+    },
+    borderWidth: {
+      type: Number,
+      default: 1
+    },
+  },
+  computed: {
+    getStyle() {
+      let style = { width: '100%', height: (this.height || 300) + 'px', border: (this.borderWidth || 1) + 'px' + ' ' + this.borderType + ' ' + this.borderColor }
+      return style
+    }
+  }
+}
+</script>
+

+ 64 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/JnpfDateRangePicker/index.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-date-picker :type="datetype" v-model="innerValue" placeholder="请选择" value-format="timestamp"
+    :range-separator="separator" :picker-options='pickerOptions' style="width:100%"
+    :start-placeholder="startPlaceholder" :end-placeholder="endPlaceholder"
+    :format="format"></el-date-picker>
+</template>
+<script>
+export default {
+  name: 'JNPF-date-picker',
+  components: {},
+  props: {
+    value: {
+      default: undefined
+    },
+    format: {
+      type: String,
+      default: 'yyyy-MM-dd HH:mm:ss'
+    },
+    separator: {
+      type: String,
+      default: '-'
+    },
+    startTime: {
+      default: undefined
+    },
+    endTime: {
+      default: undefined
+    },
+    startPlaceholder: {
+      type: String,
+      default: '开始日期'
+    },
+    endPlaceholder: {
+      type: String,
+      default: '结束日期'
+    }
+  },
+  data() {
+    return {
+      innerValue: this.value,
+      pickerOptions: {
+        disabledDate: (time) => {
+          if (this.format === 'yyyy') return time.getMonth() + 1 != 1
+        },
+      }
+    }
+  },
+  watch: {
+    innerValue(val) {
+      this.$emit('input', val)
+      this.$emit('change', val)
+    },
+    value(val) {
+      this.innerValue = val
+    },
+  },
+  computed: {
+    datetype() {
+      let type = this.format === 'yyyy' ? 'month' : this.format === 'yyyy-MM' ? 'month' : this.format === 'yyyy-MM-dd' ? 'date' : 'datetime'
+      return type + 'range'
+    }
+  }
+}
+</script>

+ 95 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/JnpfNumber/index.vue

@@ -0,0 +1,95 @@
+<template>
+  <div>
+    <p class="jnpf-detail-text">
+      <span v-if="addonBefore" class="addonBefore">{{ addonBefore }} </span>
+      <span class="innerValue">{{ innerValue }}</span>
+      <span v-if="addonAfter" class="addonAfter">{{ addonAfter }}</span>
+    </p>
+    <p v-if="isAmountChinese" class="input-color">{{ amountChineseName }}</p>
+  </div>
+</template>
+<script>
+import { getAmountChinese, thousandsFormat } from "@/components/Generator/utils/index"
+export default {
+  name: 'JNPF-number',
+  components: {},
+  props: {
+    value: {
+      type: [Number, String],
+      default: undefined
+    },
+    thousands: {
+      type: Boolean,
+      default: false
+    },
+    isAmountChinese: {
+      type: Boolean,
+      default: false
+    },
+    addonBefore: {
+      type: String,
+      default: ''
+    },
+    addonAfter: {
+      type: String,
+      default: ''
+    },
+    precision: {
+      type: Number,
+      default: undefined
+    },
+  },
+  data() {
+    return {
+      innerValue: 0,
+      amountChineseName: ''
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.innerValue = val
+        this.thousandSeparator()
+      },
+      immediate: true
+    }
+  },
+  computed: {},
+  created() { },
+  mounted() {
+  },
+  methods: {
+    thousandSeparator() {
+      if (this.isAmountChinese) this.amountChineseName = getAmountChinese(this.innerValue)
+      if (this.precision) this.innerValue = Number(this.innerValue).toFixed(this.precision)
+      if (this.thousands) {
+        this.innerValue = thousandsFormat(this.innerValue)
+      }
+      return this.innerValue
+    },
+  }
+}
+</script>
+<style lang="scss" scoped>
+.input-number {
+  width: 100%;
+  border-radius: 0px !important;
+  >>> .el-input__inner {
+    border-radius: 0px !important;
+  }
+}
+.addonBefore {
+  padding-right: 8px;
+  color: #303133;
+}
+.addonAfter {
+  padding-left: 8px;
+  color: #303133;
+}
+.innerValue {
+  color: #606266;
+}
+.input-color {
+  color: #c0c0c0;
+}
+</style>

+ 532 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Location/index.vue

@@ -0,0 +1,532 @@
+<template>
+  <div>
+    <el-button icon="icon-ym icon-ym-daka" v-if="!detailed" size="mini"
+      @click="handleLocation()">{{ innerValue.fullAddress ? '重新定位' : '添加定位' }}</el-button>
+    <div class="location-card" v-if="innerValue.fullAddress" @click="handleClick()">
+      <div class="location-card-info">
+        <img class="location-card-static-map" :src="staticMapUrl" v-if="enableLocationScope" />
+        <div class="location-card-address">{{ innerValue.fullAddress }}</div>
+      </div>
+      <div class="el-icon-error" @click.stop="handleClear()"
+        v-if="!detailed && !disabled && clearable" />
+    </div>
+    <el-dialog title="选择位置" :close-on-click-modal="false" append-to-body :visible.sync="visible"
+      @close="handleCancel"
+      class="JNPF-dialog template-dialog JNPF-dialog_center location-container-modal" lock-scroll
+      width="600px" destroy-on-close>
+      <div class="location-contain">
+        <el-input v-model="keyword" placeholder="搜索或直接在地图上点选" @keyup.enter.native="onSearch">
+          <el-button slot="append" @click="onSearch" icon="el-icon-search"></el-button>
+        </el-input>
+        <div class="map-contain">
+          <div :id="containerId" class="map"></div>
+          <img class="map-marker" src="@/assets/images/mark.png" />
+        </div>
+        <div class="around-contain" ref="bodyRef" v-loading="loading && listQuery.currentPage == 1">
+          <el-radio-group v-model="selectValue" @change="onSelectValueChange">
+            <el-radio class="around-contain-item" :label="index" v-for="(item, index) in list"
+              :key="index">
+              <div class="around-item-title" :title="item.name"> {{ item.name }}</div>
+              <div class="around-item-sub-title"
+                :title="item.address.length ? item.address : item.name">
+                {{ item.address.length ? item.address : item.name }}</div>
+            </el-radio>
+          </el-radio-group>
+          <!-- 请选择允许微调范围内的地点 -->
+          <el-empty class="around-contain-empty" v-if="!list.length && !loading"
+            description="暂无数据"></el-empty>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" :loading="loading" @click="handleSubmit()">确定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import AMapLoader from '@amap/amap-jsapi-loader';
+import { getAroundList, getTextList } from '@/api/common'
+import define from '@/utils/define'
+const defaultValue = {
+  lat: 25.416422,
+  lng: 118.990923,
+  name: '',
+  fullAddress: '',
+};
+window._AMapSecurityConfig = {
+  securityJsCode: define.aMapSecurityJsCode, // 安全密钥
+};
+
+export default {
+  name: 'JnpfLocation',
+  props: {
+    value: {
+      type: String,
+      default: ''
+    },
+    autoLocation: {
+      type: Boolean,
+      default: false
+    },
+    enableLocationScope: {
+      type: Boolean,
+      default: false
+    },
+    adjustmentScope: {
+      type: [String, Number],
+      default: 500
+    },
+    enableDesktopLocation: {
+      type: Boolean,
+      default: false
+    },
+    locationScope: {
+      type: Array,
+      default: () => []
+    },
+    detailed: {
+      type: Boolean,
+      default: false
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+  },
+  data() {
+    return {
+      visible: false,
+      keyword: '',
+      location:
+      {
+        lat: '',
+        lng: ''
+      },
+      currentLocation: {
+        lat: '',
+        lng: ''
+      },
+      AMap: '',
+      map: '',
+      list: [],
+      loading: false,
+      finish: false,
+      listQuery: {
+        currentPage: 1,
+        pageSize: 50,
+      },
+      selectValue: -1,
+      innerValue: '',
+      setCenterLoading: false,
+      dragLoading: false,
+      containerId: ""
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.innerValue = val ? JSON.parse(val) : defaultValue;
+      },
+      immediate: true
+    },
+  },
+  computed: {
+    staticMapUrl() {
+      if (!this.enableLocationScope) return ' ';
+      const location = this.innerValue.lng + ',' + this.innerValue.lat;
+      const url = `${this.define.APIURl}/api/system/Location/staticmap?location=${location}&zoom=19&size=80*80&key=${this.define.aMapWebKey}`;
+      return url;
+    }
+  },
+  methods: {
+    handleClick() {
+      if (this.detailed) return this.openAMap();
+      if (this.disabled) return;
+      if (this.enableLocationScope) this.handleLocation();
+    },
+    openAMap() {
+      const position = this.innerValue.lng + ',' + this.innerValue.lat;
+      const url = `http://uri.amap.com/marker?position=${position}&name=${this.innerValue.name}&coordinate=gaode&callnative=0`;
+      window.open(url, '_blank');
+    },
+    handleLocation() {
+      if (this.disabled) return;
+      this.visible = true;
+      this.selectValue = -1;
+      this.list = [];
+      this.keyword = '';
+      this.$nextTick(() => {
+        this.initMap();
+      });
+    },
+    async initMap() {
+      this.containerId = 'container' + this.jnpf.idGenerator()
+      AMapLoader.reset()
+      await AMapLoader.load({
+        key: this.define.aMapJsKey,
+        version: '2.0',
+        plugins: ['AMap.PlaceSearch', 'AMap.Geolocation'],
+      }).then(AMap => {
+        this.AMap = AMap;
+        const query = {
+          viewMode: '3D',
+          resizeEnable: true,
+          zoom: 16,
+        };
+        if (this.innerValue && this.innerValue.lng && this.innerValue.lat) {
+          this.location = { lat: this.innerValue.lat, lng: this.innerValue.lng };
+          query.center = [this.location.lng, this.location.lat];
+        }
+        this.map = new AMap.Map(this.containerId, query);
+
+        //获取中心位置
+        this.handleGetCenter();
+        //查询附近地点
+        this.getList();
+        this.bindScroll();
+        //添加可选区域圆形
+        this.handelCircle();
+        //添加微调区域圆形
+        this.handleScopeCircle();
+        //地图事件
+        this.handleListener();
+        //添加定位按钮
+        this.map.addControl(new AMap.Geolocation());
+      });
+    },
+    onSelectValueChange() {
+      this.setCenterLoading = true;
+      this.$nextTick(() => {
+        const location = this.list[this.selectValue].location || '';
+        const [lng, lat] = location.split(',');
+        this.location = { lat, lng };
+        this.map.setCenter([lng, lat]);
+        setTimeout(() => {
+          this.setCenterLoading = false;
+        }, 500);
+      });
+    },
+    handleCenter(e) {
+      this.map.setCenter(e.lnglat);
+    },
+    handleListener() {
+      this.map.on('click', this.handleCenter);
+      this.map.on('moveend', this.handleMapChange);
+      this.map.on('movestart', this.handleSetLoading);
+    },
+    handleOffListener() {
+      this.map.off('click', this.handleCenter);
+      this.map.off('moveend', this.handleMapChange);
+      this.map.off('movestart', this.handleSetLoading);
+    },
+    handleMapChange() {
+      if (this.dragLoading) return (this.dragLoading = false);
+      this.handleGetCenter();
+      this.selectValue = -1;
+      this.listQuery.currentPage = 1;
+      this.list = [];
+      if (this.enableLocationScope && this.adjustmentScope) {
+        const discount = this.getDiscount(this.currentLocation.lat, this.currentLocation.lng, this.location.lat, this.location.lng) || 0;
+        if (discount > this.adjustmentScope) return this.$message.warning('超出微调范围');
+      }
+      this.getList();
+    },
+    handleSetLoading() {
+      if (this.setCenterLoading) this.dragLoading = true;
+    },
+    handleGetCenter() {
+      const { lat, lng } = this.map.getCenter();
+      this.location = { lat, lng };
+    },
+    handleResult(res) {
+      if (res.pois.length < this.listQuery.pageSize) this.finish = true;
+      this.list = [...this.list, ...res.pois];
+      this.loading = false;
+    },
+    bindScroll() {
+      let _this = this,
+        vBody = _this.$refs.bodyRef;
+      vBody.addEventListener("scroll", function () {
+        if (vBody.scrollHeight - vBody.clientHeight - vBody.scrollTop <= 50 && !_this.loading && !_this.finish) {
+          _this.listQuery.currentPage += 1;
+          _this.keyword ? _this.handleSearch() : _this.getList();
+        }
+      });
+    },
+    getList() {
+      this.loading = true;
+      const query = {
+        key: this.define.aMapWebKey,
+        location: this.location.lng + ',' + this.location.lat,
+        radius: -1,
+        offset: this.listQuery.pageSize,
+        page: this.listQuery.currentPage,
+      };
+      getAroundList(query).then(res => {
+        this.handleResult(res.data);
+      }).catch(() => { this.loading = false })
+    },
+    handelCircle() {
+      if (!this.enableDesktopLocation || !this.locationScope.length) return;
+      for (let i = 0; i < this.locationScope.length; i++) {
+        const o = this.locationScope[i];
+        if (!o.lng || !o.lat || !o.radius) continue;
+        this.addCircle({ ...o, fillColor: '#7ad98f' });
+      }
+    },
+    handleScopeCircle() {
+      if (!this.enableLocationScope || !this.adjustmentScope) return;
+      this.currentLocation = this.location;
+      this.addCircle({ ...this.location, radius: this.adjustmentScope, fillColor: '#1791fc' });
+    },
+    addCircle(o) {
+      var circle = new this.AMap.Circle({
+        center: [o.lng, o.lat],
+        radius: o.radius,
+        strokeOpacity: 0.2,
+        fillOpacity: 0.4,
+        fillColor: o.fillColor,
+        bubble: false,
+      });
+      circle.on('click', this.handleCenter);
+      this.map.add(circle);
+    },
+    handleSearch() {
+      this.loading = true;
+      const query = {
+        key: this.define.aMapWebKey,
+        keywords: this.keyword,
+        radius: this.enableLocationScope && this.adjustmentScope ? this.adjustmentScope : -1,
+        offset: this.listQuery.pageSize,
+        page: this.listQuery.currentPage,
+      };
+      getTextList(query).then(res => {
+        this.handleResult(res.data);
+      });
+    },
+    onSearch() {
+      this.selectValue = -1;
+      this.listQuery.currentPage = 1;
+      this.list = [];
+      this.keyword ? this.handleSearch() : this.getList();
+    },
+    getDiscount(lat1, lng1, lat2, lng2) {
+      const p1 = new this.AMap.LngLat(lng1, lat1);
+      const p2 = new this.AMap.LngLat(lng2, lat2);
+      return p1.distance(p2) || 0;
+    },
+    handleAutoLocation() {
+      if (!this.autoLocation || this.innerValue.fullAddress) return;
+      AMapLoader.load({
+        key: this.define.aMapJsKey,
+        version: '2.0',
+        plugins: ['AMap.Geolocation', 'AMap.Geocoder'],
+      }).then(AMap => {
+        this.AMap = AMap;
+        var geolocation = new AMap.Geolocation({
+          enableHighAccuracy: true,
+          timeout: 1500,
+        });
+        let that = this
+        const getAddress = position => {
+          var geocoder = new AMap.Geocoder();
+          geocoder.getAddress(position, function (status, res) {
+            if (status === 'complete' && res.info === 'OK') {
+              const data = res.regeocode.addressComponent;
+              that.innerValue = {
+                pName: data.province,
+                cName: data.city,
+                adName: data.district,
+                address: data.street + data.streetNumber,
+                name: '',
+                lng: position[0],
+                lat: position[1],
+                fullAddress: res.regeocode.formattedAddress,
+              };
+
+              that.$emit('input', JSON.stringify(that.innerValue));
+              that.$emit('change', JSON.stringify(that.innerValue));
+            }
+          });
+        };
+        geolocation.getCityInfo((_status, res) => {
+          if (res.status == 0) {
+            if (this.enableDesktopLocation && this.locationScope.length) {
+              let list = [];
+              for (let i = 0; i < this.locationScope.length; i++) {
+                const o = this.locationScope[i];
+                const discount = this.getDiscount(o.lat, o.lng, res.position[1], res.position[0]) || 0;
+                list.push(discount > o.radius);
+              }
+              if (list.every(o => o === true)) return;
+            }
+            getAddress(res.position);
+          } else {
+            console.error('定位失败');
+          }
+        });
+      });
+    },
+    handleSubmit() {
+      if ((!this.selectValue && this.selectValue != 0) || this.selectValue == -1) return this.$message.error('请选择地址');
+      const data = this.list[this.selectValue];
+      const [lng, lat] = data.location.split(',');
+      //判断微调范围
+      if (this.enableLocationScope) {
+        const discount = this.getDiscount(this.currentLocation.lat, this.currentLocation.lng, lat, lng) || 0;
+        if (discount > (this.adjustmentScope || 500)) return this.$message.warning('超出微调范围');
+      }
+      //判断可选范围
+      if (this.enableDesktopLocation && this.locationScope.length) {
+        let list = [];
+        for (let i = 0; i < this.locationScope.length; i++) {
+          const o = this.locationScope[i];
+          const discount = this.getDiscount(o.lat, o.lng, lat, lng) || 0;
+          list.push(discount > o.radius);
+        }
+        if (list.every(o => o === true)) return this.$message.warning('超出规定范围');
+      }
+      const address = data.address && data.address.length ? data.address : '';
+      //台湾、北京、上海、重庆、深圳地址特殊处理
+      let fullAddress = data.pname + data.cityname + data.adname + address + data.name;
+      if (data.pname == data.cityname) fullAddress = data.pname + data.adname + address + data.name;
+      if (data.pname == data.cityname && data.pname == data.adname) fullAddress = data.pname + address + data.name;
+      this.innerValue = {
+        pName: data.pname,
+        cName: data.cityname,
+        adName: data.adname,
+        address,
+        name: data.name,
+        lng,
+        lat,
+        fullAddress,
+      };
+      this.$emit('input', JSON.stringify(this.innerValue));
+      this.$emit('change', JSON.stringify(this.innerValue));
+      this.handleCancel();
+    },
+    handleCancel() {
+      this.visible = false;
+      this.destroyMap()
+    },
+    destroyMap() {
+      this.handleOffListener();
+      this.$nextTick(() => {
+        this.map && this.map.destroy();
+      })
+    },
+    handleClear() {
+      this.innerValue = defaultValue;
+      this.$emit('input', '');
+      this.$emit('change', '');
+    }
+  }
+}
+
+</script>
+
+<style lang="scss" scoped>
+.location-container-modal {
+  >>> .el-dialog {
+    .el-dialog__body {
+      height: 62vh !important;
+      overflow: hidden;
+    }
+    .location-contain {
+      display: flex;
+      flex-direction: column;
+      height: 100%;
+      padding: 0px 20px 0 20px;
+
+      .map-contain {
+        position: relative;
+        .map {
+          margin: 10px 0;
+          width: 100%;
+          height: 230px;
+        }
+        .map-marker {
+          width: 19px;
+          height: 32px;
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          transform: translate(-50%, calc(-50% - 9.5px));
+        }
+      }
+
+      .around-contain {
+        width: 100%;
+        height: 100%;
+        overflow: auto;
+        .el-radio-group {
+          width: 100% !important;
+        }
+        .around-contain-item {
+          width: 100%;
+          padding: 8px;
+          border-bottom: 1px solid #f2f2f6;
+          display: flex;
+          height: 60px;
+          line-height: 22px;
+          align-items: center;
+          width: 100%;
+          .around-item-title {
+            color: #171a1d;
+            width: 512px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+          .around-item-sub-title {
+            color: #b9babb;
+            width: 512px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+        }
+        .around-contain-empty {
+          height: 90%;
+        }
+      }
+    }
+  }
+}
+.location-card {
+  display: flex;
+  align-items: center;
+  margin-top: 8px;
+  background: #f2f2f6;
+  padding: 12px;
+  border-radius: 8px;
+  justify-content: space-between;
+  cursor: pointer;
+  .location-card-info {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    .location-card-static-map {
+      width: 48px;
+      height: 48px;
+      margin-right: 4px;
+    }
+    .location-card-address {
+      line-height: 1.5;
+      padding: 0 4px;
+      word-break: normal;
+      white-space: normal;
+    }
+  }
+  .location-card-actions {
+    color: rgb(135, 143, 149);
+    cursor: pointer;
+    flex-shrink: 0;
+  }
+}
+</style>

+ 193 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/MaskConfigDialog/index.vue

@@ -0,0 +1,193 @@
+
+<template>
+  <el-dialog title="掩码设置" :close-on-click-modal="false" :visible.sync="visible"
+    class="JNPF-dialog JNPF-dialog_center mask-config-dialog" lock-scroll width="600px"
+    append-to-body>
+    <el-form :model="formData" :rules="rules" ref="formRef" class="mt-20" label-width="120px"
+      @submit.native.prevent>
+      <jnpf-form-tip-item label="填充符号" prop="filler">
+        <jnpf-select v-model="formData.filler" placeholder="请选择填充符号" :options="fillerOptions"
+          :props='props' :clearable='false' />
+      </jnpf-form-tip-item>
+      <jnpf-form-tip-item label="掩码规则" prop="maskType">
+        <el-select v-model="formData.maskType" placeholder="请选择掩码规则" popper-class="select-down"
+          @change="onMaskTypeChange($event)">
+          <el-option v-for="item in maskTypeOptions" :key="item.id" :label="item.fullName"
+            :value="item.id">
+          </el-option>
+        </el-select>
+      </jnpf-form-tip-item>
+      <template v-if="formData.maskType == 0">
+        <jnpf-form-tip-item label="开头显示" name="prefixType">
+          <jnpf-select v-model="formData.prefixType" placeholder="请选择开头显示"
+            :options="prefixTypeOptions" :props='props' :clearable='false' />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="字数" name="prefixLimit" v-if="formData.prefixType === 2">
+          <el-input-number v-model="formData.prefixLimit" placeholder="请输入" :min="0" :precision="0"
+            @change="onNumberChange($event, 'prefixLimit')" controls-position="right" />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="字符" name="prefixSpecifyChar"
+          v-if="formData.prefixType === 3 || formData.prefixType === 4">
+          <el-input v-model="formData.prefixSpecifyChar" placeholder="请输入字符" />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="结尾显示" name="suffixType">
+          <jnpf-select v-model="formData.suffixType" placeholder="请选择结尾显示"
+            :options="suffixTypeOptions" :props='props' :clearable='false' />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="字数" name="suffixLimit" v-if="formData.suffixType === 2">
+          <el-input-number v-model="formData.suffixLimit" placeholder="请输入" :min="0" :precision="0"
+            @change="onNumberChange($event, 'suffixLimit')" controls-position="right" />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="字符" name="suffixSpecifyChar"
+          v-if="formData.suffixType === 3 || formData.suffixType === 4">
+          <el-input v-model="formData.suffixSpecifyChar" placeholder="请输入字符" />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="显示字符" name="ignoreChar">
+          <el-input v-model="formData.ignoreChar" placeholder="多个字符之间用英文,号隔开,如:a,b,c" clearable />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item name="useUnrealMask" label="虚拟掩码"
+          tip-label="禁用:按照真实字数显示掩码;</br>启用:按照虚拟位数显示掩码,不暴露真实长度">
+          <el-switch v-model="formData.useUnrealMask" />
+        </jnpf-form-tip-item>
+        <jnpf-form-tip-item label="掩码长度" name="unrealMaskLength" v-if="formData.useUnrealMask">
+          <el-input-number v-model="formData.unrealMaskLength" placeholder="请输入" :min="1"
+            controls-position="right" :precision="0" addonAfter="个字"
+            @change="onNumberChange($event, 'unrealMaskLength')" />
+        </jnpf-form-tip-item>
+      </template>
+      <el-divider></el-divider>
+      <jnpf-form-tip-item label="效果测试" name="testText">
+        <el-input v-model="testText" placeholder="请输入" clearable :disabled="isMask">
+          <template slot="append">
+            <p class="cursor-pointer" @click="handleMask()" v-show="!isMask">掩盖</p>
+            <p class="cursor-pointer" @click="handleRevert()" v-show="isMask">解码</p>
+          </template>
+        </el-input>
+      </jnpf-form-tip-item>
+    </el-form>
+
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="handleSubmit">{{$t('common.confirmButton')}}</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { useTextMask, defaultMaskOptions } from '@/components/Generator/utils/useTextMask';
+const fillerOptions = [
+  { id: '*', fullName: '*' },
+  { id: '●', fullName: '●' },
+  { id: '-', fullName: '-' },
+  { id: '.', fullName: '.' },
+  { id: '#', fullName: '#' },
+]
+const maskTypeOptions = [
+  { id: 1, fullName: '全掩盖' },
+  { id: 2, fullName: '姓名-显示前1个字,后1个字' },
+  { id: 3, fullName: '手机号-显示前3位,后4位' },
+  { id: 4, fullName: '邮箱-显示前3个字,@和之后的字' },
+  { id: 5, fullName: '身份证-显示前6位,后3位,虚拟为4位' },
+  { id: 6, fullName: 'IP地址-显示第1段IP' },
+  { id: 7, fullName: '车牌号-显示前1个字,后2位' },
+  { id: 8, fullName: '银行卡号-显示前6位,后4位' },
+  { id: 0, fullName: '自定义规则' },
+];
+const prefixTypeOptions = [
+  { id: 1, fullName: '不显示' },
+  { id: 2, fullName: '指定字数' },
+  { id: 3, fullName: '指定字符之前的字' },
+  { id: 4, fullName: '指定字符和之前的字' },
+];
+const suffixTypeOptions = [
+  { id: 1, fullName: '不显示' },
+  { id: 2, fullName: '指定字数' },
+  { id: 3, fullName: '指定字符之后的字' },
+  { id: 4, fullName: '指定字符和之后的字' },
+];
+export default {
+  props: ['type'],
+  components: {},
+  data() {
+    return {
+      visible: false,
+      fillerOptions,
+      maskTypeOptions,
+      prefixTypeOptions,
+      suffixTypeOptions,
+      formData: defaultMaskOptions,
+      rules: {
+        fullName: [{ required: true, message: '请输入选项名' }],
+        id: [{ required: true, message: '请输入选项值' }],
+      },
+      testText: '',
+      originTestText: '',
+      isMask: false,
+      props: {
+        label: "fullName",
+        value: "id"
+      }
+    }
+  },
+  methods: {
+    openModal(data) {
+      this.visible = true;
+      this.isMask = false;
+      this.formData = JSON.parse(JSON.stringify(data));
+      this.testText = ''
+      this.$nextTick(() => {
+        this.$refs.formRef.clearValidate();
+      })
+    },
+    handleCancel() {
+      this.visible = false;
+    },
+    async handleSubmit() {
+      try {
+        this.visible = false;
+        this.$emit('confirm', this.formData);
+      } catch (_) { }
+    },
+    handleMask() {
+      if (!this.testText) return;
+      this.originTestText = this.testText;
+      this.isMask = true;
+      const { getMaskedText } = useTextMask(this.formData);
+      this.testText = getMaskedText(this.testText);
+    },
+    handleRevert() {
+      this.testText = this.originTestText;
+      this.originTestText = '';
+      this.isMask = false;
+    },
+    onNumberChange(val, key) {
+      if (val) return;
+      if (key === 'unrealMaskLength') return this.$nextTick(() => { (this.formData[key] = 1) })
+      this.$nextTick(() => { (this.formData[key] = 0) })
+    },
+    onMaskTypeChange(val) {
+      this.testText = ''
+      this.originTestText = ''
+      this.isMask = false
+      this.formData = {
+        ...defaultMaskOptions,
+        maskType: val
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" >
+.mask-config-dialog {
+  >>> .el-dialog__body {
+    padding: 20px 50px 0 !important;
+  }
+  .cursor-pointer {
+    cursor: pointer;
+  }
+}
+.select-down .el-select-dropdown__wrap {
+  height: 320px !important;
+  min-height: 320px !important;
+}
+</style>

+ 69 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/NumRange/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div class="numRange">
+    <el-input-number v-model="min" :controls="false" :precision="precision" placeholder="最小值"
+      @change="onChange" />
+    <span class="separator">-</span>
+    <el-input-number v-model="max" :controls="false" :precision="precision" placeholder="最大值"
+      @change="onChange" />
+  </div>
+</template>
+<script>
+export default {
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  props: ["value", "precision"],
+  name: 'numRange',
+  data() {
+    return {
+      min: undefined,
+      max: undefined
+    }
+  },
+  watch: {
+    value(val) {
+      this.setDefault()
+    },
+  },
+  created() {
+    this.setDefault()
+  },
+  methods: {
+    onChange() {
+      if (!this.min && this.min !== 0 && !this.max && this.max !== 0)
+        return this.$emit("change", []);
+      this.$emit("change", [this.min, this.max])
+    },
+    setDefault() {
+      if (Array.isArray(this.value) && this.value.length === 2) {
+        this.min = this.value[0] ? this.value[0] : this.value[0] == 0 ? 0 : undefined
+        this.max = this.value[1] ? this.value[1] : this.value[1] == 0 ? 0 : undefined
+      } else {
+        this.min = undefined
+        this.max = undefined
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.numRange {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  max-width: 220px;
+  .el-input-number {
+    flex: 1;
+    width: auto;
+    >>> .el-input__inner {
+      text-align: left;
+    }
+  }
+  .separator {
+    margin: 0 5px;
+    flex-shrink: 0;
+  }
+}
+</style>

+ 92 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/Sign/index.vue

@@ -0,0 +1,92 @@
+<template>
+  <div>
+    <div class="sign-main">
+      <el-image :src="innerValue" alt="" v-if="innerValue" class="sign-img"
+        :preview-src-list="srcList" />
+      <div @click="addSign" class="sign-style" :class="{ 'disabled-btn': disabled }"
+        v-if="!detailed">
+        <i class="icon-ym icon-ym-signature add-sign"></i>
+        <span class="sign-title" v-if="!innerValue">{{signTip}}</span>
+      </div>
+    </div>
+    <SignImgDialog v-if="signVisible" ref="SignImg" :lineWidth='3' :isDefault='1' :type='1'
+      @close="signDialog" />
+  </div>
+</template>
+<script>
+import SignImgDialog from '@/components/SignImgDialog'
+export default {
+  name: 'JnpfSign',
+  components: { SignImgDialog },
+  props: {
+    value: { type: String, default: '' },
+    signTip: { type: String, default: '手写签名' },
+    disabled: { type: Boolean, default: false },
+    detailed: { type: Boolean, default: false },
+  },
+  data() {
+    return {
+      signVisible: false,
+      innerValue: '',
+      srcList: []
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.innerValue = val || ''
+        if (this.innerValue) this.srcList = [this.innerValue]
+      },
+      deep: true,
+      immediate: true
+    },
+  },
+  methods: {
+    addSign() {
+      if (this.disabled) return
+      this.signVisible = true
+      this.$nextTick(() => {
+        this.$refs.SignImg.init()
+      })
+    },
+    signDialog(val) {
+      this.signVisible = false
+      if (val) {
+        this.srcList = [val]
+        this.innerValue = val
+        this.$emit('input', this.innerValue)
+      }
+    },
+  }
+}
+</script>
+<style lang="scss" scoped>
+.sign-img {
+  width: 80px;
+  height: 40px;
+  object-fit: contain;
+  cursor: pointer;
+}
+.add-sign {
+  font-size: 28px !important;
+}
+.sign-style {
+  height: 40px;
+  font-size: 14px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  &.disabled-btn {
+    cursor: no-drop !important;
+  }
+  .sign-icon {
+    font-size: 28px;
+  }
+  .sign-title {
+    height: 30px;
+    font-size: 14px;
+    color: #2188ff;
+    margin-top: 4px;
+  }
+}
+</style>

+ 48 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/components/UserSelect/index.vue

@@ -0,0 +1,48 @@
+<template>
+  <div class="comSelect-container">
+    <JnpfTreeSelect :options="treeData" v-model="innerValue" :placeholder="placeholder"
+      :clearable="clearable" :disabled="disabled" v-on="$listeners" lastLevel lastLevelKey='type'
+      :multiple="multiple" lastLevelValue='user'>
+    </JnpfTreeSelect>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'userSelect',
+  props: ["value", "disabled", "placeholder", "multiple", "clearable"],
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  data() {
+    return {
+      treeData: [],
+      innerValue: this.value
+    }
+  },
+  methods: {
+    async getData() {
+      this.treeData = await this.$store.dispatch('base/getUserTree')
+    }
+  },
+  created() {
+    this.getData()
+  },
+  watch: {
+    innerValue(val) {
+      // val && this.$emit('change', val)
+    },
+    value(val) {
+      this.innerValue = val
+    }
+  }
+}
+</script>
+<style lang="scss">
+.comSelect-container {
+  .el-select {
+    width: 100%;
+  }
+}
+</style>

+ 110 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/comConfig.js

@@ -0,0 +1,110 @@
+// 动态options
+const dyOptionsList = ['radio', 'checkbox', 'select', 'cascader', 'treeSelect']
+// 不添加vModel
+const noVModelList = ['divider', 'text', 'link', 'alert', 'groupTitle', 'relationFormAttr', 'popupAttr', 'button', 'barcode', 'qrcode', 'calculate', 'iframe', 'ITSM-INPUT']
+// 不可以添加到子表组件
+const noTableAllowList = ['divider', 'text', 'link', 'alert', 'groupTitle', 'button', 'barcode', 'qrcode', 'editor', 'radio', 'checkbox', 'createUser', 'createTime', 'modifyUser', 'modifyTime', 'currOrganize', 'currDept', 'currPosition', 'PsdInput', 'colorPicker', 'iframe']
+// 不可以添加到列表展示
+const noColumnShowList = ['PsdInput', 'colorPicker', 'divider', 'editor', 'text', 'relationFormAttr', 'popupAttr', 'groupTitle']
+// 不可以添加到搜索
+const noSearchList = [...noColumnShowList, 'switch', 'uploadImg', 'uploadFile', 'timeRange', 'dateRange', 'relationForm', 'popupSelect', 'popupTableSelect', 'sign']
+// 搜索时控件为input
+const useInputList = ['input', 'textarea', 'text', 'link', 'billRule', 'location']
+// 搜索时控件为日期选择器
+const useDateList = ['createTime', 'modifyTime']
+// 搜索时控件为下拉选择器
+const useSelectList = ['radio', 'checkbox', 'select']
+// 系统控件
+const systemComponentsList = ['createUser', 'createTime', 'modifyUser', 'modifyTime', 'currOrganize', 'currPosition', 'billRule']
+// 不允许关联到联动里面的控件
+const noAllowRelationList = ['table', 'uploadImg', 'uploadFile', 'modifyUser', 'modifyTime']
+// 不允许分组、排序、导入导出
+const noGroupList = ['sign', 'location', 'uploadImg', 'uploadFile'];
+const calculateItem = {
+  __config__: {
+    jnpfKey: 'calculate',
+    label: '计算公式',
+    tipLabel: "",
+    labelWidth: undefined,
+    showLabel: true,
+    required: false,
+    tag: 'JnpfCalculate',
+    tagIcon: 'icon-ym icon-ym-generator-count',
+    tableAlign: 'left',
+    tableFixed: 'none',
+    className: [],
+    defaultValue: null,
+    layout: 'colFormItem',
+    span: 24,
+    dragDisabled: false,
+    visibility: ["pc", "app"],
+    tableName: '',
+    noShow: false,
+    regList: []
+  },
+  style: { width: "100%" },
+  expression: [],
+  thousands: false,
+  isAmountChinese: false,
+  precision: 2,
+  isStorage: 0,
+}
+// 在线开发-功能设计/流程设计/移动设计独有组件
+const onlinePeculiarList = [{
+  __config__: {
+    jnpfKey: 'qrcode',
+    label: '二维码',
+    tipLabel: "",
+    labelWidth: undefined,
+    showLabel: true,
+    tag: 'JnpfQrcode',
+    tagIcon: 'icon-ym icon-ym-generator-qrcode',
+    tableAlign: 'left',
+    tableFixed: 'none',
+    className: [],
+    defaultValue: '',
+    layout: 'colFormItem',
+    span: 24,
+    dragDisabled: false,
+    visibility: ["pc", "app"],
+    tableName: '',
+    regList: [],
+  },
+  colorDark: '#000',
+  colorLight: '#fff',
+  width: 100,
+  dataType: 'static',
+  staticText: '二维码',
+  relationField: '',
+},
+{
+  __config__: {
+    jnpfKey: 'barcode',
+    label: '条形码',
+    tipLabel: "",
+    labelWidth: undefined,
+    showLabel: true,
+    tag: 'JnpfBarcode',
+    tagIcon: 'icon-ym icon-ym-generator-barcode',
+    tableAlign: 'left',
+    tableFixed: 'none',
+    className: [],
+    defaultValue: '',
+    layout: 'colFormItem',
+    span: 24,
+    dragDisabled: false,
+    visibility: ["pc", "app"],
+    tableName: '',
+    regList: [],
+  },
+  format: 'code128',
+  lineColor: '#000',
+  background: '#fff',
+  width: 4,
+  height: 40,
+  dataType: 'static',
+  staticText: '20220810',
+  relationField: '',
+},
+]
+export { dyOptionsList, noVModelList, noTableAllowList, noColumnShowList, noSearchList, calculateItem, onlinePeculiarList, useInputList, useDateList, useSelectList, systemComponentsList, noAllowRelationList, noGroupList }

+ 1988 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/config.js

@@ -0,0 +1,1988 @@
+// 表单属性【右面板】
+export const formConf = {
+  formRef: "formRef",
+  formModel: "dataForm",
+  size: "small",
+  labelPosition: "right",
+  labelWidth: 100,
+  labelSuffix: '', // 标题后缀
+  formRules: "rules",
+  popupType: "general",
+  generalWidth: "600px",
+  fullScreenWidth: "100%",
+  drawerWidth: "600px",
+  gutter: 15,
+  disabled: false,
+  span: 24,
+  formBtns: false,
+  hasCancelBtn: true,
+  cancelButtonText: "取 消",
+  hasConfirmBtn: true,
+  confirmButtonText: "确 定",
+  hasPrintBtn: false,
+  hasConfirmAndAddBtn: true,
+  confirmAndAddText: '确定并继续操作',
+  printButtonText: "打 印",
+  primaryKeyPolicy: 1,
+  concurrencyLock: false,
+  logicalDelete: false,
+  printId: "",
+  formStyle: "",
+  classNames: [],
+  className: [],
+  classJson: "",
+  funcs: {
+    onLoad:
+      "({ formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+    beforeSubmit:
+      "({ formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    return new Promise((resolve, reject) => {\n        // 在此编写代码\n        \n        // 继续执行\n        resolve()\n    })\n}",
+    afterSubmit:
+      "({ formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+  },
+  idGlobal: 100
+};
+
+// 基础控件 【左面板】
+export const inputComponents = [
+  {
+    __config__: {
+      jnpfKey: "input",
+      label: "单行输入",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-input",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      unique: false,
+      regList: [],
+      trigger: "blur"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请输入",
+    style: { width: "100%" },
+    useScan: false,
+    useMask: false,
+    maskConfig: {
+      filler: '*',
+      maskType: 1,
+      prefixType: 1,
+      prefixLimit: 0,
+      prefixSpecifyChar: '',
+      suffixType: 1,
+      suffixLimit: 0,
+      suffixSpecifyChar: '',
+      ignoreChar: '',
+      useUnrealMask: false,
+      unrealMaskLength: 1,
+    },
+    clearable: true,
+    addonBefore: '',
+    addonAfter: '',
+    prefixIcon: '',
+    suffixIcon: '',
+    maxlength: null,
+    showWordLimit: false,
+    showPassword: false,
+    readonly: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "textarea",
+      label: "多行输入",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfTextarea",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "blur"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    type: "textarea",
+    placeholder: "请输入",
+    autoSize: {
+      minRows: 4,
+      maxRows: 4
+    },
+    style: { width: "100%" },
+    useScan: false,
+    useMask: false,
+    maskConfig: {
+      filler: '*',
+      maskType: 1,
+      prefixType: 1,
+      prefixLimit: 0,
+      prefixSpecifyChar: '',
+      suffixType: 1,
+      suffixLimit: 0,
+      suffixSpecifyChar: '',
+      ignoreChar: '',
+      useUnrealMask: false,
+      unrealMaskLength: 1,
+    },
+    maxlength: null,
+    showWordLimit: true,
+    readonly: false,
+    disabled: false,
+    readonly: false,
+  },
+  {
+    __config__: {
+      jnpfKey: "inputNumber",
+      label: "数字输入",
+      tipLabel: "",
+      showLabel: true,
+      labelWidth: undefined,
+      tag: "JnpfInputNumber",
+      tagIcon: "icon-ym icon-ym-generator-number",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: ["blur", "change"]
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: null },
+    placeholder: "数字文本",
+    min: undefined,
+    max: undefined,
+    step: 1,
+    "step-strictly": false,
+    precision: undefined,
+    controls: false,
+    controlsPosition: "",
+    addonBefore: null,
+    addonAfter: null,
+    thousands: false,
+    isAmountChinese: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "switch",
+      label: "开关",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfSwitch",
+      tagIcon: "icon-ym icon-ym-generator-switch",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    disabled: false,
+    activeTxt: "开",
+    inactiveTxt: "关",
+    activeColor: null,
+    inactiveColor: null,
+    activeValue: 1,
+    inactiveValue: 0
+  },
+  {
+    __config__: {
+      jnpfKey: "radio",
+      label: "单选框组",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfRadio",
+      tagIcon: "icon-ym icon-ym-generator-radio",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      dataType: "static",
+      dictionaryType: "",
+      propsUrl: "",
+      propsName: "",
+      templateJson: [],
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: {},
+    size: "small",
+    disabled: false,
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    direction: 'horizontal',
+    optionType: "default",
+    border: false,
+  },
+  {
+    __config__: {
+      jnpfKey: "checkbox",
+      label: "多选框组",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfCheckbox",
+      tagIcon: "icon-ym icon-ym-generator-checkbox",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      dataType: "static",
+      dictionaryType: "",
+      propsUrl: "",
+      propsName: "",
+      templateJson: [],
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: {},
+    size: "small",
+    optionType: "default",
+    border: false,
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    direction: 'horizontal',
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "select",
+      label: "下拉选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfSelect",
+      tagIcon: "icon-ym icon-ym-generator-select",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      dataType: "static",
+      dictionaryType: "",
+      propsUrl: "",
+      propsName: "",
+      templateJson: [],
+    },
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请选择",
+    style: { width: "100%" },
+    clearable: true,
+    disabled: false,
+    filterable: false,
+    multiple: false
+  },
+  {
+    __config__: {
+      jnpfKey: "cascader",
+      label: "级联选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfCascader",
+      tagIcon: "icon-ym icon-ym-generator-cascader",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      dataType: "static",
+      propsUrl: "",
+      propsName: "",
+      templateJson: [],
+      dictionaryType: ""
+    },
+    options: [
+      {
+        id: "1",
+        fullName: "选项1",
+        children: [
+          {
+            id: "2",
+            fullName: "选项1-1"
+          }
+        ]
+      }
+    ],
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请选择",
+    style: { width: "100%" },
+    props: {
+      value: "id",
+      label: "fullName",
+      children: "children"
+    },
+    multiple: false,
+    showAllLevels: true,
+    disabled: false,
+    clearable: true,
+    filterable: false,
+    separator: "/"
+  },
+  {
+    __config__: {
+      jnpfKey: "datePicker",
+      label: "日期选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfDatePicker",
+      tagIcon: "icon-ym icon-ym-generator-date",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      noShow: false,
+      defaultValue: null,
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      startTimeRule: false,
+      startTimeType: 1,
+      startTimeTarget: 1,
+      startTimeValue: "",
+      endTimeRule: false,
+      endTimeType: 1,
+      endTimeTarget: 1,
+      startRelationField: "",
+      endRelationField: "",
+      endTimeValue: "",
+      visibility: ["pc", "app"],
+      tableName: "",
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请选择",
+    type: "date",
+    style: { width: "100%" },
+    disabled: false,
+    clearable: true,
+    format: "yyyy-MM-dd",
+    startTime: null,
+    endTime: null,
+    readonly: false,
+  },
+  {
+    __config__: {
+      jnpfKey: "timePicker",
+      label: "时间选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfTimePicker",
+      tagIcon: "icon-ym icon-ym-generator-time",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      noShow: false,
+      defaultValue: null,
+      required: false,
+      defaultCurrent: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      startTimeRule: false,
+      startTimeType: 1,
+      startTimeTarget: 1,
+      startTimeValue: "",
+      endTimeRule: false,
+      endTimeType: 1,
+      endTimeTarget: 1,
+      endTimeValue: "",
+      startRelationField: "",
+      endRelationField: "",
+      visibility: ["pc", "app"],
+      tableName: "",
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请选择",
+    style: { width: "100%" },
+    disabled: false,
+    clearable: true,
+    startTime: null,
+    endTime: null,
+    readonly: false,
+    format: "HH:mm:ss",
+  },
+  {
+    __config__: {
+      jnpfKey: "uploadFile",
+      label: "文件上传",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfUploadFile",
+      tagIcon: "icon-ym icon-ym-generator-upload",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    disabled: false,
+    accept: "",
+    fileSize: 10,
+    sizeUnit: "MB",
+    buttonText: "点击上传",
+    showTip: false,
+    pathType: "defaultPath",
+    isAccount: 0,
+    folder: "",
+    tipText: '',
+    limit: 9
+  },
+  {
+    __config__: {
+      jnpfKey: "uploadImg",
+      label: "图片上传",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfUploadImg",
+      tagIcon: "icon-ym icon-ym-generator-upload",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    disabled: false,
+    accept: "",
+    showTip: false,
+    fileSize: 10,
+    sizeUnit: "MB",
+    pathType: "defaultPath",
+    isAccount: 0,
+    folder: "",
+    tipText: '',
+    limit: 9
+  },
+  {
+    __config__: {
+      jnpfKey: "colorPicker",
+      label: "颜色选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfColorPicker",
+      tagIcon: "icon-ym icon-ym-generator-color",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    showAlpha: false,
+    colorFormat: "",
+    disabled: false,
+    size: "small"
+  },
+  {
+    __config__: {
+      jnpfKey: "rate",
+      label: "评分",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfRate",
+      tagIcon: "icon-ym icon-ym-generator-rate",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: 0,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: {},
+    count: 5,
+    allowHalf: false,
+    showText: false,
+    showScore: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "slider",
+      label: "滑块",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfSlider",
+      tagIcon: "icon-ym icon-ym-generator-slider",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    disabled: false,
+    min: 0,
+    max: 100,
+    step: 1,
+    showStops: false,
+    range: false
+  },
+  {
+    __config__: {
+      jnpfKey: "editor",
+      label: "富文本",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfEditor",
+      tagIcon: "icon-ym icon-ym-generator-rich-text",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "blur"
+    },
+    style: { width: "100%" },
+    placeholder: "请输入",
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "link",
+      label: "链接",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfLink",
+      tagIcon: "icon-ym icon-ym-generator-link",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    on: {
+      click:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    content: '文本链接',
+    href: "",
+    target: "_self",
+    textStyle: {
+      "text-align": "left",
+    }
+  },
+  {
+    __config__: {
+      jnpfKey: "button",
+      label: "按钮",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfButton",
+      tagIcon: "icon-ym icon-ym-generator-button",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      regList: [],
+      trigger: "click"
+    },
+    on: {
+      click:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    align: "left",
+    buttonText: "按钮",
+    type: "",
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "text",
+      label: "文本",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfText",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    textStyle: {
+      color: "#000000",
+      "text-align": "left",
+      "font-weight": "normal",
+      "font-style": "normal",
+      "text-decoration": "none",
+      "line-height": 32,
+      "font-size": 12
+    },
+    content: '这是一段文字',
+  },
+  {
+    __config__: {
+      jnpfKey: "alert",
+      label: "提示",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfAlert",
+      tagIcon: "icon-ym icon-ym-generator-alert",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    title: '这是一个提示',
+    type: "success",
+    showIcon: false,
+    closable: true,
+    description: '',
+    closeText: '',
+  },
+];
+
+// 高级控件 【左面板】
+export const selectComponents = [
+  {
+    __config__: {
+      jnpfKey: "organizeSelect",
+      label: "组织选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfOrganizeSelect",
+      tagIcon: "icon-ym icon-ym-generator-company",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    multiple: false,
+    clearable: true,
+    filterable: false,
+    disabled: false,
+    ableIds: [],
+    selectType: 'all',
+  },
+  {
+    __config__: {
+      jnpfKey: "depSelect",
+      label: "部门选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfDepSelect",
+      tagIcon: "icon-ym icon-ym-generator-department",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    selectType: 'all',
+    ableIds: [],
+    multiple: false,
+    clearable: true,
+    filterable: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "posSelect",
+      label: "岗位选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfPosSelect",
+      tagIcon: "icon-ym icon-ym-generator-jobs",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    selectType: 'all',
+    ableIds: [],
+    multiple: false,
+    clearable: true,
+    filterable: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "userSelect",
+      label: "用户选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfUserSelect",
+      tagIcon: "icon-ym icon-ym-generator-user",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      relationField: '',
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    ableIds: [],
+    selectType: 'all',
+    relationField: '',
+    multiple: false,
+    clearable: true,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "roleSelect",
+      label: "角色选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      defaultCurrent: false,
+      showLabel: true,
+      tag: "JnpfRoleSelect",
+      tagIcon: "icon-ym icon-ym-generator-role",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    multiple: false,
+    clearable: true,
+    disabled: false,
+    ableIds: [],
+    selectType: 'all',
+  },
+  {
+    __config__: {
+      jnpfKey: "groupSelect",
+      label: "分组选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfGroupSelect",
+      tagIcon: "icon-ym icon-ym-generator-group1",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: '',
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    multiple: false,
+    clearable: true,
+    filterable: false,
+    disabled: false,
+    ableIds: [],
+    selectType: 'all',
+  },
+  {
+    __config__: {
+      jnpfKey: "usersSelect",
+      label: "用户组件",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfUsersSelect",
+      tagIcon: "icon-ym icon-ym-generator-founder",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      defaultCurrent: false,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请选择",
+    selectType: 'all',
+    ableIds: [],
+    multiple: false,
+    clearable: true,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "table",
+      label: "设计子表",
+      tipLabel: "",
+      showLabel: false,
+      tagIcon: "icon-ym icon-ym-generator-table",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      tag: "JnpfInputTable",
+      defaultValue: [],
+      layout: "rowFormItem",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      showTitle: true,
+      type: "table",
+      rowType: "table",
+      children: [],
+      tableName: "",
+      complexHeaderList: [],
+      fixed: 'none',
+      align: "left"
+    },
+    showAddBtn: true,
+    showDeleteBtn: true,
+    disabled: false,
+    actionText: "添加",
+    showSummary: false,
+    addType: 0,
+    summaryField: [],
+    thousands: false,
+    thousandsField: [],
+    tableConf: {},
+    defaultValue: [],
+    columnBtnsList: [
+      { value: 'copy', label: '复制', show: true, btnType: 'primary', btnIcon: 'icon-ym icon-ym-btn-edit' },
+      { value: 'remove', label: '删除', show: true, btnType: 'danger', btnIcon: 'icon-ym icon-ym-btn-clearn', showConfirm: true },
+    ],
+    footerBtnsList: [
+      { value: 'add', label: '添加', show: true, btnType: 'primary', btnIcon: 'icon-ym icon-ym-btn-add' },
+      { value: 'batchRemove', label: '批量删除', show: true, btnType: 'danger', btnIcon: 'icon-ym icon-ym-btn-clearn', showConfirm: true },
+    ],
+  },
+  {
+    __config__: {
+      jnpfKey: "treeSelect",
+      label: "下拉树形",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfTreeSelect",
+      tagIcon: "icon-ym icon-ym-generator-tree",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      dataType: "static",
+      dictionaryType: "",
+      propsUrl: "",
+      propsName: "",
+      templateJson: [],
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    props: {
+      value: "id",
+      label: "fullName",
+      children: "children"
+    },
+    options: [
+      {
+        id: "1",
+        fullName: "选项1",
+        children: [
+          {
+            id: "2",
+            fullName: "选项1-1"
+          }
+        ]
+      }
+    ],
+    multiple: false,
+    clearable: true,
+    filterable: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "popupTableSelect",
+      label: "下拉表格",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      required: false,
+      tag: "JnpfPopupTableSelect",
+      tagIcon: "icon-ym icon-ym-generator-popupTableSelect",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      noShow: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    interfaceId: "",
+    interfaceName: "",
+    templateJson: [],
+    hasPage: false,
+    pageSize: 20,
+    columnOptions: [],
+    propsValue: "id",
+    relationField: "fullName",
+    popupType: "popover",
+    popupTitle: "选择数据",
+    popupWidth: "800px",
+    disabled: false,
+    clearable: true,
+    multiple: false,
+    filterable: false,
+    interfaceHasPage: 0
+  },
+  {
+    __config__: {
+      jnpfKey: "autoComplete",
+      label: "下拉补全",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfAutoComplete",
+      tagIcon: "icon-ym icon-ym-generator-autoComplete",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      unique: false,
+      regList: [],
+      trigger: "blur"
+    },
+    __slot__: {
+      prepend: "",
+      append: ""
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}",
+      blur:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    placeholder: "请输入",
+    style: { width: "100%" },
+    total: 10,
+    relationField: "fullName",
+    interfaceId: "",
+    interfaceName: "",
+    templateJson: [],
+    clearable: true,
+    maxlength: null,
+    readonly: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "areaSelect",
+      label: "省市区域",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfAreaSelect",
+      tagIcon: "icon-ym icon-ym-generator-Provinces",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      noShow: false,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      regList: [],
+      trigger: "change"
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    disabled: false,
+    ableAddressIds: [],
+    selectType: 'all',
+    clearable: true,
+    filterable: false,
+    multiple: false,
+    level: 2
+  },
+  {
+    __config__: {
+      jnpfKey: "relationForm",
+      label: "关联表单",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfRelationForm",
+      tagIcon: "icon-ym icon-ym-generator-menu",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      noShow: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      regList: [],
+      trigger: "change",
+      transferList: []
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    modelId: "",
+    relationField: "",
+    hasPage: false,
+    pageSize: 20,
+    columnOptions: [],
+    clearable: true,
+    popupType: "dialog",
+    popupTitle: "选择数据",
+    popupWidth: "800px",
+    filterable: false,
+    disabled: false
+  },
+  {
+    __config__: {
+      jnpfKey: "relationFormAttr",
+      label: "关联表单属性",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfRelationFormAttr",
+      tagIcon: "icon-ym icon-ym-generator-nature",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      noShow: false,
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    style: { width: "100%" },
+    showField: "",
+    relationField: "",
+    isStorage: 0,
+  },
+  {
+    __config__: {
+      jnpfKey: "popupSelect",
+      label: "弹窗选择",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      required: false,
+      tag: "JnpfPopupSelect",
+      tagIcon: "icon-ym icon-ym-generator-popup",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      regList: [],
+      trigger: "change",
+      transferList: []
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    style: { width: "100%" },
+    placeholder: "请选择",
+    interfaceId: "",
+    interfaceName: "",
+    templateJson: [],
+    hasPage: false,
+    pageSize: 20,
+    columnOptions: [],
+    propsValue: "id",
+    relationField: "fullName",
+    popupType: "dialog",
+    popupTitle: "选择数据",
+    popupWidth: "800px",
+    disabled: false,
+    clearable: true,
+    interfaceHasPage: 0
+  },
+  {
+    __config__: {
+      jnpfKey: "popupAttr",
+      label: "弹窗选择属性",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfPopupAttr",
+      tagIcon: "icon-ym icon-ym-generator-popup-attr",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      noShow: false,
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    style: { width: "100%" },
+    showField: "",
+    relationField: "",
+    isStorage: 0,
+  },
+  {
+    __config__: {
+      jnpfKey: 'sign',
+      label: '手写签名',
+      tipLabel: '',
+      labelWidth: undefined,
+      showLabel: true,
+      tag: 'JnpfSign',
+      tagIcon: 'icon-ym icon-ym-signature',
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: '',
+      defaultCurrent: false,
+      required: false,
+      layout: 'colFormItem',
+      span: 24,
+      dragDisabled: false,
+      visibility: ['pc', 'app'],
+      tableName: '',
+      noShow: false,
+      regList: [],
+      trigger: 'change',
+    },
+    on: {
+      change: '({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}',
+    },
+    clearable: true,
+    disabled: false,
+  },
+  {
+    __config__: {
+      jnpfKey: 'location',
+      label: '定位',
+      tipLabel: '',
+      labelWidth: undefined,
+      showLabel: true,
+      tag: 'JnpfLocation',
+      tagIcon: 'icon-ym icon-ym-portal-mapChart',
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: undefined,
+      required: false,
+      layout: 'colFormItem',
+      span: 24,
+      dragDisabled: false,
+      visibility: ['pc', 'app'],
+      tableName: '',
+      noShow: false,
+      regList: [],
+      trigger: 'change',
+    },
+    on: {
+      change: '({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}',
+    },
+    autoLocation: false,
+    adjustmentScope: 500,
+    enableLocationScope: false,
+    enableDesktopLocation: false,
+    locationScope: undefined,
+    clearable: true,
+    disabled: false,
+  },
+  {
+    __config__: {
+      jnpfKey: 'iframe',
+      label: 'Iframe',
+      labelWidth: undefined,
+      showLabel: true,
+      tag: 'JnpfIframe',
+      tagIcon: 'icon-ym icon-ym-generator-iframe',
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: 'colFormItem',
+      span: 24,
+      dragDisabled: false,
+      visibility: ['pc'],
+      noShow: false,
+    },
+    href: '',
+    height: 300,
+    borderType: 'solid',
+    borderColor: '#E2E0E0',
+    borderWidth: 1
+  },
+];
+
+// 系统控件 【左面板】
+export const systemComponents = [
+  {
+    __config__: {
+      jnpfKey: "createUser",
+      label: "创建人员",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfOpenData",
+      tagIcon: "icon-ym icon-ym-generator-founder",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    type: 'currUser',
+    readonly: true,
+    placeholder: ""
+  },
+  {
+    __config__: {
+      jnpfKey: "createTime",
+      label: "创建时间",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfOpenData",
+      tagIcon: "icon-ym icon-ym-generator-createtime",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      layout: "colFormItem",
+      required: false,
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    type: 'currTime',
+    readonly: true,
+    placeholder: ""
+  },
+  {
+    __config__: {
+      jnpfKey: "modifyUser",
+      label: "修改人员",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-modifier",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    readonly: true,
+    placeholder: "系统自动生成"
+  },
+  {
+    __config__: {
+      jnpfKey: "modifyTime",
+      label: "修改时间",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-modifytime",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    readonly: true,
+    placeholder: "系统自动生成"
+  },
+  {
+    __config__: {
+      jnpfKey: "currOrganize",
+      label: "所属组织",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfOpenData",
+      tagIcon: "icon-ym icon-ym-generator-company",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    type: 'currOrganize',
+    readonly: true,
+    showLevel: "last",
+    placeholder: ""
+  },
+  {
+    __config__: {
+      jnpfKey: "currPosition",
+      label: "所属岗位",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfOpenData",
+      tagIcon: "icon-ym icon-ym-generator-station",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: "",
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false
+    },
+    style: { width: "100%" },
+    type: 'currPosition',
+    readonly: true,
+    placeholder: ""
+  },
+  {
+    __config__: {
+      jnpfKey: "billRule",
+      label: "单据组件",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-documents",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      layout: "colFormItem",
+      required: false,
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      tableName: "",
+      noShow: false,
+      trigger: "change",
+      rule: "",
+      ruleName: "",
+    },
+    style: { width: "100%" },
+    readonly: true,
+    placeholder: "系统自动生成"
+  },
+];
+
+// 布局控件 【左面板】
+export const layoutComponents = [
+  {
+    __config__: {
+      jnpfKey: "groupTitle",
+      label: "分组标题",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfGroupTitle",
+      tagIcon: "icon-ym icon-ym-generator-group",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    content: "分组标题",
+    helpMessage: "",
+    contentPosition: "left"
+  },
+  {
+    __config__: {
+      jnpfKey: "divider",
+      label: "分割线",
+      tipLabel: "",
+      labelWidth: undefined,
+      showLabel: false,
+      tag: "JnpfDivider",
+      tagIcon: "icon-ym icon-ym-generator-divider",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: null,
+      required: false,
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      visibility: ["pc", "app"]
+    },
+    content: '我是分割线',
+    contentPosition: "center"
+  },
+  {
+    __config__: {
+      jnpfKey: "collapse",
+      label: "折叠面板",
+      tipLabel: "",
+      showLabel: false,
+      tag: "el-collapse",
+      tagIcon: "icon-ym icon-ym-generator-fold",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      children: [
+        {
+          title: "面板1",
+          name: "1",
+          __config__: {
+            jnpfKey: "collapseItem",
+            children: [],
+          }
+        },
+        {
+          title: "面板2",
+          name: "2",
+          __config__: {
+            jnpfKey: "collapseItem",
+            children: [],
+          }
+        }
+      ],
+      active: ["1"]
+    },
+    on: {
+      change:
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    accordion: false
+  },
+  {
+    __config__: {
+      jnpfKey: "tab",
+      label: "标签面板",
+      tipLabel: "",
+      showLabel: false,
+      tag: "el-tab",
+      tagIcon: "icon-ym icon-ym-generator-label",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      children: [
+        {
+          title: "Tab 1",
+          name: '1',
+          __config__: {
+            jnpfKey: "tabItem",
+            children: [],
+          }
+        },
+        {
+          title: "Tab 2",
+          name: '2',
+          __config__: {
+            jnpfKey: "tabItem",
+            children: [],
+          }
+        }
+      ],
+      active: "1"
+    },
+    on: {
+      "tabClick":
+        "({ data, rowIndex, formData, setFormData, setShowOrHide, setRequired, setDisabled, onlineUtils }) => {\n    // 在此编写代码\n    \n}"
+    },
+    type: "",
+    tabPosition: "top"
+  },
+  {
+    __config__: {
+      jnpfKey: "row",
+      label: "栅格容器",
+      tipLabel: "",
+      tagIcon: "icon-ym icon-ym-generator-layout",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      layoutTree: true,
+      rowType: "layout"
+    },
+    type: "default",
+    justify: "start",
+    align: "top"
+  },
+  {
+    __config__: {
+      jnpfKey: "card",
+      label: "卡片容器",
+      tipLabel: "",
+      showLabel: false,
+      tag: "el-card",
+      tagIcon: "icon-ym icon-ym-generator-card",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      children: []
+    },
+    header: "卡片容器",
+    shadow: "always"
+  },
+  {
+    __config__: {
+      jnpfKey: "tableGrid",
+      label: "表格容器",
+      showLabel: false,
+      tagIcon: "icon-ym icon-ym-generator-tableGrid",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      className: [],
+      defaultValue: [],
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      visibility: ["pc", "app"],
+      borderType: "solid",
+      borderColor: '#E2E0E0',
+      borderWidth: 1,
+      rowType: "tableGrid",
+      children: [
+        {
+          __config__: {
+            jnpfKey: "tableGridTr",
+            children: [
+              {
+                __config__: {
+                  jnpfKey: "tableGridTd",
+                  merged: false,
+                  rowType: "tableGrid",
+                  colspan: 1,
+                  rowspan: 1,
+                  children: [],
+                  backgroundColor: '',
+                }
+              }
+            ]
+          },
+        }
+      ]
+    },
+  },
+];

+ 18 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/css.js

@@ -0,0 +1,18 @@
+const styles = {
+  'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}',
+  'el-upload': '.el-upload__tip{line-height: 1.2;}'
+}
+
+function addCss(cssList, el) {
+  const css = styles[el.__config__.tag]
+  css && cssList.indexOf(css) === -1 && cssList.push(css)
+  if (el.__config__.children) {
+    el.__config__.children.forEach(el2 => addCss(cssList, el2))
+  }
+}
+
+export function makeUpCss(conf) {
+  const cssList = []
+  conf.fields.forEach(el => addCss(cssList, el))
+  return cssList.join('\n')
+}

+ 35 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/drawingDefalut.js

@@ -0,0 +1,35 @@
+export default [{
+  __config__: {
+    label: '单行输入框',
+    labelWidth: null,
+    showLabel: true,
+    changeTag: true,
+    tag: 'el-input',
+    tagIcon: 'input',
+    defaultValue: undefined,
+    required: true,
+    layout: 'colFormItem',
+    span: 24,
+    jnpfKey: 'input',
+    // 正则校验规则
+    regList: [{
+      pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
+      message: '手机号格式错误'
+    }]
+  },
+  // 组件的插槽属性
+  __slot__: {
+    prepend: '',
+    append: ''
+  },
+  __vModel__: 'mobile',
+  placeholder: '请输入手机号',
+  style: { width: '100%' },
+  clearable: true,
+  prefixIcon: 'el-icon-mobile',
+  suffixIcon: '',
+  maxlength: 11,
+  showWordLimit: true,
+  readonly: false,
+  disabled: false,
+}]

+ 508 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/html.js

@@ -0,0 +1,508 @@
+/* eslint-disable max-len */
+import ruleTrigger from './ruleTrigger'
+
+let confGlobal
+let someSpanIsNot24
+
+export function dialogWrapper(str) {
+  return `<el-dialog v-bind="$attrs" v-on="$listeners" @open="onOpen" @close="onClose" title="Dialog Titile">
+    ${str}
+    <div slot="footer">
+      <el-button @click="close">取消</el-button>
+      <el-button type="primary" @click="handelConfirm">确定</el-button>
+    </div>
+  </el-dialog>`
+}
+
+export function vueTemplate(str) {
+  return `<template>
+    <div>
+      ${str}
+    </div>
+  </template>`
+}
+
+export function vueScript(str) {
+  return `<script>
+    ${str}
+  </script>`
+}
+
+export function cssStyle(cssStr) {
+  return `<style>
+    ${cssStr}
+  </style>`
+}
+
+function buildFormTemplate(scheme, child, type) {
+  let labelPosition = ''
+  if (scheme.labelPosition !== 'right') {
+    labelPosition = `label-position="${scheme.labelPosition}"`
+  }
+  const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : ''
+  let str = `<el-form ref="${scheme.formRef}" :model="${scheme.formModel}" :rules="${scheme.formRules}" size="${scheme.size}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
+      ${child}
+      ${buildFromBtns(scheme, type)}
+    </el-form>`
+  if (someSpanIsNot24) {
+    str = `<el-row :gutter="${scheme.gutter}">
+        ${str}
+      </el-row>`
+  }
+  return str
+}
+
+function buildFromBtns(scheme, type) {
+  let str = ''
+  if (scheme.formBtns && type === 'file') {
+    str = `<el-form-item size="large">
+          <el-button type="primary" @click="submitForm">提交</el-button>
+          <el-button @click="resetForm">重置</el-button>
+        </el-form-item>`
+    if (someSpanIsNot24) {
+      str = `<el-col :span="24">
+          ${str}
+        </el-col>`
+    }
+  }
+  return str
+}
+
+// span不为24的用el-col包裹
+function colWrapper(scheme, str) {
+  if (someSpanIsNot24 || scheme.__config__.span !== 24) {
+    return `<el-col :span="${scheme.__config__.span}">
+      ${str}
+    </el-col>`
+  }
+  return str
+}
+
+const layouts = {
+  colFormItem(scheme) {
+    const config = scheme.__config__
+    let labelWidth = ''
+    let label = `label="${config.label}"`
+    if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
+      labelWidth = `label-width="${config.labelWidth}px"`
+    }
+    if (config.showLabel === false) {
+      labelWidth = 'label-width="0"'
+      label = ''
+    }
+    const required = !ruleTrigger[config.tag] && config.required ? 'required' : ''
+    const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null
+    let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}>
+        ${tagDom}
+      </el-form-item>`
+    str = colWrapper(scheme, str)
+    return str
+  },
+  rowFormItem(scheme) {
+    const config = scheme.__config__
+    const type = scheme.type === 'default' ? '' : `type="${scheme.type}"`
+    const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"`
+    const align = scheme.type === 'default' ? '' : `align="${scheme.align}"`
+    const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : ''
+    const children = config.children.map(el => layouts[el.__config__.layout](el))
+    let str = `<el-row ${type} ${justify} ${align} ${gutter}>
+      ${children.join('\n')}
+    </el-row>`
+    str = colWrapper(scheme, str)
+    return str
+  }
+}
+
+const tags = {
+  'el-button': el => {
+    const {
+      tag,
+      disabled
+    } = attrBuilder(el)
+    const type = el.type ? `type="${el.type}"` : ''
+    const icon = el.icon ? `icon="${el.icon}"` : ''
+    const round = el.round ? 'round' : ''
+    const size = el.size ? `size="${el.size}"` : ''
+    const plain = el.plain ? 'plain' : ''
+    const circle = el.circle ? 'circle' : ''
+    let child = buildElButtonChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`
+  },
+  'el-input': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      clearable,
+      placeholder,
+      width
+    } = attrBuilder(el)
+    const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
+    const showWordLimit = el.showWordLimit ? 'showWordLimit' : ''
+    const readonly = el.readonly ? 'readonly' : ''
+    const prefixIcon = el.prefixIcon ? `prefix-icon='${el.prefixIcon}'` : ''
+    const suffixIcon = el.suffixIcon ? `suffix-icon='${el.suffixIcon}'` : ''
+    const showPassword = el.showPassword ? 'showPassword' : ''
+    const type = el.type ? `type="${el.type}"` : ''
+    const autosize = el.autosize && el.autosize.minRows ?
+      `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` :
+      ''
+    let child = buildElInputChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`
+  },
+  'el-input-number': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      placeholder
+    } = attrBuilder(el)
+    const controlsPosition = el['controls-position'] ? `controls-position='${el['controls-position']}'` : ''
+    const min = el.min ? `:min='${el.min}'` : ''
+    const max = el.max ? `:max='${el.max}'` : ''
+    const step = el.step ? `:step='${el.step}'` : ''
+    const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
+    const precision = el.precision ? `:precision='${el.precision}'` : ''
+
+    return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`
+  },
+  'el-select': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      clearable,
+      placeholder,
+      width
+    } = attrBuilder(el)
+    const filterable = el.filterable ? 'filterable' : ''
+    const multiple = el.multiple ? 'multiple' : ''
+    let child = buildElSelectChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
+  },
+  'el-radio-group': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const size = `size="${el.size}"`
+    let child = buildElRadioGroupChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`
+  },
+  'el-checkbox-group': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const size = `size="${el.size}"`
+    const min = el.min ? `:min="${el.min}"` : ''
+    const max = el.max ? `:max="${el.max}"` : ''
+    let child = buildElCheckboxGroupChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`
+  },
+  'el-switch': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
+    const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
+    const activeColor = el['activeColor'] ? `activeColor="${el['activeColor']}"` : ''
+    const inactiveColor = el['inactiveColor'] ? `inactiveColor="${el['inactiveColor']}"` : ''
+    const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
+    const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
+
+    return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`
+  },
+  'el-cascader': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      clearable,
+      placeholder,
+      width
+    } = attrBuilder(el)
+    const options = el.options ? `:options="${el.__vModel__}Options"` : ''
+    const props = el.props ? `:props="${el.__vModel__}Props"` : ''
+    const showAllLevels = el.showAllLevels ? '' : ':show-all-levels="false"'
+    const filterable = el.filterable ? 'filterable' : ''
+    const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
+
+    return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`
+  },
+  'el-slider': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const min = el.min ? `:min='${el.min}'` : ''
+    const max = el.max ? `:max='${el.max}'` : ''
+    const step = el.step ? `:step='${el.step}'` : ''
+    const range = el.range ? 'range' : ''
+    const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
+
+    return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`
+  },
+  'el-time-picker': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      clearable,
+      placeholder,
+      width
+    } = attrBuilder(el)
+    const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+    const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+    const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+    const isRange = el['is-range'] ? 'is-range' : ''
+    const format = el.format ? `format="${el.format}"` : ''
+    const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+    const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
+
+    return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`
+  },
+  'el-date-picker': el => {
+    const {
+      tag,
+      disabled,
+      vModel,
+      clearable,
+      placeholder,
+      width
+    } = attrBuilder(el)
+    const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
+    const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
+    const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : ''
+    const format = el.format ? `format="${el.format}"` : ''
+    const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
+    const type = el.type === 'date' ? '' : `type="${el.type}"`
+    const readonly = el.readonly ? 'readonly' : ''
+
+    return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`
+  },
+  'el-rate': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const max = el.max ? `:max='${el.max}'` : ''
+    const allowHalf = el['allow-half'] ? 'allow-half' : ''
+    const showText = el['show-text'] ? 'show-text' : ''
+    const showScore = el['show-score'] ? 'show-score' : ''
+
+    return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`
+  },
+  'el-color-picker': el => {
+    const { tag, disabled, vModel } = attrBuilder(el)
+    const size = `size="${el.size}"`
+    const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
+    const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
+
+    return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`
+  },
+  'el-upload': el => {
+    const { tag } = el.__config__
+    const disabled = el.disabled ? ':disabled=\'true\'' : ''
+    const action = el.action ? `:action="${el.__vModel__}Action"` : ''
+    const multiple = el.multiple ? 'multiple' : ''
+    const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
+    const accept = el.accept ? `accept="${el.accept}"` : ''
+    const name = el.name !== 'file' ? `name="${el.name}"` : ''
+    const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
+    const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`
+    const fileList = `:file-list="${el.__vModel__}fileList"`
+    const ref = `ref="${el.__vModel__}"`
+    let child = buildElUploadChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`
+  },
+  tinymce: el => {
+    const { tag, vModel, placeholder } = attrBuilder(el)
+    const height = el.height ? `:height="${el.height}"` : ''
+    const branding = el.branding ? `:branding="${el.branding}"` : ''
+    return `<${tag} ${vModel} ${placeholder} ${height} ${branding}></${tag}>`
+  },
+  "JNPF-Text": el => {
+    const { tag } = attrBuilder(el)
+    let child = '',
+      textStyle = ''
+    for (const key in el.textStyle) {
+      const value = el.textStyle[key];
+      child += "\'" + key + "\': \'" + value + "\',"
+    }
+    textStyle = `:textStyle="{${child}}"`
+    const lineHeight = `:lineHeight="${el.lineHeight}"`
+    const fontSize = `:fontSize="${el.fontSize}"`
+    // const vModel = `v-model="'${el.__config__.defaultValue}'"`
+    const vModel = `value="${el.__config__.defaultValue}"`
+    return `<${tag} ${vModel} ${textStyle} ${lineHeight} ${fontSize}></${tag}>`
+  },
+  'JNPF-Amount': function (el) {
+    const tag = this['el-input-number'](el)
+    const showChinese = el.showChinese ? `:showChinese='${el.showChinese}'` : ''
+    return addPropToTag(tag, showChinese)
+  },
+  'el-divider': function (el) {
+    const { tag } = attrBuilder(el)
+    const contentPosition = `content-position="${el['content-position']}"`
+    const child = el.__slot__.default
+    return `<${tag} ${contentPosition}>${child}</${tag}>`
+  },
+  'JnpfUploadImg': function (el) {
+    const { tag, vModel, disabled } = attrBuilder(el)
+    const accept = `accept="${el.accept}"`
+    const fileSize = `:fileSize="${el.fileSize}"`
+    const limit = `:limit="${el.limit}"`
+    const showTip = `:showTip="${el.showTip}"`
+    const sizeUnit = `sizeUnit="${el.sizeUnit}"`
+    return `<${tag} ${vModel} ${accept} ${fileSize} ${limit} ${showTip} ${sizeUnit} ${disabled}></${tag}>`
+  },
+  'JnpfUploadFile': function (el) {
+    const tag = this['JnpfUploadImg'](el)
+    const buttonText = `buttonText="${el.buttonText}"`
+    return addPropToTag(tag, buttonText)
+  },
+  'JNPF-Quill': function (el) {
+    const { tag, vModel, placeholder } = attrBuilder(el)
+    return `<${tag} ${vModel} ${placeholder}></${tag}>`
+  },
+  'com-select': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    return `<${tag} ${vModel} ${placeholder} ${disabled}></${tag}>`
+  },
+  'dep-select': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    return `<${tag} ${vModel} ${placeholder} ${disabled}></${tag}>`
+  },
+  'pos-select': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    const multiple = `:multiple="${el.multiple}"`
+    return `<${tag} ${vModel} ${placeholder} ${multiple} ${disabled}></${tag}>`
+  },
+  'user-select': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    const multiple = `:multiple="${el.multiple}"`
+    return `<${tag} ${vModel} ${placeholder} ${multiple} ${disabled}></${tag}>`
+  },
+  'dic-select': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    return `<${tag} ${vModel} ${placeholder} ${disabled}></${tag}>`
+  },
+  'JNPF-Address': function (el) {
+    const { tag, vModel, placeholder, disabled } = attrBuilder(el)
+    const level = `:multiple="${el.level}"`
+    return `<${tag} ${vModel} ${placeholder} ${level} ${disabled}></${tag}>`
+  },
+}
+const addPropToTag = (tag, ...props) => {
+  const insetIndex = tag.search(/>\s*<\//)
+
+  if (insetIndex > -1) {
+    return tag.slice(0, insetIndex) + props.join(' ') + tag.slice(insetIndex)
+  } else {
+    return tag
+  }
+}
+
+function attrBuilder(el) {
+  return {
+    tag: el.__config__.tag,
+    vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
+    clearable: el.clearable ? 'clearable' : '',
+    placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
+    width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
+    disabled: el.disabled ? ':disabled=\'true\'' : ''
+  }
+}
+
+// el-buttin 子级
+function buildElButtonChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__ || {}
+  if (slot.default) {
+    children.push(slot.default)
+  }
+  return children.join('\n')
+}
+
+// el-input 子级
+function buildElInputChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__
+  if (slot && slot.prepend) {
+    children.push(`<template slot="prepend">${slot.prepend}</template>`)
+  }
+  if (slot && slot.append) {
+    children.push(`<template slot="append">${slot.append}</template>`)
+  }
+  return children.join('\n')
+}
+
+// el-select 子级
+function buildElSelectChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__
+  if (slot && slot.options && slot.options.length) {
+    children.push(`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
+  }
+  return children.join('\n')
+}
+
+// el-radio-group 子级
+function buildElRadioGroupChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__
+  const config = scheme.__config__
+  if (slot && slot.options && slot.options.length) {
+    const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio'
+    const border = config.border ? 'border' : ''
+    children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+  }
+  return children.join('\n')
+}
+
+// el-checkbox-group 子级
+function buildElCheckboxGroupChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__
+  const config = scheme.__config__
+  if (slot && slot.options && slot.options.length) {
+    const tag = config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
+    const border = config.border ? 'border' : ''
+    children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+  }
+  return children.join('\n')
+}
+
+// el-upload 子级
+function buildElUploadChild(scheme) {
+  const list = []
+  const config = scheme.__config__
+  if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
+  else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`)
+  if (config.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件</div>`)
+  return list.join('\n')
+}
+
+/**
+ * 组装html代码。【入口函数】
+ * @param {Object} formConfig 整个表单配置
+ * @param {String} type 生成类型,文件或弹窗等
+ */
+export function makeUpHtml(formConfig, type) {
+  const htmlList = []
+  confGlobal = formConfig
+  // 判断布局是否都沾满了24个栅格,以备后续简化代码结构
+  someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24)
+  // 遍历渲染每个组件成html
+  formConfig.fields.forEach(el => {
+    htmlList.push(layouts[el.__config__.layout](el))
+  })
+  const htmlStr = htmlList.join('\n')
+  // 将组件代码放进form标签
+  let temp = buildFormTemplate(formConfig, htmlStr, type)
+  // dialog标签包裹代码
+  if (type === 'dialog') {
+    temp = dialogWrapper(temp)
+  }
+  confGlobal = null
+  return temp
+}

+ 405 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/itsmConfig.js

@@ -0,0 +1,405 @@
+// 基础控件 【左面板】
+export const itsmBasicComponents = [
+  {
+    __config__: {
+      jnpfKey: "custom-input",
+      label: "单行输入",
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-input",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    placeholder: "请输入",
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-textarea",
+      label: "多行输入",
+      showLabel: true,
+      tag: "JnpfTextarea",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    placeholder: "请输入",
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-number",
+      label: "数字输入",
+      showLabel: true,
+      tag: "JnpfInputNumber",
+      tagIcon: "icon-ym icon-ym-generator-number",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    placeholder: "请输入",
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-radio",
+      label: "单选框组",
+      showLabel: true,
+      tag: "JnpfRadio",
+      tagIcon: "icon-ym icon-ym-generator-radio",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)  
+      fieldApi: null, // 选择项数据源  api/process/itsm/findOrganizeList  583948307240322117
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    disabled: false,
+    optionType: "default",
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-checkbox",
+      label: "多选框组",
+      showLabel: true,
+      tag: "JnpfCheckbox",
+      tagIcon: "icon-ym icon-ym-generator-checkbox",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: [],// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)  
+      fieldApi: null, // 选择项数据源  api/process/itsm/findOrganizeList  583948307240322117
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    disabled: false,
+    optionType: "default",
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-select",
+      label: "下拉选择",
+      showLabel: true,
+      tag: "JnpfSelect",
+      tagIcon: "icon-ym icon-ym-generator-select",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: [],// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)  
+      fieldApi: null, // 选择项数据源  api/process/itsm/findOrganizeList  583948307240322117
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    disabled: false,
+    multiple: false
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-group",
+      label: "下拉组选择",
+      showLabel: true,
+      tag: "JnpfSelect",
+      tagIcon: "icon-ym icon-ym-generator-cascader",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: [],// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)  
+      fieldApi: null, // 选择项数据源  api/process/itsm/findOrganizeList  583948307240322117
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    props: {
+      label: "fullName",
+      value: "id"
+    },
+    options: [
+      {
+        fullName: "选项一",
+        id: "1"
+      },
+      {
+        fullName: "选项二",
+        id: "2"
+      }
+    ],
+    disabled: false,
+    multiple: false
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-datePicker",
+      label: "日期选择",
+      showLabel: true,
+      tag: "JnpfDatePicker",
+      tagIcon: "icon-ym icon-ym-generator-date",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-timePicker",
+      label: "日期时间选择",
+      showLabel: true,
+      tag: "JnpfTimePicker",
+      tagIcon: "icon-ym icon-ym-generator-time",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-richText",
+      label: "富文本",
+      showLabel: true,
+      tag: "JnpfEditor",
+      tagIcon: "icon-ym icon-ym-generator-rich-text",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    disabled: false, // 是否不可编辑
+    placeholder: "请输入"
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-member",
+      label: "成员选择",
+      showLabel: true,
+      tag: "JnpfInput",
+      tagIcon: "icon-ym icon-ym-generator-user",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    disabled: false, // 是否不可编辑
+    placeholder: "请输入"
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-uploadFile",
+      label: "文件上传",
+      showLabel: true,
+      tag: "JnpfUploadFile",
+      tagIcon: "icon-ym icon-ym-generator-upload",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: [],// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+      formId: null,
+      renderKey: null,
+    },
+    disabled: false, // 是否不可编辑
+    placeholder: "请输入",
+    pathType: "defaultPath",
+    fileSize: null,//文件大小限制
+    sizeUnit: null,//文件大小单位
+    limit: null,//文件个数限制
+    moduleFileId: null,//文件附件ID
+    moduleFileName: null,//文件附件名称
+  },
+  {
+    __config__: {
+      jnpfKey: "custom-text",
+      label: "文本提示",
+      showLabel: true,
+      tag: "JnpfAlert",
+      tagIcon: "icon-ym icon-ym-generator-alert",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "colFormItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: '这是一条文本提示',// 默认值
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    disabled: false, // 是否不可编辑
+    placeholder: "请输入",
+    title: '这是一条文本提示',
+  },
+];
+
+//布局控件 【左面板】
+export const itsmLayoutComponents = [
+  {
+    __config__: {
+      jnpfKey: "custom-card",
+      label: "模块容器",
+      tag: "el-card",
+      tagIcon: "icon-ym icon-ym-generator-card",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "rowFormItem",
+      span: "24",
+      dragDisabled: false,
+      children: []
+    },
+    header: "模块容器",
+    isNew: true
+  },
+]
+
+// 高级控件 【左面板】
+export const itsmSelectComponents = [
+  {
+    __config__: {
+      jnpfKey: "custom-table",
+      tag: "CustomTable",
+      tagIcon: "icon-ym icon-ym-generator-popupTableSelect",
+      label: "表格",
+      required: false,
+      noShow: false,
+      isDisplay: true,
+      showLabel: true,
+      tableAlign: "left",
+      tableFixed: "none",
+      layout: "rowFormItem",
+      span: 24,
+      dragDisabled: false,
+      children: [],
+    },
+    disabled: false
+  },
+];

+ 256 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/js.js

@@ -0,0 +1,256 @@
+import { exportDefault, titleCase, deepClone } from '@/components/Generator/utils'
+import ruleTrigger from './ruleTrigger'
+
+const units = {
+  KB: '1024',
+  MB: '1024 / 1024',
+  GB: '1024 / 1024 / 1024'
+}
+let confGlobal
+const inheritAttrs = {
+  file: '',
+  dialog: 'inheritAttrs: false,'
+}
+
+/**
+ * 组装js 【入口函数】
+ * @param {Object} formConfig 整个表单配置
+ * @param {String} type 生成类型,文件或弹窗等
+ */
+export function makeUpJs(formConfig, type) {
+  confGlobal = formConfig = deepClone(formConfig)
+  const dataList = []
+  const ruleList = []
+  const optionsList = []
+  const propsList = []
+  const methodList = mixinMethod(type)
+  const uploadVarList = []
+
+  formConfig.fields.forEach(el => {
+    buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+  })
+
+  const script = buildexport(
+    formConfig,
+    type,
+    dataList.join('\n'),
+    ruleList.join('\n'),
+    optionsList.join('\n'),
+    uploadVarList.join('\n'),
+    propsList.join('\n'),
+    methodList.join('\n')
+  )
+  confGlobal = null
+  return script
+}
+
+// 构建组件属性
+function buildAttributes(scheme, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) {
+  const config = scheme.__config__
+  const slot = scheme.__slot__
+  buildData(scheme, dataList)
+  buildRules(scheme, ruleList)
+
+  // 特殊处理options属性
+  if (scheme.options || (slot && slot.options && slot.options.length)) {
+    buildOptions(scheme, optionsList)
+    if (config.dataType === 'dynamic') {
+      const model = `${scheme.__vModel__}Options`
+      const options = titleCase(model)
+      buildOptionMethod(`get${options}`, model, methodList)
+    }
+  }
+
+  // 处理props
+  if (scheme.props && scheme.props) {
+    buildProps(scheme, propsList)
+  }
+
+  // 处理el-upload的action
+  if (scheme.action && config.tag === 'el-upload') {
+    uploadVarList.push(
+      `${scheme.__vModel__}Action: '${scheme.action}',
+      ${scheme.__vModel__}fileList: [],`
+    )
+    methodList.push(buildBeforeUpload(scheme))
+    // 非自动上传时,生成手动上传的函数
+    if (!scheme['auto-upload']) {
+      methodList.push(buildSubmitUpload(scheme))
+    }
+  }
+
+  // 构建子级组件属性
+  if (config.children) {
+    config.children.forEach(item => {
+      buildAttributes(item, dataList, ruleList, optionsList, methodList, propsList, uploadVarList)
+    })
+  }
+}
+
+// 混入处理函数
+function mixinMethod(type) {
+  const list = [];
+  const
+    minxins = {
+      file: confGlobal.formBtns ? {
+        submitForm: `submitForm() {
+        this.$refs['${confGlobal.formRef}'].validate(valid => {
+          if(!valid) return
+          // TODO 提交表单
+        })
+      },`,
+        resetForm: `resetForm() {
+        this.$refs['${confGlobal.formRef}'].resetFields()
+      },`
+      } : null,
+      dialog: {
+        onOpen: 'onOpen() {},',
+        onClose: `onClose() {
+        this.$refs['${confGlobal.formRef}'].resetFields()
+      },`,
+        close: `close() {
+        this.$emit('update:visible', false)
+      },`,
+        handelConfirm: `handelConfirm() {
+        this.$refs['${confGlobal.formRef}'].validate(valid => {
+          if(!valid) return
+          this.close()
+        })
+      },`
+      }
+    }
+
+  const methods = minxins[type]
+  if (methods) {
+    Object.keys(methods).forEach(key => {
+      list.push(methods[key])
+    })
+  }
+
+  return list
+}
+
+// 构建data
+function buildData(scheme, dataList) {
+  const config = scheme.__config__
+  if (scheme.__vModel__ === undefined) return
+  const defaultValue = JSON.stringify(config.defaultValue)
+  dataList.push(`${scheme.__vModel__}: ${defaultValue},`)
+}
+
+// 构建校验规则
+function buildRules(scheme, ruleList) {
+  const config = scheme.__config__
+  if (scheme.__vModel__ === undefined) return
+  const rules = []
+  if (ruleTrigger[config.tag]) {
+    if (config.required) {
+      const type = Array.isArray(config.defaultValue) ? 'type: \'array\',' : ''
+      let message = Array.isArray(config.defaultValue) ? `请至少选择一个${config.label}` : scheme.placeholder
+      if (message === undefined) message = `${config.label}不能为空`
+      rules.push(`{ required: true, ${type} message: '${message}', trigger: '${ruleTrigger[config.tag]}' }`)
+    }
+    if (config.regList && Array.isArray(config.regList)) {
+      config.regList.forEach(item => {
+        if (item.pattern) {
+          rules.push(
+            `{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${ruleTrigger[config.tag]}' }`
+          )
+        }
+      })
+    }
+    ruleList.push(`${scheme.__vModel__}: [${rules.join(',')}],`)
+  }
+}
+
+// 构建options
+function buildOptions(scheme, optionsList) {
+  if (scheme.__vModel__ === undefined) return
+  // el-cascader直接有options属性,其他组件都是定义在slot中,所以有两处判断
+  let { options } = scheme
+  if (!options) options = scheme.__slot__.options
+  if (scheme.__config__.dataType === 'dynamic') { options = [] }
+  const str = `${scheme.__vModel__}Options: ${JSON.stringify(options)},`
+  optionsList.push(str)
+}
+
+function buildProps(scheme, propsList) {
+  const str = `${scheme.__vModel__}Props: ${JSON.stringify(scheme.props)},`
+  propsList.push(str)
+}
+
+// el-upload的BeforeUpload
+function buildBeforeUpload(scheme) {
+  const config = scheme.__config__
+  const unitNum = units[config.sizeUnit];
+  let rightSizeCode = '';
+  let acceptCode = '';
+  const
+    returnList = []
+  if (config.fileSize) {
+    rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${config.fileSize}
+    if(!isRightSize){
+      this.$message.error('文件大小超过 ${config.fileSize}${config.sizeUnit}')
+    }`
+    returnList.push('isRightSize')
+  }
+  if (scheme.accept) {
+    acceptCode = `let isAccept = new RegExp('${scheme.accept}').test(file.type)
+    if(!isAccept){
+      this.$message.error('应该选择${scheme.accept}类型的文件')
+    }`
+    returnList.push('isAccept')
+  }
+  const str = `${scheme.__vModel__}BeforeUpload(file) {
+    ${rightSizeCode}
+    ${acceptCode}
+    return ${returnList.join('&&')}
+  },`
+  return returnList.length ? str : ''
+}
+
+// el-upload的submit
+function buildSubmitUpload(scheme) {
+  const str = `submitUpload() {
+    this.$refs['${scheme.__vModel__}'].submit()
+  },`
+  return str
+}
+
+function buildOptionMethod(methodName, model, methodList) {
+  const str = `${methodName}() {
+    // TODO 发起请求获取数据
+    this.${model}
+  },`
+  methodList.push(str)
+}
+
+// js整体拼接
+function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) {
+  const str = `${exportDefault}{
+  ${inheritAttrs[type]}
+  components: {},
+  props: [],
+  data () {
+    return {
+      ${conf.formModel}: {
+        ${data}
+      },
+      ${conf.formRules}: {
+        ${rules}
+      },
+      ${uploadVar}
+      ${selectOptions}
+      ${props}
+    }
+  },
+  computed: {},
+  watch: {},
+  created () {},
+  mounted () {},
+  methods: {
+    ${methods}
+  }
+}`
+  return str
+}

+ 27 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/generator/ruleTrigger.js

@@ -0,0 +1,27 @@
+/**
+ * 用于生成表单校验,指定正则规则的触发方式。
+ * 未在此处声明无触发方式的组件将不生成rule!!
+ */
+export default {
+  'el-input': 'blur',
+  'el-input-number': 'blur',
+  'el-select': 'change',
+  'el-radio-group': 'change',
+  'el-checkbox-group': 'change',
+  'el-cascader': 'change',
+  'el-time-picker': 'change',
+  'el-date-picker': 'change',
+  'el-rate': 'change',
+  'el-color-picker': 'change',
+  'JNPF-Quill': 'blur',
+  "JNPF-Text": 'blur',
+  "JNPF-Amount": 'change',
+  "JnpfUploadFile": 'change',
+  "JnpfUploadImg": 'change',
+  "com-select": 'change',
+  "dep-select": 'change',
+  "pos-select": 'change',
+  "user-select": 'change',
+  "dic-select": 'change',
+  "JNPF-Address": 'change',
+}

+ 510 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/DraggableItem.vue

@@ -0,0 +1,510 @@
+<script>
+import draggable from 'vuedraggable'
+import render from '@/components/Generator/render/render'
+import { dyOptionsList } from '@/components/Generator/generator/comConfig'
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+import { getDataInterfaceRes } from '@/api/systemData/dataInterface'
+
+let activeData = {}
+const components = {
+    itemBtns(h, element, index, parent) {
+        const gutter = element.__config__.layout === 'colFormItem' && this.formConf.gutter ? this.formConf.gutter : 15
+        const rightDistance = gutter / 2
+        const { copyItem, deleteItem } = this.$listeners
+        return [
+            <span class="drawing-item-copy" style={{ '--rightDistance': rightDistance + 50 + 'px' }} title="复制" onClick={event => {
+                copyItem(element, parent); event.stopPropagation()
+            }}>
+                <i class="el-icon-copy-document" />
+            </span>,
+            <el-popconfirm title="确定删除该组件?" class="drawing-item-delete" style={{ '--rightDistance': rightDistance + 18 + 'px' }} onConfirm={event => {
+                deleteItem(index, parent)
+            }}>
+                <span title="删除" slot="reference" style="width:100%;height:100%;display:inline-block" >
+                    <i class="el-icon-delete" />
+                </span>
+            </el-popconfirm>
+        ]
+    },
+    tableSetting(h, element) {
+        const { addRow, addCol } = this.$listeners
+        return [
+            <span class="drawing-item-add-row" title="插入行" onClick={event => {
+                addRow(element); event.stopPropagation()
+            }}>
+                <i class="icon-ym icon-ym-generator-insertRow" />
+            </span>,
+            <span class="drawing-item-add-col" title="插入列" onClick={event => {
+                addCol(element); event.stopPropagation()
+            }}>
+                <i class="icon-ym icon-ym-generator-insertCol" />
+            </span>
+        ]
+    },
+    cellSetting(h, element, rowIndex, colIndex) {
+        const { handleTableSetting, handleShowMenu } = this.$listeners
+        const {
+            mergeLeftColDisabled,
+            mergeRightColDisabled,
+            mergeWholeRowDisabled,
+            mergeAboveRowDisabled,
+            mergeBelowRowDisabled,
+            mergeWholeColDisabled,
+            undoMergeRowDisabled,
+            undoMergeColDisabled,
+            deleteWholeColDisabled,
+            deleteWholeRowDisabled
+        } = this.$attrs
+        return [
+            <span class="drawing-item-cell" >
+                <el-dropdown trigger="click" onCommand={event => { handleTableSetting(event, element) }}
+                    on-visible-change={event => { if (event) handleShowMenu(element, rowIndex, colIndex) }} >
+                    <i class="icon-ym icon-ym-generator-TableSettings" />
+                    <el-dropdown-menu slot="dropdown">
+                        <el-dropdown-item command="1">插入左侧列</el-dropdown-item>
+                        <el-dropdown-item command="2">插入右侧列</el-dropdown-item>
+                        <el-dropdown-item command="3">插入上方行</el-dropdown-item>
+                        <el-dropdown-item command="4">插入下方行</el-dropdown-item>
+                        <el-dropdown-item command="5" disabled={mergeLeftColDisabled} divided>向左合并</el-dropdown-item>
+                        <el-dropdown-item command="6" disabled={mergeRightColDisabled}>向右合并</el-dropdown-item>
+                        <el-dropdown-item command="7" disabled={mergeWholeRowDisabled}>合并整行</el-dropdown-item>
+                        <el-dropdown-item command="8" disabled={mergeAboveRowDisabled} divided>向上合并</el-dropdown-item>
+                        <el-dropdown-item command="9" disabled={mergeBelowRowDisabled}>向下合并</el-dropdown-item>
+                        <el-dropdown-item command="10" disabled={mergeWholeColDisabled}>合并整列</el-dropdown-item>
+                        <el-dropdown-item command="11" disabled={undoMergeRowDisabled} divided>撤销行合并</el-dropdown-item>
+                        <el-dropdown-item command="12" disabled={undoMergeColDisabled}>撤销列合并</el-dropdown-item>
+                        <el-dropdown-item command="13" disabled={deleteWholeColDisabled} divided>删除整列</el-dropdown-item>
+                        <el-dropdown-item command="14" disabled={deleteWholeRowDisabled}>删除整行</el-dropdown-item>
+                    </el-dropdown-menu>
+                </el-dropdown>
+            </span >
+        ]
+    }
+}
+
+const layouts = {
+    //问卷的头部特殊组件
+    questionTopItem(h, element, index, parent) {
+        const { activeItem } = this.$listeners
+        const config = element.__config__
+        let className = this.activeId === config.formId ? 'drawing-item active-from-item top-item' : 'drawing-item top-item'
+
+        let Item = <render key={config.renderKey} conf={element} onInput={event => { this.$set(config, 'defaultValue', event) }} />
+        return (
+            <el-col span={config.span} class={className}
+                nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                <jnpf-form-tip-item label-width="0">
+                    {Item}
+                </jnpf-form-tip-item>
+            </el-col>
+        )
+    },
+    //问卷内部组件
+    questionItem(h, element, index, parent) {
+        const { activeItem } = this.$listeners
+        const config = element.__config__
+        let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item top-item'
+        let labelWidth = `5px`
+        let label = config.label ? config.parentVModel ? config.label : config.label + (this.formConf.labelSuffix || '') : ''
+        let Item = <render key={config.renderKey} index={index} conf={element} onInput={event => { this.$set(config, 'defaultValue', event) }} />
+        return (
+            <el-col span={config.span} class={className}
+                nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                <jnpf-form-tip-item label-width={labelWidth} tip-label={config.label && config.showLabel ? config.tipLabel : ''}
+                    label={config.showLabel ? label : ''} required={config.required}>
+                    {Item}
+                </jnpf-form-tip-item>
+                {components.itemBtns.apply(this, arguments)}
+            </el-col>
+        )
+    },
+    colFormItem(h, element, index, parent) {
+        const { activeItem } = this.$listeners
+        const config = element.__config__
+        let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
+        if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
+        let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
+        let label = config.label ? config.parentVModel ? config.label : config.label + (this.formConf.labelSuffix || '') : ''
+        if (config.showLabel === false) labelWidth = '0'
+        let Item = config.jnpfKey === 'cascader'
+            ? <JnpfCascader props={{ props: element.props }} v-model={config.defaultValue} placeholder={element.placeholder} options={element.options} multiple={element.multiple}
+                disabled={element.disabled} show-all-levels={element.showAllLevels} separator={element.separator}
+                style={element.style} clearable={element.clearable} filterable={element.filterable} key={config.renderKey}></JnpfCascader>
+            : <render key={config.renderKey} conf={element} onInput={event => { this.$set(config, 'defaultValue', event) }} />
+
+        if (!element.__config__.isDisplay && element.__config__.jnpfKey.includes('custom')) {
+            return
+        }
+
+        return (
+            <el-col span={config.span} class={className}
+                nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                <jnpf-form-tip-item label-width={labelWidth} tip-label={config.label && config.showLabel ? config.tipLabel : ''}
+                    label={config.showLabel ? label : ''} required={config.required}>
+                    {Item}
+                </jnpf-form-tip-item>
+                {components.itemBtns.apply(this, arguments)}
+            </el-col>
+        )
+    },
+    rowFormItem(h, element, index, parent) {
+        const { activeItem } = this.$listeners
+        const { put, end } = this.$attrs
+        const className = this.activeId === element.__config__.formId
+            ? 'drawing-row-item active-from-item'
+            : 'drawing-row-item'
+        if (element.__config__.jnpfKey === 'tab') {
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <el-tabs type={element.type} tab-position={element.tabPosition} vModel={element.__config__.active}>
+                            {
+                                element.__config__.children.map((item, i) => {
+                                    let child = renderChildren.apply(this, [h, item, i, element])
+                                    let childgroup = { name: 'componentsGroup', put: (...arg) => put(...arg, item) }
+                                    const onChildEnd = (...arg) => end(...arg, activeData, item)
+                                    let tip = ''
+                                    if (!item.__config__.children.length) {
+                                        tip = <div class="table-tip tab-tip">请将组件拖到此区域(可拖多个组件)</div>
+                                    }
+                                    return (
+                                        <el-tab-pane key={item.name} name={item.name} label={item.title} >
+                                            <el-col >
+                                                {tip}
+                                                <el-row gutter={element.__config__.gutter} style="padding-top:15px">
+                                                    <draggable list={item.__config__.children} animation={340} group={childgroup} onEnd={onChildEnd} class="drag-wrapper">
+                                                        {child}
+                                                    </draggable>
+                                                </el-row>
+                                            </el-col>
+                                        </el-tab-pane>
+                                    )
+                                })
+                            }
+                        </el-tabs>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+        if (element.__config__.jnpfKey === 'collapse') {
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <el-collapse vModel={element.__config__.active} accordion={element.accordion}>
+                            {
+                                element.__config__.children.map((item, i) => {
+                                    let child = renderChildren.apply(this, [h, item, i, element])
+                                    let childgroup = { name: 'componentsGroup', put: (...arg) => put(...arg, item) }
+                                    const onChildEnd = (...arg) => end(...arg, activeData, item)
+                                    let tip = ''
+                                    if (!item.__config__.children.length) {
+                                        tip = <div class="table-tip card-tip">请将组件拖到此区域(可拖多个组件)</div>
+                                    }
+                                    return (
+                                        <el-collapse-item key={item.name} title={item.title} name={item.name} >
+                                            <el-col style="position:relative;padding-top:15px">
+                                                {tip}
+                                                <el-row gutter={element.__config__.gutter} >
+                                                    <draggable list={item.__config__.children} animation={340} group={childgroup} onEnd={onChildEnd} class="drag-wrapper">
+                                                        {child}
+                                                    </draggable>
+                                                </el-row>
+                                            </el-col>
+                                        </el-collapse-item>
+                                    )
+                                })
+                            }
+                        </el-collapse>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+        if (element.__config__.jnpfKey === 'tableGrid') {
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <table class="table-grid">
+                            <tbody>
+                                {
+                                    element.__config__.children.map((item, rowIndex) => {
+                                        return (
+                                            <tr>
+                                                {
+                                                    item.__config__.children.map((it, colIndex) => {
+                                                        let child = renderChildren.apply(this, [h, it, colIndex, item])
+                                                        let childGroup = { name: 'componentsGroup', put: (...arg) => put(...arg, it) }
+                                                        const onChildEnd = (...arg) => end(...arg, activeData, it)
+                                                        const childClassName = this.activeId === it.__config__.formId ? 'drawing-row-item active-from-item' : 'drawing-row-item'
+                                                        return !it.__config__.merged ? (
+                                                            <td class={childClassName} colspan={it.__config__.colspan || 1} rowspan={it.__config__.rowspan || 1} onClick={event => { activeItem(it); event.stopPropagation() }}>
+                                                                <el-col>
+                                                                    <el-row gutter={element.__config__.gutter} >
+                                                                        <draggable list={it.__config__.children} animation={340} group={childGroup} onEnd={onChildEnd} class='table-cell'>
+                                                                            {child}
+                                                                        </draggable>
+                                                                    </el-row>
+                                                                </el-col>
+                                                                {components.cellSetting.apply(this, [h, element, rowIndex, colIndex])}
+                                                            </td>
+                                                        ) : ''
+                                                    })
+                                                }
+                                            </tr>
+                                        )
+                                    })
+                                }
+                            </tbody>
+                        </table>
+                        {components.tableSetting.apply(this, arguments)}
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+        let child = renderChildren.apply(this, arguments)
+        const group = { name: 'componentsGroup', put: (...arg) => put(...arg, element) }
+        const onEnd = (...arg) => end(...arg, activeData, element)
+        if (element.__config__.jnpfKey === 'row') {
+            if (element.type === 'flex') {
+                child = <el-row type={element.type} justify={element.justify} align={element.align}>
+                    {child}
+                </el-row>
+            }
+            let tip = ''
+            if (!element.__config__.children.length) {
+                tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+            }
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <span class="component-name" >{element.__config__.componentName} </span>
+                        {tip}
+                        <draggable list={element.__config__.children} animation={340} group={group} onEnd={onEnd} class="drag-wrapper" style="margin-top:20px">
+                            {child}
+                        </draggable>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+        if (element.__config__.jnpfKey === 'table') {
+            let tip = ''
+            if (!element.__config__.children.length) {
+                tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+            }
+            let toolTip = element.__config__.label
+            if (element.__config__.tipLabel && element.__config__.label) {
+                toolTip = <span slot="label">{element.__config__.label}
+                    <el-tooltip placement="top" content={element.__config__.tipLabel}>
+                        <a class='el-icon-question tooltip-question content-name'></a>
+                    </el-tooltip>
+                </span >
+            }
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className + ' drawing-row-item-table'}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <span class="component-name" >{toolTip} </span>
+                        {tip}
+                        <el-form label-position="top">
+                            <draggable list={element.__config__.children} animation={340} group={group} class="drag-wrapper table-wrapper table-wrapper-web" onEnd={onEnd} clone={cloneComponent}>
+                                {child}
+                            </draggable>
+                        </el-form>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+        if (element.__config__.jnpfKey === 'card') {
+            let tip = ''
+            if (!element.__config__.children.length) {
+                tip = <div class="table-tip card-tip">请将组件拖到此区域(可拖多个组件)</div>
+            }
+            let toolTip = element.header
+            if (element.__config__.tipLabel && element.__config__.label) {
+                toolTip = <span slot="label">{element.header}
+                    <el-tooltip placement="top" content={element.__config__.tipLabel}>
+                        <a class='el-icon-question tooltip-question content-name'></a>
+                    </el-tooltip>
+                </span >
+            }
+            let header = ''
+            if (element.header) {
+                header = <div slot="header" ><span>{toolTip}</span></div>
+            }
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <el-card shadow={element.shadow} header={element.header}>
+                            {header}
+                            <el-col>
+                                <el-row gutter={element.__config__.gutter}
+                                    nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                                    {tip}
+                                    <draggable list={element.__config__.children} animation={340} group={group} onEnd={onEnd} class="drag-wrapper">
+                                        {child}
+                                    </draggable>
+                                </el-row>
+                            </el-col>
+                        </el-card>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+
+
+        //自定义模块容器
+        if (element.__config__.jnpfKey === 'custom-card') {
+
+
+            // 判断模块内是否有可提交字段,没有就不渲染
+            let flag = element.__config__.children.some(item => {
+                return item.__config__.isDisplay
+            })
+            if (!flag && !element.isNew) {
+                return
+            }
+
+            let tip = ''
+            if (!element.__config__.children.length) {
+                tip = <div class="table-tip card-tip">请将组件拖到此区域(可拖多个组件)</div>
+            }
+            let toolTip = element.header
+            if (element.__config__.tipLabel && element.__config__.label) {
+                toolTip = <span slot="label">{element.header}
+                    <el-tooltip placement="top" content={element.__config__.tipLabel}>
+                        <a class='el-icon-question tooltip-question content-name'></a>
+                    </el-tooltip>
+                </span >
+            }
+            let header = ''
+            if (element.header) {
+                header = <div slot="header" ><span>{toolTip}</span></div>
+            }
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <el-card shadow={element.shadow} header={element.header}>
+                            {header}
+                            <el-col>
+                                <el-row gutter={element.__config__.gutter}
+                                    nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                                    {tip}
+                                    <draggable list={element.__config__.children} animation={340} group={group} onEnd={onEnd} class="custom-card drag-wrapper">
+                                        {child}
+                                    </draggable>
+                                </el-row>
+                            </el-col>
+                        </el-card>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+
+        if (element.__config__.jnpfKey === 'custom-table') {
+            let tip = ''
+            if (!element.__config__.children.length) {
+                tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+            }
+            let toolTip = element.__config__.label
+            if (element.__config__.tipLabel && element.__config__.label) {
+                toolTip = <span slot="label">{element.__config__.label}
+                    <el-tooltip placement="top" content={element.__config__.tipLabel}>
+                        <a class='el-icon-question tooltip-question content-name'></a>
+                    </el-tooltip>
+                </span >
+            }
+            return (
+                <el-col span={element.__config__.span}>
+                    <el-row gutter={element.__config__.gutter} class={className + ' drawing-row-item-table'}
+                        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+                        <span class="component-name" >{toolTip} </span>
+                        {tip}
+                        <el-form label-position="top">
+                            <draggable list={element.__config__.children} animation={340} group={group} class="drag-wrapper custom-table-wrapper table-wrapper-web" onEnd={onEnd} clone={cloneComponent}>
+                                {child}
+                            </draggable>
+                        </el-form>
+                        {components.itemBtns.apply(this, arguments)}
+                    </el-row>
+                </el-col>
+            )
+        }
+    }
+}
+function cloneComponent(origin) {
+    activeData = origin
+}
+
+function renderChildren(h, element, index, parent) {
+    const config = element.__config__
+    if (!Array.isArray(config.children)) return null
+    return config.children.map((el, i) => {
+        const layout = layouts[el.__config__.layout]
+        if (layout) {
+            return layout.call(this, h, el, i, config.children)
+        }
+        return layoutIsNotFound.call(this)
+    })
+}
+
+function layoutIsNotFound() {
+    throw new Error(`没有与${this.element.__config__.layout}匹配的layout`)
+}
+
+function buildOptions(element) {
+    const config = element.__config__
+    if (dyOptionsList.indexOf(config.jnpfKey) > -1) {
+        if (config.dataType === 'dictionary' && config.dictionaryType) {
+            getDictionaryDataSelector(config.dictionaryType).then(res => {
+                element.options = res.data.list
+            })
+        }
+        if (config.dataType === 'dynamic' && config.propsUrl) {
+            getDataInterfaceRes(config.propsUrl).then(res => {
+                element.options = Array.isArray(res.data) ? res.data : []
+            })
+        }
+    }
+    if (config.children && Array.isArray(config.children)) {
+        for (let i = 0; i < config.children.length; i++) {
+            buildOptions(config.children[i])
+        }
+    }
+}
+
+export default {
+    components: {
+        render,
+        draggable
+    },
+    props: [
+        'element',
+        'index',
+        'drawingList',
+        'activeId',
+        'formConf'
+    ],
+    render(h) {
+        // buildOptions(this.element)
+        const layout = layouts[this.element.__config__.layout]
+
+
+        if (layout) {
+            return layout.call(this, h, this.element, this.index, this.drawingList)
+        }
+        return layoutIsNotFound.call(this)
+    }
+}
+</script>

+ 347 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/DraggableItemApp.vue

@@ -0,0 +1,347 @@
+<script>
+import draggable from 'vuedraggable'
+import render from '@/components/Generator/render/render'
+
+let activeData = {}
+const components = {
+  itemBtns(h, element, index, parent) {
+    const gutter = element.__config__.layout === 'colFormItem' && this.formConf.gutter ? this.formConf.gutter : 15
+    const rightDistance = gutter / 2
+    const { copyItem, deleteItem } = this.$listeners
+    return [
+      <span class="drawing-item-copy" style={{ '--rightDistance': rightDistance + 50 + 'px' }} title="复制" onClick={event => {
+        copyItem(element, parent); event.stopPropagation()
+      }}>
+        <i class="el-icon-copy-document" />
+      </span>,
+      <el-popconfirm title="确定删除该组件?" style={{ '--rightDistance': rightDistance + 18 + 'px' }} class="drawing-item-delete" onConfirm={event => {
+        deleteItem(index, parent)
+      }}>
+        <span title="删除" slot="reference" style="width:100%;height:100%;display:inline-block">
+          <i class="el-icon-delete" />
+        </span>
+      </el-popconfirm>
+    ]
+  }
+}
+const layouts = {
+  colFormItem(h, element, index, parent) {
+    const { activeItem } = this.$listeners
+    const config = element.__config__
+    const __vModel__ = element.__vModel__
+    let className = this.activeId === config.formId ? 'drawing-item active-from-item' : 'drawing-item'
+    if (this.formConf.unFocusedComponentBorder) className += ' unfocus-bordered'
+    let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
+    let label = config.label ? config.parentVModel ? config.label : config.label + (this.formConf.labelSuffix || '') : ''
+    if (config.showLabel === false) labelWidth = '0'
+    if (config.jnpfKey === 'text') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width={labelWidth}
+            label={config.showLabel ? label : ''} required={config.required}>
+            {element.content}
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'link') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width="0">
+            <jnpf-link content={element.content} href={element.href} target={element.target}
+              textStyle={element.textStyle} />
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'alert') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width="0">
+            <el-alert title={element.title} type={element.type} closable={element.closable}
+              show-icon={element.showIcon} />
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'groupTitle') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width="0">{element.content}</el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'barcode') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width={labelWidth}
+            label={config.showLabel ? config.label : ''} required={config.required}>
+            <jnpf-barcode format={element.format} lineColor={element.lineColor} background={element.background} width={element.width} height={element.height} staticText={element.staticText}></jnpf-barcode>
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'qrcode') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width={labelWidth}
+            label={config.showLabel ? label : ''} required={config.required}>
+            <jnpf-qrcode size={element.size} colorLight={element.colorLight} colorDark={element.colorDark} staticText={element.staticText}></jnpf-qrcode>
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    if (config.jnpfKey === 'button') {
+      return (
+        <el-col span={24} class={className}
+          nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+          <el-form-item label-width="0">
+            <jnpf-button align={element.align} buttonText={element.buttonText} type={element.type} disabled={element.disabled}></jnpf-button>
+          </el-form-item>
+          {components.itemBtns.apply(this, arguments)}
+        </el-col>
+      )
+    }
+    return (
+      <el-col span={24} class={className}
+        nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+        <jnpf-form-tip-item label-width={labelWidth} tip-label={config.label && config.showLabel ? config.tipLabel : ''}
+          label={config.showLabel ? label : ''} required={config.required}>
+          {__vModel__}
+        </jnpf-form-tip-item>
+        {components.itemBtns.apply(this, arguments)}
+      </el-col>
+    )
+  },
+  rowFormItem(h, element, index, parent) {
+    const { activeItem } = this.$listeners
+    const { put, end } = this.$attrs
+    const className = this.activeId === element.__config__.formId
+      ? 'drawing-row-item active-from-item'
+      : 'drawing-row-item'
+    if (element.__config__.jnpfKey === 'tab') {
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className}
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <el-tabs type={element.type} tab-position={element.tabPosition} vModel={element.__config__.active}>
+              {
+                element.__config__.children.map((item, i) => {
+                  let child = renderChildren.apply(this, [h, item, i, element])
+                  let childgroup = { name: 'componentsGroup', put: (...arg) => put(...arg, item) }
+                  const onChildEnd = (...arg) => end(...arg, activeData, item)
+                  let tip = ''
+                  if (!item.__config__.children.length) {
+                    tip = <div class="table-tip tab-tip">请将组件拖到此区域(可拖多个组件)</div>
+                  }
+                  return (
+                    <el-tab-pane key={item.name} name={item.name} label={item.title} >
+                      <el-col >
+                        {tip}
+                        <el-row gutter={element.__config__.gutter} style="padding-top:15px">
+                          <draggable list={item.__config__.children} animation={340} group={childgroup} onEnd={onChildEnd} class="drag-wrapper">
+                            {child}
+                          </draggable>
+                        </el-row>
+                      </el-col>
+                    </el-tab-pane>
+                  )
+                })
+              }
+            </el-tabs>
+            {components.itemBtns.apply(this, arguments)}
+          </el-row>
+        </el-col>
+      )
+    }
+    if (element.__config__.jnpfKey === 'collapse') {
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className}
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <el-collapse vModel={element.__config__.active} accordion={element.accordion}>
+              {
+                element.__config__.children.map((item, i) => {
+                  let child = renderChildren.apply(this, [h, item, i, element])
+                  let childgroup = { name: 'componentsGroup', put: (...arg) => put(...arg, item) }
+                  const onChildEnd = (...arg) => end(...arg, activeData, item)
+                  let tip = ''
+                  if (!item.__config__.children.length) {
+                    tip = <div class="table-tip card-tip">请将组件拖到此区域(可拖多个组件)</div>
+                  }
+                  return (
+                    <el-collapse-item key={item.name} title={item.title} name={item.name} >
+                      <el-col style="position:relative">
+                        {tip}
+                        <el-row gutter={element.__config__.gutter} style="padding-top:15px">
+                          <draggable list={item.__config__.children} animation={340} group={childgroup} onEnd={onChildEnd} class="drag-wrapper">
+                            {child}
+                          </draggable>
+                        </el-row>
+                      </el-col>
+                    </el-collapse-item>
+                  )
+                })
+              }
+            </el-collapse>
+            {components.itemBtns.apply(this, arguments)}
+          </el-row>
+        </el-col>
+      )
+    }
+    if (element.__config__.jnpfKey === 'tableGrid') {
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className} style="padding-top:30px"
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <span class="component-name">{element.__config__.label}</span>
+            <div class=" drag-wrapper table-wrapper">
+              <div class="table-tip">请在桌面端设计表格(移动端不支持)</div>
+            </div>
+            {components.itemBtns.apply(this, arguments)}
+          </el-row>
+        </el-col>
+      )
+    }
+    let child = renderChildren.apply(this, arguments)
+    const group = { name: 'componentsGroup', put: (...arg) => put(...arg, element) }
+    const onEnd = (...arg) => end(...arg, activeData, element)
+    if (element.__config__.jnpfKey === 'row') {
+      if (element.type === 'flex') {
+        child = <el-row type={element.type} justify={element.justify} align={element.align}>
+          {child}
+        </el-row>
+      }
+      let tip = ''
+      if (!element.__config__.children.length) {
+        tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+      }
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className} style="padding-top:30px"
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <span class="component-name">{element.__config__.componentName}</span>
+            {tip}
+            <draggable list={element.__config__.children} animation={340} group={group} onEnd={onEnd} class="drag-wrapper">
+              {child}
+            </draggable>
+            {components.itemBtns.apply(this, arguments)}
+          </el-row>
+        </el-col>
+      )
+    }
+    if (element.__config__.jnpfKey === 'card') {
+      let tip = ''
+      if (!element.__config__.children.length) {
+        tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+      }
+      let toolTip = element.header
+      if (element.__config__.tipLabel) {
+        toolTip = <span slot="label">{element.header}
+          <el-tooltip placement="top" content={element.__config__.tipLabel}>
+            <a class='el-icon-question tooltip-question content-name'></a>
+          </el-tooltip>
+        </span >
+      }
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className} style="padding-top:30px"
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <span class="component-name">{toolTip}</span>
+            {tip}
+            <draggable list={element.__config__.children} animation={340} group={group} onEnd={onEnd} class="drag-wrapper">
+              {child}
+            </draggable>
+            {components.itemBtns.apply(this, arguments)}
+          </el-row>
+        </el-col>
+      )
+    }
+    if (element.__config__.jnpfKey === 'table') {
+      let tip = ''
+      if (!element.__config__.children.length) {
+        tip = <div class="table-tip">请将组件拖到此区域(可拖多个组件)</div>
+      }
+      let toolTip = element.__config__.label
+      if (element.__config__.tipLabel) {
+        toolTip = <span slot="label">{element.__config__.label}
+          <el-tooltip placement="top" content={element.__config__.tipLabel}>
+            <a class='el-icon-question tooltip-question content-name'></a>
+          </el-tooltip>
+        </span >
+      }
+      return (
+        <el-col span={24}>
+          <el-row gutter={element.__config__.gutter} class={className} style="padding-top:30px"
+            nativeOnClick={event => { activeItem(element); event.stopPropagation() }}>
+            <span class="component-name">{toolTip}</span>
+            {tip}
+            <draggable list={element.__config__.children} animation={340} group={group} class="drag-wrapper table-wrapper" onEnd={onEnd} clone={cloneComponent}>
+              {child}
+            </draggable>
+            {components.itemBtns.apply(this, arguments)}
+            {
+              // <div style="text-align: center;background: white;color: #4e79ff;padding: .4rem 1rem;">
+              //   <i class="el-icon-plus"></i> {element.actionText}
+              // </div>
+            }
+          </el-row>
+        </el-col>
+      )
+    }
+  }
+}
+function cloneComponent(origin) {
+  activeData = origin
+}
+
+function renderChildren(h, element, index, parent) {
+  const config = element.__config__
+  if (!Array.isArray(config.children)) return null
+  return config.children.map((el, i) => {
+    const layout = layouts[el.__config__.layout]
+    if (layout) {
+      return layout.call(this, h, el, i, config.children)
+    }
+    return layoutIsNotFound.call(this)
+  })
+}
+
+function layoutIsNotFound() {
+  throw new Error(`没有与${this.element.__config__.layout}匹配的layout`)
+}
+
+export default {
+  components: {
+    render,
+    draggable
+  },
+  props: [
+    'element',
+    'index',
+    'drawingList',
+    'activeId',
+    'formConf'
+  ],
+  render(h) {
+    const layout = layouts[this.element.__config__.layout]
+
+    if (layout) {
+      return layout.call(this, h, this.element, this.index, this.drawingList)
+    }
+    return layoutIsNotFound.call(this)
+  }
+}
+</script>

+ 223 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/FieldDialog.vue

@@ -0,0 +1,223 @@
+<template>
+  <el-dialog title="字段设置" :close-on-click-modal="false" width="1000px"
+    class="JNPF-dialog JNPF-dialog_center field-dialog" lock-scroll append-to-body
+    :visible.sync="visible">
+    <div class="main">
+      <JNPF-table v-loading="listLoading" :data="list">
+        <el-table-column prop="field" label="列名">
+          <template slot-scope="scope">
+            <span class="table-cell" v-if="scope.row.disabled">{{scope.row.field}}</span>
+            <el-input v-model="scope.row.field" placeholder="请输入列名" maxlength="50" v-else />
+          </template>
+        </el-table-column>
+        <el-table-column prop="dataType" label="类型">
+          <template slot-scope="scope">
+            <span class="table-cell"
+              v-if="scope.row.disabled">{{scope.row.dataType|dataType}}</span>
+            <el-select v-model="scope.row.dataType" placeholder="请选择" filterable v-else>
+              <el-option v-for="item in options" :key="item.value" :label="item.label"
+                :value="item.value" />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column prop="dataLength" label="长度">
+          <template slot-scope="scope">
+            <span class="table-cell" v-if="scope.row.disabled">{{scope.row.dataLength}}</span>
+            <el-input v-model="scope.row.dataLength" placeholder="请输入长度" v-else
+              :disabled="scope.row.dataType!=='varchar'&&scope.row.dataType!=='decimal'" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="allowNull" label="允许空" width="60" align="center">
+          <template slot-scope="scope">
+            <el-checkbox :value='!!scope.row.allowNull' v-if="scope.row.disabled" />
+            <el-checkbox v-model="scope.row.allowNull" :true-label="1" :false-label="0" v-else />
+          </template>
+        </el-table-column>
+        <el-table-column prop="fieldName" label="说明">
+          <template slot-scope="scope">
+            <span class="table-cell" v-if="scope.row.disabled">{{scope.row.fieldName}}</span>
+            <el-input v-model="scope.row.fieldName" placeholder="请输入说明" v-else />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="50">
+          <template slot-scope="scope">
+            <el-button class="JNPF-table-delBtn" size="mini" type="text" v-if="!scope.row.disabled"
+              @click="handleDel(scope.$index)">删除
+            </el-button>
+          </template>
+        </el-table-column>
+      </JNPF-table>
+      <div class="table-actions" @click="addHandle()">
+        <el-button type="text" icon="el-icon-plus">新建</el-button>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="closeDialog">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="dataFormSubmit()" :loading="btnLoading">
+        {{$t('common.confirmButton')}}</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+import { DataModelInfo, addTableFields } from '@/api/systemData/dataModel'
+export default {
+  data() {
+    return {
+      visible: false,
+      listLoading: true,
+      btnLoading: false,
+      options: [
+        { label: '字符串', value: 'varchar' },
+        { label: '整型', value: 'int' },
+        { label: '日期时间', value: 'datetime' },
+        { label: '浮点', value: 'decimal' },
+        { label: '长整型', value: 'bigint' },
+        { label: '文本', value: 'text' }
+      ],
+      list: [],
+      dataForm: {},
+      dataBase: '0'
+    }
+  },
+  methods: {
+    init(dataBase, table) {
+      this.visible = true
+      this.dataBase = dataBase
+      if (!dataBase || !table) return
+      this.initData(table)
+    },
+    closeDialog() {
+      this.$emit('close')
+      this.visible = false
+    },
+    initData(table) {
+      this.listLoading = true
+      DataModelInfo(this.dataBase, table).then(res => {
+        this.dataForm = res.data.tableInfo
+        this.$set(this.dataForm, 'newTable', this.dataForm.table)
+        this.list = res.data.tableFieldList.map((o, i) => ({ disabled: true, ...o }))
+        this.listLoading = false
+      })
+    },
+    dataFormSubmit() {
+      let tableFieldList = this.list.filter(o => !o.disabled)
+      if (!tableFieldList.length) {
+        this.$message({
+          message: `请至少新增一个字段`,
+          type: 'error',
+          duration: 1000
+        });
+        return
+      }
+      if (!this.exist()) return
+      this.btnLoading = true
+      let query = {
+        tableFieldList,
+        tableInfo: this.dataForm
+      }
+      addTableFields(this.dataBase, query).then((res) => {
+        this.$message({
+          message: res.msg,
+          type: 'success',
+          duration: 1500,
+          onClose: () => {
+            this.visible = false
+            this.btnLoading = false
+            this.$emit('updateOptions', this.list)
+            this.$emit('close')
+          }
+        })
+      }).catch(() => { this.btnLoading = false })
+    },
+    exist() {
+      let isOk = true;
+      //  遍历数组,判断非空
+      for (let i = 0; i < this.list.length; i++) {
+        const e = this.list[i];
+        if (e.disabled) continue
+        if (!e.field) {
+          this.$message({
+            message: `第${i + 1}行列名不能为空`,
+            type: 'error',
+            duration: 1000
+          });
+          isOk = false
+          break
+        }
+        let reg = /(^_([a-zA-Z0-9]_?)*$)|(^[a-zA-Z](_?[a-zA-Z0-9])*_?$)/
+        if (!reg.test(e.field)) {
+          this.$message({
+            message: `第${i + 1}行列名格式错误,请重新输入`,
+            type: 'error',
+            duration: 1000
+          });
+          isOk = false
+          break
+        }
+        let num = this.list.filter(o => o.field == e.field)
+        if (num.length > 1) {
+          this.$message({
+            message: `第${i + 1}行列名'${e.field}'已重复`,
+            type: 'error',
+            duration: 1000
+          });
+          isOk = false
+          break
+        }
+        if (!e.fieldName) {
+          this.$message({
+            message: `第${i + 1}行说明不能为空`,
+            type: 'error',
+            duration: 1000
+          });
+          isOk = false
+          break
+        }
+      }
+      return isOk;
+    },
+    handleDel(index) {
+      this.list.splice(index, 1)
+    },
+    addHandle(row) {
+      let item = {}
+      if (!row) {
+        item = {
+          field: "", dataType: "varchar", dataLength: 50, allowNull: 1, primaryKey: 0, fieldName: "", disabled: false
+        }
+      } else {
+        item = {
+          field: row.field,
+          dataType: row.dataType,
+          dataLength: row.dataLength,
+          allowNull: row.allowNull,
+          fieldName: row.fieldName,
+          primaryKey: 0,
+          disabled: false
+        }
+      }
+      this.list.push(item)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.field-dialog {
+  >>> .el-dialog__body {
+    height: 70vh;
+    padding: 0 !important;
+    .main {
+      height: 100%;
+      display: flex;
+      flex-direction: column;
+      overflow: hidden;
+    }
+    .el-table {
+      border-top: 0;
+    }
+    .table-cell {
+      font-size: 13px;
+    }
+  }
+}
+</style>

+ 121 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/FormScript.vue

@@ -0,0 +1,121 @@
+<template>
+  <el-dialog :close-on-click-modal="false" class="JNPF-dialog JNPF-dialog_center form-script-dialog"
+    lock-scroll append-to-body v-bind="$attrs" width="1100px" :modal-append-to-body="false"
+    v-on="$listeners" @open="onOpen">
+    <span slot="title" class="dialog-title">
+      <span>表单脚本
+        <el-tooltip content="小程序不支持在线JS脚本" placement="top-start">
+          <a class="el-icon-question tooltip-question"></a>
+        </el-tooltip>
+      </span>
+    </span>
+    <div class="form-script-dialog-body">
+      <div class="left-tree">
+        <el-tree :data="treeData" default-expand-all :expand-on-click-node="false"
+          :props="treeProps" @node-click="handleNodeClick"></el-tree>
+      </div>
+      <div class="right-main">
+        <div class="codeEditor">
+          <JNPFCodeEditor v-model="text" :options="options" ref="CodeEditor" />
+        </div>
+        <div class="tips">
+          <p>请从左侧面板选择的字段名,支持JavaScript的脚本,
+            <ScriptDemo :type="type" />
+          </p>
+        </div>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="closeDialog">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="onClose()">{{$t('common.confirmButton')}}</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import JNPFCodeEditor from '@/components/JNPFEditor/monaco'
+import ScriptDemo from './ScriptDemo';
+export default {
+  components: { JNPFCodeEditor, ScriptDemo },
+  props: ['tpl', 'fields', 'type'],
+  inject: ["getFormInfo"],
+  data() {
+    return {
+      text: '',
+      treeData: [],
+      treeProps: {
+        children: 'children',
+        label: 'label',
+      },
+      treeData: [],
+      options: {
+        language: 'javascript'
+      },
+    }
+  },
+  methods: {
+    onOpen() {
+      this.text = this.tpl
+      this.getTree()
+      this.$nextTick(() => {
+        this.$refs.CodeEditor.changeEditor({
+          value: this.text,
+          options: this.options
+        })
+      });
+    },
+    getTree() {
+      let list = []
+      const loop = (data, parent) => {
+        if (!data) return
+        if (data.__config__ && data.__config__.jnpfKey !== 'table' && data.__config__.children && Array.isArray(data.__config__.children)) {
+          loop(data.__config__.children, data)
+        }
+        if (Array.isArray(data)) data.forEach(d => loop(d, parent))
+        if (data.__vModel__) {
+          if (data.__config__.jnpfKey === 'table') {
+            let item = {
+              value: data.__vModel__,
+              label: data.__config__.label,
+              children: []
+            }
+            let children = []
+            if (data.__config__.children && Array.isArray(data.__config__.children) && data.__config__.children.length) {
+              for (let i = 0; i < data.__config__.children.length; i++) {
+                const child = data.__config__.children[i]
+                if (child.__vModel__) {
+                  children.push({ value: data.__vModel__ + '.' + child.__vModel__, label: child.__config__.label })
+                }
+              }
+            }
+            item.children = children
+            list.push(item)
+          } else {
+            list.push({ value: data.__vModel__, label: data.__config__.label })
+          }
+        }
+      }
+      loop(this.fields)
+      let topItem = {
+        value: +new Date(),
+        label: this.getFormInfo().fullName,
+        top: true,
+        children: list
+      }
+      this.treeData = [topItem]
+    },
+    onClose() {
+      this.$emit('updateScript', this.text)
+      this.closeDialog()
+    },
+    closeDialog() {
+      this.$emit('update:visible', false)
+    },
+    handleNodeClick(item, node) {
+      if (item.top) return
+      if (item.children) return
+      this.$refs.CodeEditor.insert(item.value);
+    }
+  }
+}
+</script>

+ 1158 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/Home.vue

@@ -0,0 +1,1158 @@
+<template>
+    <div class="container">
+        <div class="left-board" v-if="!isQuestionnaire">
+            <el-scrollbar class="left-scrollbar">
+                <div class="components-list">
+                    <div v-for="(item, listIndex) in leftComponents" :key="listIndex"
+                        class="components-part">
+                        <div class="components-title">{{ item.title }}</div>
+                        <draggable class="components-draggable" :list="item.list"
+                            :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
+                            :clone="cloneComponent" draggable=".components-item" filter=".disabled"
+                            :sort="false" @end="onEnd">
+                            <div v-for="(element, index) in item.list" :key="index"
+                                class="components-item"
+                                :class="{'disabled':element.__config__.dragDisabled}"
+                                @click="addComponent(element)">
+                                <div class="components-body">
+                                    <i :class="element.__config__.tagIcon" />
+                                    {{ element.__config__.label }}
+                                </div>
+                            </div>
+                        </draggable>
+                    </div>
+                </div>
+            </el-scrollbar>
+        </div>
+
+        <div class="custom-left-board" v-if="isQuestionnaire">
+            <div class="tab">
+                <el-tabs v-model="currentTab">
+                    <el-tab-pane label="控件列表" name="component" />
+                </el-tabs>
+            </div>
+            <template v-if="currentTab == 'component'">
+                <el-scrollbar class="left-scrollbar">
+                    <div class="components-list">
+                        <div v-for="(item, listIndex) in leftComponents" :key="listIndex"
+                            class="components-part">
+                            <div class="components-title">{{ item.title }}</div>
+                            <draggable class="components-draggable" :list="item.list"
+                                :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
+                                :clone="cloneComponent" draggable=".components-item"
+                                filter=".disabled" :sort="false" @end="onEnd">
+                                <div v-for="(element, index) in item.list" :key="index"
+                                    class="components-item"
+                                    :class="{'disabled':element.__config__.dragDisabled}"
+                                    @click="addComponent(element)">
+                                    <div class="components-body">
+                                        <i :class="element.__config__.tagIcon" />
+                                        {{ element.__config__.label }}
+                                    </div>
+                                </div>
+                            </draggable>
+                        </div>
+                    </div>
+                </el-scrollbar>
+            </template>
+
+        </div>
+        <div class="center-board">
+            <div class="action-bar">
+                <div class="action-bar-right">
+                    <template v-if="!isQuestionnaire">
+                        <el-button icon="icon-ym icon-ym-pc"
+                            :class="{'unActive-btn':showType!='pc'}" type="text"
+                            @click="showType='pc'" size="medium">桌面端</el-button>
+                        <el-button icon="icon-ym icon-ym-mobile"
+                            :class="{'unActive-btn':showType!='app'}" type="text"
+                            @click="showType='app'" size="medium">移动端</el-button>
+                        <el-button class="unActive-btn" icon="el-icon-video-play" type="text"
+                            @click="preview" size="medium">预览</el-button>
+                        <!-- <el-button icon="el-icon-view" type="text" @click="showJson" size="medium">
+            查看json</el-button> -->
+                    </template>
+
+                    <el-button class="delete-btn unActive-btn" icon="icon-ym icon-ym-clean"
+                        type="text" @click="empty" size="medium">重置</el-button>
+                </div>
+            </div>
+            <el-scrollbar class="center-scrollbar" v-show="showType==='pc'">
+                <el-row class="center-board-row" :gutter="formConf.gutter">
+                    <el-form :size="formConf.size" :label-position="formConf.labelPosition"
+                        :disabled="formConf.disabled" :label-width="formConf.labelWidth + 'px'">
+                        <draggable class="drawing-board" :list="drawingList" :animation="340"
+                            group="componentsGroup" @end='onCenterEnd' @start="onCenterStart"
+                            filter=".top-item">
+                            <draggable-item v-for="(element, index) in drawingList"
+                                :key="element.renderKey" :drawing-list="drawingList"
+                                :element="element" :index="index" :active-id="activeId"
+                                :form-conf="formConf" :mergeLeftColDisabled="mergeLeftColDisabled"
+                                :mergeRightColDisabled="mergeRightColDisabled"
+                                :mergeWholeRowDisabled="mergeWholeRowDisabled"
+                                :mergeAboveRowDisabled="mergeAboveRowDisabled"
+                                :mergeBelowRowDisabled="mergeBelowRowDisabled"
+                                :mergeWholeColDisabled="mergeWholeColDisabled"
+                                :undoMergeRowDisabled="undoMergeRowDisabled"
+                                :undoMergeColDisabled="undoMergeColDisabled"
+                                :deleteWholeColDisabled="deleteWholeColDisabled"
+                                :deleteWholeRowDisabled="deleteWholeRowDisabled"
+                                @activeItem="activeFormItem" @copyItem="drawingItemCopy"
+                                @deleteItem="drawingItemDelete" @addRow="handleTableAddRow"
+                                @addCol="handleTableAddCol" @handleTableSetting="handleTableSetting"
+                                @handleShowMenu="handleShowMenu" :put="shouldClone"
+                                :end='onTableEnd' />
+                        </draggable>
+                        <div v-show="!drawingList.length" class="empty-info">
+                            <img src="@/assets/images/emptyElement.png" alt="" class="empty-img">
+                        </div>
+                    </el-form>
+                </el-row>
+            </el-scrollbar>
+        </div>
+        <right-panel :active-data="activeData" :form-conf="formConf"
+            :show-field="!!drawingList.length" @tag-change="tagChange" :modelType="modelType"
+            :webType="webType" :formInfo='getFormInfo' @relationChange="relationChange"
+            :isQuestionnaire="isQuestionnaire" :drawingList="drawingList" @setVModel="setVModel"
+            @copyItem="drawingItemCopy" />
+        <json-drawer size="550px" :visible.sync="jsonDrawerVisible" :jsonData="formData"
+            @refresh="refreshJson" />
+        <Preview :visible.sync="previewVisible" :form-data="formData" />
+    </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+import { debounce } from 'throttle-debounce'
+import render from '@/components/Generator/render/render'
+import JsonDrawer from './JsonDrawer'
+import RightPanel from './RightPanel'
+import Preview from '../preview'
+import {
+    inputComponents, selectComponents, systemComponents, layoutComponents, formConf
+} from '@/components/Generator/generator/config'
+
+
+//引入政务云独有组件
+import {
+    questionnaireinputComponents, questionnaireselectComponents
+} from './questionnaireComponentConfig'
+
+import { noVModelList, noTableAllowList, calculateItem, onlinePeculiarList } from '@/components/Generator/generator/comConfig'
+import {
+    exportDefault, beautifierConf, isNumberStr, titleCase, deepClone
+} from '@/components/Generator/utils'
+import drawingDefalut from '@/components/Generator/generator/drawingDefalut'
+import DraggableItem from './DraggableItem'
+import DraggableItemApp from './DraggableItemApp'
+import {
+    getDrawingList, saveDrawingList, getIdGlobal, saveIdGlobal, getFormConf
+} from '@/components/Generator/utils/db'
+import { validURL, isUrl } from '@/utils/validate'
+import object from 'element-resize-detector/src/detection-strategy/object'
+
+const emptyActiveData = { style: {}, autosize: {} }
+let oldActiveId
+let tempActiveData
+const idGlobal = getIdGlobal()
+
+export default {
+    components: {
+        draggable,
+        render,
+        JsonDrawer,
+        RightPanel,
+        DraggableItem,
+        DraggableItemApp,
+        Preview
+    },
+    // props: [{}'conf', 'modelType', 'webType', 'dbType', 'formInfo', 'isQuestionnaire',],
+    props: {
+        conf: {
+            type: Object,
+            deep: true
+        },
+        modelType: {
+        },
+        webType: {
+        },
+        dbType: {
+        },
+        formInfo: {
+        },
+        isQuestionnaire: {
+        },
+        itsmDrawingList: {
+        },
+    },
+    data() {
+        return {
+            idGlobal: 100,
+            formConf: deepClone(formConf),
+            inputComponents,
+            selectComponents,
+            systemComponents,
+            layoutComponents,
+            labelWidth: 100,
+            drawingList: [],
+            drawingData: {},
+            activeId: null,
+            drawerVisible: false,
+            previewVisible: false,
+            formData: {},
+            dialogVisible: false,
+            jsonDrawerVisible: false,
+            generateConf: null,
+            showFileName: false,
+            activeData: {},
+            saveDrawingListDebounce: debounce(340, saveDrawingList),
+            saveIdGlobalDebounce: debounce(340, saveIdGlobal),
+            isDrawingListChange: true,
+            showTip: true,
+            activeItem: {},
+            activeTableItem: {},
+            showType: 'pc',
+            leftComponents: [
+                {
+                    title: '基础控件',
+                    list: inputComponents
+                },
+                {
+                    title: '高级控件',
+                    list: selectComponents
+                },
+                {
+                    title: '系统控件',
+                    list: systemComponents
+                },
+                {
+                    title: '布局控件',
+                    list: layoutComponents
+                }
+            ],
+            rowIndex: 0,
+            colIndex: 0,
+            rowData: [],
+            colData: [],
+            selectCell: {
+                __config__: {
+                    rowspan: 1,
+                    colspan: 1
+                }
+            },
+            multipleSelection: [], // 存储选中的行,
+            currentTab: 'component',
+            centerKey: null,
+            oldDrawingList: null
+        }
+    },
+    provide() {
+        return {
+            getShowType: () => this.showType
+        }
+    },
+    computed: {
+        mergeLeftColDisabled() {
+            if (!this.colData.length) return true
+            return (this.colIndex <= 0) || (this.colData[this.colIndex - 1].__config__.rowspan !== this.selectCell.__config__.rowspan)
+        },
+        mergeRightColDisabled() {
+            if (!this.colData.length) return true
+            let rightColIndex = this.colIndex + this.selectCell.__config__.colspan
+            return (this.colIndex >= this.colData.length - 1) || (rightColIndex > this.colData.length - 1)
+                || (this.colData[rightColIndex].__config__.rowspan !== this.selectCell.__config__.rowspan)
+        },
+        mergeWholeRowDisabled() {
+            if (!this.selectCell.__config__ || !this.rowData.length) return true
+            let rowDataChildren = this.rowData[this.rowIndex].__config__.children
+            let startRowspan = rowDataChildren[0].__config__.rowspan
+            let unmatchedFlag = false
+            for (let i = 1; i < rowDataChildren.length; i++) {
+                if (rowDataChildren[i].__config__.rowspan !== startRowspan) {
+                    unmatchedFlag = true
+                    break;
+                }
+            }
+            if (unmatchedFlag) return true
+            return (this.colData.length <= 1) || (this.colData.length === this.selectCell.__config__.colspan)
+        },
+        mergeAboveRowDisabled() {
+            if (!this.rowData.length || this.rowIndex <= 0) return true
+            return (this.rowData[this.rowIndex - 1].__config__.children[this.colIndex].__config__.colspan
+                !== this.selectCell.__config__.colspan) || this.rowData[this.rowIndex - 1].__config__.children[this.colIndex].__config__.merged
+        },
+        mergeBelowRowDisabled() {
+            if (!this.rowData.length || (this.rowIndex == this.rowData.length)) return true
+            let belowRowIndex = this.rowIndex + this.selectCell.__config__.rowspan
+            return (this.rowIndex >= this.rowData.length - 1) || (belowRowIndex > this.rowData.length - 1)
+                || (this.rowData[belowRowIndex].__config__.children[this.colIndex].__config__.colspan !== this.selectCell.__config__.colspan)
+                || this.rowData[belowRowIndex].__config__.children[this.colIndex].__config__.merged
+        },
+        mergeWholeColDisabled() {
+            if (!this.rowData.length) return true
+            let startColspan = this.rowData[0].__config__.children[this.colIndex].__config__.colspan
+            let unmatchedFlag = false
+            for (let i = 1; i < this.rowData.length; i++) {
+                if (this.rowData[i].__config__.children[this.colIndex].__config__.colspan !== startColspan) {
+                    unmatchedFlag = true
+                    break;
+                }
+            }
+            if (unmatchedFlag) return true
+            return (this.rowData.length <= 1) || (this.rowData.length === this.selectCell.__config__.rowspan)
+        },
+        undoMergeRowDisabled() {
+            return this.selectCell.__config__.merged || (this.selectCell.__config__.colspan <= 1)
+        },
+        undoMergeColDisabled() {
+            return this.selectCell.__config__.merged || (this.selectCell.__config__.rowspan <= 1)
+        },
+        deleteWholeColDisabled() {
+            if (!this.rowData.length) return true
+            if (this.rowData[0].__config__.children[0].__config__.colspan === this.rowData[0].__config__.children.length) return true
+            let startColspan = this.rowData[0].__config__.children[this.colIndex].__config__.colspan
+            let unmatchedFlag = false
+            for (let i = 1; i < this.rowData.length; i++) {
+                if (this.rowData[i].__config__.children[this.colIndex].__config__.colspan !== startColspan) {
+                    unmatchedFlag = true
+                    break;
+                }
+            }
+            if (unmatchedFlag) return true
+            return (this.selectCell.__config__.colspan === this.colData.length)
+        },
+        deleteWholeRowDisabled() {
+            if (!this.rowData.length || this.rowData.length <= this.rowIndex) return true
+            if (this.rowData[0].__config__.children[0].__config__.rowspan === this.rowData.length) return true
+            //整行所有单元格行高不一致不可删除!!
+            let startRowspan = this.rowData[this.rowIndex].__config__.children[0].__config__.rowspan
+            let unmatchedFlag = false
+            for (let i = 1; i < this.rowData[this.rowIndex].__config__.children.length; i++) {
+                if (this.rowData[this.rowIndex].__config__.children[i].__config__.rowspan !== startRowspan) {
+                    unmatchedFlag = true
+                    break;
+                }
+            }
+            if (unmatchedFlag) return true
+            return (this.rowData.length === 1) || (this.selectCell.__config__.rowspan === this.rowData.length)
+        },
+        getFormInfo() {
+            return this.formInfo || {}
+        }
+    },
+    watch: {
+        // eslint-disable-next-line func-names
+        'activeData.__config__.label': function (val, oldVal) {
+            // if (
+            //   this.activeData.placeholder === undefined
+            //   || !this.activeData.__config__.tag
+            //   || oldActiveId !== this.activeId
+            // ) {
+            //   return
+            // }
+            // this.activeData.placeholder = this.activeData.placeholder.replace(oldVal, '') + val
+        },
+        activeId: {
+            handler(val) {
+                oldActiveId = val
+            },
+            immediate: true
+        },
+        drawingList: {
+            handler(val) {
+                this.saveDrawingListDebounce(val)
+                if (val.length === 0) {
+                    this.idGlobal = 100
+                    this.activeData = {}
+                }
+
+                if (this.isQuestionnaire) {
+                    this.$emit('drawingListChange', this.drawingList)
+                }
+
+            },
+            deep: true
+        },
+        idGlobal: {
+            handler(val) {
+                this.formConf.idGlobal = val
+                // this.saveIdGlobalDebounce(val)
+            },
+            immediate: true
+        }
+    },
+    created() {
+        console.log(this.conf)
+        console.log(this.modelType)
+        console.log(this.webType)
+        console.log(this.dbType)
+        console.log(this.formInfo)
+        console.log(this.isQuestionnaire)
+
+
+
+        if (typeof this.conf === 'object' && this.conf !== null) {
+            this.isDrawingListChange = false
+            this.drawingList = deepClone(this.conf.fields)
+            Object.assign(this.formConf, this.conf)
+            this.idGlobal = this.formConf.idGlobal
+            if (this.drawingList.length) this.activeFormItem(this.drawingList[0])
+        } else {
+            this.drawingList = []
+            this.idGlobal = 100
+        }
+        if (this.modelType == 1 || this.modelType == 6) {
+            this.leftComponents[1].list = [...this.leftComponents[1].list, calculateItem]
+            this.leftComponents[0].list = [...this.leftComponents[0].list, ...onlinePeculiarList]
+        }
+        if (this.webType != 2 || this.modelType == 3 || this.modelType == 6) this.formConf.popupType = 'fullScreen'
+
+        if (this.isQuestionnaire) {
+            this.leftComponents = [
+                {
+                    title: '输入型',
+                    list: questionnaireinputComponents
+                },
+                {
+                    title: '选择型',
+                    list: questionnaireselectComponents
+                },
+                // {
+                //     title: '高级控件',
+                //     list: itsmSelectComponents
+                // }
+            ]
+
+            this.drawingList = this.itsmDrawingList
+        }
+    },
+    mounted() {
+        // fix: firefox 下 拖拽 会新打卡一个选项卡
+        // https://github.com/JakHuang/form-generator/issues/15
+        document.body.ondrop = event => {
+            event.preventDefault()
+            event.stopPropagation()
+        }
+    },
+    methods: {
+        handleSelectionChange(val) {
+            console.log(val)
+            this.multipleSelection = val; // 当表格的选项发生变化时,更新选中项数组
+        },
+        changeIsdisplay(type) {
+            this.multipleSelection.forEach(item => {
+                item.__config__.isDisplay = type
+            })
+            this.$message.success('设置成功')
+            this.$refs.multipleTableRef.clearSelection()
+        },
+
+        // 供父组件使用 获取表单JSON
+        getData() {
+            return new Promise((resolve, reject) => {
+                if (!this.drawingList.length) {
+                    reject({ msg: '表单不允许为空', target: 1 })
+                    return
+                }
+                const loop = list => {
+                    for (let i = 0; i < list.length; i++) {
+                        const e = list[i]
+                        const config = e.__config__
+                        if (config.layout === "colFormItem" && !noVModelList.includes(config.jnpfKey) && !e.__vModel__) {
+                            reject({ msg: `${config.label}的控件字段不能为空`, target: 1 })
+                            break
+                        }
+                        if (e.isStorage == 1 && !e.__vModel__) {
+                            reject({ msg: `${config.label}的控件字段不能为空`, target: 1 })
+                            break
+                        }
+                        if (config.jnpfKey === 'billRule') {
+                            if (!config.rule) {
+                                reject({ msg: '单据组件“单据模板”属性为必填项', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.jnpfKey === 'relationForm') {
+                            if (!e.modelId) {
+                                reject({ msg: '关联表单控件“关联功能”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.relationField) {
+                                reject({ msg: '关联表单控件“显示字段”属性为必填项', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.jnpfKey === 'popupSelect') {
+                            if (!e.interfaceId) {
+                                reject({ msg: '弹窗选择控件“远端数据”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.propsValue) {
+                                reject({ msg: '弹窗选择控件“储存字段”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.relationField) {
+                                reject({ msg: '弹窗选择控件“显示字段”属性为必填项', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.jnpfKey === 'popupTableSelect') {
+                            if (!e.interfaceId) {
+                                reject({ msg: '下拉表格控件“远端数据”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.propsValue) {
+                                reject({ msg: '下拉表格控件“储存字段”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.relationField) {
+                                reject({ msg: '下拉表格控件“显示字段”属性为必填项', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.jnpfKey === 'autoComplete') {
+                            if (!e.interfaceId) {
+                                reject({ msg: '下拉补全控件“远端数据”属性为必填项', target: 1 })
+                                break
+                            }
+                            if (!e.relationField) {
+                                reject({ msg: '下拉补全控件“显示字段”属性为必填项', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.layout === 'rowFormItem' && !config.children.length) {
+                            reject({ msg: `您的${config.label}控件中没有组件`, target: 1 })
+                            break
+                        }
+                        if (config && config.children && Array.isArray(config.children)) {
+                            loop(config.children)
+                        }
+                        if (config.jnpfKey === 'uploadFile') {
+                            if (e.pathType === 'selfPath') {
+                                if (e.isAccount === 0) {
+                                    if (!e.folder) {
+                                        reject({ msg: '请设置自定义路径', target: 1 })
+                                        break
+                                    }
+                                }
+                            }
+                        }
+                        if (config.jnpfKey === 'uploadImg') {
+                            if (e.pathType === 'selfPath') {
+                                if (e.isAccount === 0) {
+                                    if (!e.folder) {
+                                        reject({ msg: '请设置自定义路径', target: 1 })
+                                        break
+                                    }
+                                }
+                            }
+                        }
+                        if (config.jnpfKey === 'link') {
+                            if (e.href && !validURL(e.href)) {
+                                reject({ msg: '请输入正确的链接地址', target: 1 })
+                                break
+                            }
+                        }
+                        if (config.jnpfKey === 'iframe') {
+                            if (e.href && !isUrl(e.href)) {
+                                reject({ msg: '请输入正确的链接地址', target: 1 })
+                                break
+                            }
+                        }
+                    }
+                }
+                loop(this.drawingList)
+                this.AssembleFormData()
+                if (this.formData.hasPrintBtn && (!this.formData.printId || !this.formData.printId.length)) {
+                    reject({ msg: `请选择打印模板`, target: 1 })
+                }
+                resolve({ formData: this.formData, target: 1 })
+            })
+        },
+        //  阻止表格中嵌套行容器
+        shouldClone(to, from, target, event, conf) {
+            const targetConf = target._underlying_vm_
+            const isRowContainer = conf.__config__.cmpType === 'common' && conf.__config__.jnpfKey === 'row'
+            if (isRowContainer) return true
+            if (conf.cmpType === 'custom') return false
+            if (conf.__config__.jnpfKey === 'table') {
+                if (noTableAllowList.includes(targetConf.__config__.jnpfKey)) {
+                    // if (this.showTip) {
+                    //   this.$message.warning(`子表内暂不支持使用该组件`)
+                    //   this.showTip = false
+                    // }
+                    return false
+                }
+                if (targetConf.__config__.layout === 'rowFormItem') return false
+                if (this.$store.getters.hasTable) {
+                    if (!conf.__config__.tableName) {
+                        if (this.showTip) {
+                            this.$message.warning(`子表请先关联数据表`)
+                            this.showTip = false
+                        }
+                        return false
+                    }
+                }
+                this.activeItem = targetConf
+                this.activeTableItem = conf
+            }
+            if (conf.__config__.jnpfKey === 'tableGridTd' && targetConf.__config__.jnpfKey === 'tableGrid') return false
+            return true
+        },
+        activeFormItem(element) {
+            this.activeData = element
+            this.activeId = element.__config__.formId
+        },
+        onEnd(obj) {
+            console.log('onEnd')
+            console.log(obj)
+            console.log(tempActiveData)
+
+
+            // 增加组织向自定义表格内拖入不匹配移动端的组件
+            if (obj.to.className.indexOf('custom-table-wrapper') > -1) {
+                if (['custom-input', 'custom-select', 'custom-number'].includes(tempActiveData.__config__.jnpfKey)) {
+                } else {
+                    obj.to.__vue__.realList.splice(obj.newIndex, 1);
+                    this.$message({
+                        message: '暂不支持向自定义表格内拖入该控件',
+                        type: 'warning'
+                    });
+                }
+            }
+
+            // 增加组织向自定义表格内拖入不匹配移动端的组件
+            if (obj.to.className.indexOf('custom-card') > -1) {
+                if (['custom-card'].includes(tempActiveData.__config__.jnpfKey)) {
+                    obj.to.__vue__.realList.splice(obj.newIndex, 1);
+                    this.$message({
+                        message: '不支持向模板内拖入模板',
+                        type: 'warning'
+                    });
+                }
+            }
+
+
+            this.showTip = true
+            if (obj.from !== obj.to) {
+                this.activeData = tempActiveData
+                this.activeId = tempActiveData.__config__.formId
+            }
+            if (obj.to.className.indexOf('table-wrapper') > -1) {
+                this.$set(this.activeItem.__config__, 'isSubTable', true)
+                this.$set(this.activeItem.__config__, 'parentVModel', this.activeTableItem.__vModel__)
+                if (this.$store.getters.hasTable) {
+                    this.$set(this.activeItem.__config__, 'relationTable', this.activeTableItem.__config__.tableName)
+                    this.activeItem.__vModel__ = ''
+                }
+            }
+        },
+        onCenterStart(obj) {
+            console.log(obj)
+            this.centerKey = this.drawingList[obj.oldIndex].__config__.jnpfKey
+            this.oldDrawingList = JSON.parse(JSON.stringify(this.drawingList))
+        },
+        onCenterEnd(obj) {
+            console.log('obj')
+            console.log(obj)
+
+            // 问卷拖动不能拖到前两个
+            if (obj.newIndex < 2) {
+                this.drawingList = JSON.parse(JSON.stringify(this.oldDrawingList))
+                this.$message({
+                    message: '问卷标题和提示默认在前两个',
+                    type: 'warning'
+                });
+            }
+            // 增加组织向自定义表格内拖入不匹配移动端的组件
+            if (obj.to.className.indexOf('custom-card') > -1 && this.centerKey == 'custom-card') {
+                this.drawingList = JSON.parse(JSON.stringify(this.oldDrawingList))
+                this.$message({
+                    message: '不支持向模板内拖入模板',
+                    type: 'warning'
+                });
+            }
+
+            this.showTip = true
+            if (obj.from == obj.to) return
+            if (obj.to.className.indexOf('table-wrapper') > -1) {
+                this.$set(this.activeItem.__config__, 'isSubTable', true)
+                this.$set(this.activeItem.__config__, 'parentVModel', this.activeTableItem.__vModel__)
+                if (this.$store.getters.hasTable) {
+                    this.$set(this.activeItem.__config__, 'relationTable', this.activeTableItem.__config__.tableName)
+                    this.activeItem.__vModel__ = ''
+                }
+            }
+        },
+        onTableEnd(obj, target, conf) {
+            console.log('tableEnd')
+
+            console.log(obj)
+            if (obj.from == obj.to) return
+            if (obj.to.className.indexOf('table-wrapper') < 0) {
+                this.$set(this.activeItem.__config__, 'isSubTable', false)
+                this.$set(this.activeItem.__config__, 'parentVModel', '')
+                if (this.$store.getters.hasTable) this.activeItem.__vModel__ = ''
+            } else {
+                this.$set(this.activeItem.__config__, 'isSubTable', true)
+                this.$set(this.activeItem.__config__, 'parentVModel', this.activeTableItem.__vModel__)
+                if (this.$store.getters.hasTable) {
+                    this.$set(this.activeItem.__config__, 'relationTable', this.activeTableItem.__config__.tableName)
+                    this.activeItem.__vModel__ = ''
+                }
+            }
+        },
+        addComponent(item) {
+            if (item.__config__.dragDisabled) return
+
+            const clone = this.cloneComponent(item)
+
+
+            console.log(clone)
+            console.log(['question-radio', 'question-checkbox'].includes(clone.__config__.jnpfKey))
+
+            if (['question-radio', 'question-checkbox'].includes(clone.__config__.jnpfKey)) {
+                for (let i = 0; i < 2; i++) {
+                    const id = this.jnpf.idGenerator()
+                    clone.options.push({
+                        id: id,
+                        fullName: '选项' + id,
+                    })
+                }
+            }
+            this.drawingList.push(clone)
+            this.activeFormItem(clone)
+        },
+        cloneComponent(origin) {
+            const clone = deepClone(origin)
+            const config = clone.__config__
+            config.span = this.formConf.span // 生成代码时,会根据span做精简判断
+            this.createIdAndKey(clone)
+            // clone.placeholder !== undefined && (clone.placeholder += config.label)
+            tempActiveData = clone
+            return tempActiveData
+        },
+        createIdAndKey(item, parent) {
+            const config = item.__config__
+            config.formId = ++this.idGlobal
+            config.renderKey = +new Date() // 改变renderKey后可以实现强制更新组件
+            if (config.layout === 'colFormItem') {
+                if (!this.$store.getters.hasTable) {
+                    // 分割线和按钮不加vModel
+                    if (noVModelList.indexOf(config.jnpfKey) < 0 || item.isStorage == 1) {
+                        item.__vModel__ = this.toggleVmodelCase(`${config.jnpfKey}Field${this.idGlobal}`)
+                    }
+                } else {
+                    if (noVModelList.indexOf(config.jnpfKey) < 0 || item.isStorage == 1) {
+                        item.__vModel__ = ""
+                    }
+                }
+                if (parent && parent.__vModel__ && parent.__config__.jnpfKey === 'table') {
+                    item.__config__.parentVModel = parent.__vModel__
+                }
+            } else if (config.layout === 'rowFormItem') {
+                if (config.jnpfKey === 'table' || config.jnpfKey === 'custom-table') {
+                    item.__vModel__ = this.toggleVmodelCase(`${config.jnpfKey}Field${this.idGlobal}`);
+                }
+                config.componentName = `row${this.idGlobal}`
+                !Array.isArray(config.children) && (config.children = [])
+                // delete config.label // rowFormItem无需配置label属性
+            }
+            if (Array.isArray(config.children)) {
+                config.children = config.children.map(childItem => this.createIdAndKey(childItem, item))
+            }
+            return item
+        },
+        setVModel(item) {
+            const config = item.__config__
+            item.__vModel__ = this.toggleVmodelCase(`${config.jnpfKey}Field${config.formId}`)
+        },
+        toggleVmodelCase(str) {
+            const dbType = this.dbType || ''
+            if (dbType.toLowerCase() === 'Oracle'.toLowerCase() || dbType.toLowerCase() === 'DM'.toLowerCase()) {
+                return str.toUpperCase()
+            }
+            if (dbType.toLowerCase() === 'PostgreSQL'.toLowerCase() || dbType.toLowerCase() === 'KingBaseES'.toLowerCase()) {
+                return str.toLowerCase()
+            }
+            return str
+        },
+        AssembleFormData() {
+            this.formData = {
+                ...this.formConf,
+                fields: deepClone(this.drawingList),
+            }
+        },
+        empty() {
+            this.$confirm('确定重置问卷吗?', '提示', { type: 'warning' }).then(() => {
+                this.drawingList = [{
+                    __config__: {
+                        jnpfKey: "question-title",
+                        tag: "QuestionnaireInput",
+                        tagIcon: "icon-ym icon-ym-generator-input",
+                        required: false,
+                        noShow: false,
+                        isDisplay: true,
+                        showLabel: true,
+                        tableAlign: "left",
+                        tableFixed: "none",
+                        layout: "questionTopItem",
+                        id: '001',
+                        formId: '001',
+                        renderKey: "001",
+                        span: 24,
+                        dragDisabled: false,
+                        valueType: null,
+                        defaultValue: '默认问卷',
+                        parentDefault: null
+                    },
+                    __vModel__: "title",
+                    disabled: false,
+                    placeholder: null
+                },
+                {
+                    __config__: {
+                        jnpfKey: "question-tip",
+                        tag: "QuestionnaireTopTip",
+                        tagIcon: "icon-ym icon-ym-generator-input",
+                        required: false,
+                        noShow: false,
+                        isDisplay: true,
+                        showLabel: true,
+                        tableAlign: "left",
+                        tableFixed: "none",
+                        layout: "questionTopItem",
+                        id: '002',
+                        formId: '002',
+                        renderKey: "002",
+                        span: 24,
+                        dragDisabled: false,
+                        valueType: null,
+                        defaultValue: '为了给您提供更好的服务,希望您能抽出几分钟时间,将您的感受和建议告诉我们,我们非常重视每位用户的宝贵意见,期待您的参与!现在我们就马上开始吧!',
+                        parentDefault: null
+                    },
+                    __vModel__: "top-tip",
+                    disabled: false,
+                    placeholder: null
+                }]
+                this.idGlobal = 100
+            }).catch(() => { })
+        },
+        drawingItemCopy(item, parent, isActiveFormItem = true) {
+            let clone = deepClone(item)
+            clone = this.createIdAndKey(clone)
+
+
+            if (['question-radio', 'question-checkbox'].includes(clone.__config__.jnpfKey)) {
+                let num = clone.options.length
+                clone.options = []
+                for (let i = 0; i < num; i++) {
+                    const id = this.jnpf.idGenerator()
+                    clone.options.push({
+                        id: id,
+                        fullName: '选项' + id,
+                    })
+                }
+            }
+
+            parent.push(clone)
+            isActiveFormItem && this.activeFormItem(clone)
+        },
+        drawingItemDelete(index, parent) {
+            parent.splice(index, 1)
+            this.$nextTick(() => {
+                const len = this.drawingList.length
+                if (len) {
+                    this.activeFormItem(this.drawingList[len - 1])
+                }
+            })
+        },
+        preview() {
+            this.AssembleFormData()
+            this.previewVisible = true
+        },
+        showJson() {
+            this.AssembleFormData()
+            this.jsonDrawerVisible = true
+        },
+        tagChange(newTag) {
+            newTag = this.cloneComponent(newTag)
+            const config = newTag.__config__
+            newTag.__vModel__ = this.activeData.__vModel__
+            config.formId = this.activeId
+            config.span = this.activeData.__config__.span
+            this.activeData.__config__.tag = config.tag
+            this.activeData.__config__.tagIcon = config.tagIcon
+            this.activeData.__config__.document = config.document
+            if (typeof this.activeData.__config__.defaultValue === typeof config.defaultValue) {
+                config.defaultValue = this.activeData.__config__.defaultValue
+            }
+            Object.keys(newTag).forEach(key => {
+                if (this.activeData[key] !== undefined) {
+                    newTag[key] = this.activeData[key]
+                }
+            })
+            this.activeData = newTag
+            this.updateDrawingList(newTag, this.drawingList)
+        },
+        updateDrawingList(newTag, list) {
+            const index = list.findIndex(item => item.__config__.formId === this.activeId)
+            if (index > -1) {
+                list.splice(index, 1, newTag)
+            } else {
+                list.forEach(item => {
+                    if (Array.isArray(item.__config__.children)) this.updateDrawingList(newTag, item.__config__.children)
+                })
+            }
+        },
+        refreshJson(data) {
+            this.drawingList = deepClone(data.fields)
+            delete data.fields
+            this.formConf = data
+        },
+        relationChange(vmodel) {
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    const config = list[i].__config__
+                    if (list[i].relationField && list[i].relationField === list[i].__vModel__) {
+                        list[i].showField = ''
+                    }
+                    if (config && config.children && Array.isArray(config.children)) {
+                        loop(config.children)
+                    }
+                }
+            }
+            loop(this.drawingList)
+        },
+        handleTableAddRow(element, insertPos, cloneRowIdx) {
+            const row = element.__config__.children
+            let rowIdx = (insertPos === undefined) ? row.length : insertPos + row[cloneRowIdx].__config__.children[this.colIndex].__config__.rowspan - 1
+            let newRow = (cloneRowIdx === undefined) ? deepClone(row[row.length - 1]) : deepClone(row[cloneRowIdx])
+            newRow.__config__.children.forEach(col => {
+                col.__config__.formId = ++this.idGlobal
+                col.__config__.merged = false
+                col.__config__.colspan = 1
+                col.__config__.rowspan = 1
+                col.__config__.children = []
+            })
+            newRow.__config__.formId = ++this.idGlobal
+            newRow.__config__.jnpfKey = "tableGridTr"
+            row.splice(rowIdx, 0, newRow)
+        },
+        handleTableAddCol(element, insertPos, cloneRowIdx) {
+            const row = element.__config__.children
+            let colIdx = (insertPos === undefined) ? row[0].__config__.children.length : insertPos  //确定插入列位置
+            row.forEach(item => {
+                let newCol = {
+                    __config__: {
+                        jnpfKey: "tableGridTd",
+                        merged: false,
+                        colspan: 1,
+                        rowspan: 1,
+                        formId: ++this.idGlobal,
+                        children: [],
+                        backgroundColor: '',
+                    }
+                }
+                item.__config__.children.splice(colIdx, 0, newCol)
+            })
+        },
+        mergeTableCol(element, type) {
+            let mergedColIndex = type == 1 ? this.colIndex : this.colIndex + this.colData[this.colIndex].__config__.colspan
+            let remainedColIndex = type == 1 ? this.colIndex - this.colData[this.colIndex - 1].__config__.colspan : this.colIndex
+            const colChildren = this.colData[mergedColIndex].__config__.children
+            const colChildren_ = this.colData[remainedColIndex].__config__.children
+            this.colData[remainedColIndex].__config__.children = [...colChildren_, ...deepClone(colChildren)]
+            let newColspan = this.colData[mergedColIndex].__config__.colspan * 1 + this.colData[remainedColIndex].__config__.colspan * 1
+            this.setPropsOfMergedCols(remainedColIndex, newColspan, this.selectCell.__config__.rowspan)
+        },
+        mergeWholeCol() {
+            let childrenData = this.colData.filter((colItem) => {
+                return !colItem.merged && colItem.__config__.children && colItem.__config__.children.length
+            })
+            if (childrenData && childrenData.length) {
+                childrenData.map((o, i) => {
+                    if (i == 0) this.colData[0].__config__.children = cloneDeep(o.__config__.children);
+                    if (i != 0) this.colData[0].__config__.children.push(...deepClone(o.__config__.children))
+                })
+            }
+            this.setPropsOfMergedCols(0, this.colData.length, this.colData[this.colIndex].__config__.rowspan)
+        },
+        mergeTableRow(type) {
+            let mergedRowIndex = type == 1 ? this.rowIndex : this.rowIndex + this.selectCell.__config__.rowspan
+            let remainedRowIndex = type == 1 ? this.rowIndex - 1 : this.rowIndex
+            let childrenData = this.rowData[mergedRowIndex].__config__.children[this.colIndex].__config__.children
+            let childrenData_ = this.rowData[remainedRowIndex].__config__.children[this.colIndex].__config__.children
+            this.rowData[remainedRowIndex].__config__.children[this.colIndex].__config__.children = [...childrenData_, ...deepClone(childrenData)]
+            let newRowspan = this.rowData[mergedRowIndex].__config__.children[this.colIndex].__config__.rowspan * 1 + this.rowData[remainedRowIndex].__config__.children[this.colIndex].__config__.rowspan * 1
+            this.setPropsOfMergedRows(remainedRowIndex, this.selectCell.__config__.colspan, newRowspan)
+        },
+        mergeWholeRow() {
+            let childrenData = []
+            this.rowData.forEach(o => {
+                let tempCell = o.__config__.children[this.colIndex]
+                if (!o.__config__.merged && !!o.__config__.children && o.__config__.children.length) {
+                    childrenData.push(tempCell)
+                }
+            })
+            let firstCellOfCol = this.rowData[0].__config__.children[this.colIndex]
+            if (childrenData && childrenData.length) {
+                childrenData.map((o, i) => {
+                    if (i != 0) firstCellOfCol.__config__.children.push(...deepClone(o.__config__.children))
+                })
+            }
+            this.setPropsOfMergedRows(0, firstCellOfCol.__config__.colspan, this.rowData.length)
+        },
+        undoMergeCol() {
+            this.setPropsOfSplitCol(this.colIndex, this.selectCell.__config__.colspan, this.selectCell.__config__.rowspan)
+        },
+        undoMergeRow() {
+            this.setPropsOfSplitRow(this.colIndex, this.selectCell.__config__.colspan, this.selectCell.__config__.rowspan)
+        },
+        deleteWholeCol() {
+            let startColspan = this.rowData[0].__config__.children[this.colIndex].__config__.colspan
+            this.rowData.forEach((rItem) => {
+                rItem.__config__.children.splice(this.colIndex, startColspan)
+            })
+        },
+        deleteWholeRow() {
+            let startRowspan = this.rowData[this.rowIndex].__config__.children[0].__config__.rowspan
+            this.rowData.splice(this.rowIndex, startRowspan)
+        },
+        setPropsOfMergedCols(startColIndex, newColspan, rowspan) {
+            for (let i = this.rowIndex; i < this.rowIndex + rowspan; i++) {
+                for (let j = startColIndex; j < startColIndex + newColspan; j++) {
+                    if ((i === this.rowIndex) && (j === startColIndex)) {
+                        this.rowData[i].__config__.children[j].__config__.colspan = newColspan
+                        continue
+                    }
+                    this.rowData[i].__config__.children[j].__config__.merged = true
+                    this.rowData[i].__config__.children[j].__config__.colspan = newColspan
+                    this.rowData[i].__config__.children[j].__config__.children = []
+                }
+            }
+        },
+        setPropsOfMergedRows(startRowIndex, colspan, newRowspan, colIndex) {
+            if (!colIndex) colIndex = this.colIndex
+            for (let i = startRowIndex; i < startRowIndex + newRowspan; i++) {
+                for (let j = colIndex; j < colIndex + colspan; j++) {
+                    if ((i === startRowIndex) && (j === colIndex)) {
+                        this.rowData[i].__config__.children[j].__config__.rowspan = newRowspan
+                        continue
+                    }
+                    this.rowData[i].__config__.children[j].__config__.merged = true
+                    this.rowData[i].__config__.children[j].__config__.rowspan = newRowspan
+                    this.rowData[i].__config__.children[j].__config__.children = []
+                }
+            }
+        },
+        setPropsOfSplitCol(startColIndex, colspan, rowspan) {
+            for (let i = this.rowIndex; i < this.rowIndex + rowspan; i++) {
+                for (let j = startColIndex; j < startColIndex + colspan; j++) {
+                    this.rowData[i].__config__.children[j].__config__.merged = false;
+                    this.rowData[i].__config__.children[j].__config__.rowspan = 1
+                    this.rowData[i].__config__.children[j].__config__.colspan = 1
+                }
+            }
+        },
+        setPropsOfSplitRow(startColIndex, colspan, rowspan) {
+            for (let i = this.rowIndex; i < this.rowIndex + rowspan; i++) {
+                for (let j = startColIndex; j < startColIndex + colspan; j++) {
+                    this.rowData[i].__config__.children[j].__config__.merged = false;
+                    this.rowData[i].__config__.children[j].__config__.rowspan = 1
+                    this.rowData[i].__config__.children[j].__config__.colspan = 1
+                }
+            }
+        },
+        handleTableSetting(e, element) {
+            switch (e) {
+                case '1':
+                    //插入左侧列
+                    this.handleTableAddCol(element, this.colIndex)
+                    break;
+                case '2':
+                    //插入右侧列
+                    this.handleTableAddCol(element, this.colIndex + 1)
+                    break;
+                case '3':
+                    //插入上方行
+                    this.handleTableAddRow(element, this.rowIndex, this.rowIndex)
+                    break;
+                case '4':
+                    //插入下方行
+                    this.handleTableAddRow(element, this.rowIndex + 1, this.rowIndex)
+                    break;
+                case '5':
+                    //向左合并
+                    this.mergeTableCol(element, 1)
+                    break;
+                case '6':
+                    //向右合并
+                    this.mergeTableCol(element)
+                    break;
+                case '7':
+                    //合并整行
+                    this.mergeWholeCol(element)
+                    break;
+                case '8':
+                    //向上合并
+                    this.mergeTableRow(1)
+                    break;
+                case '9':
+                    //向下合并
+                    this.mergeTableRow()
+                    break;
+                case '10':
+                    //合并整列
+                    this.mergeWholeRow()
+                    break;
+                case '11':
+                    //撤销行合并
+                    this.undoMergeCol()
+                    break;
+                case '12':
+                    //撤销列合并
+                    this.undoMergeRow()
+                    break;
+                case '13':
+                    //删除整列
+                    this.deleteWholeCol()
+                    break;
+                case '14':
+                    //删除整行
+                    this.deleteWholeRow()
+                    break;
+                default:
+                    break;
+            }
+            this.resetData()
+        },
+        resetData() {
+            this.rowIndex = 0,
+                this.colIndex = 0,
+                this.rowData = [],
+                this.colData = [],
+                this.selectCell = {
+                    __config__: {
+                        rowspan: 1,
+                        colspan: 1
+                    }
+                }
+        },
+        handleShowMenu(element, rowIndex, colIndex) {
+            this.rowIndex = rowIndex
+            this.colIndex = colIndex
+            this.rowData = element.__config__.children
+            this.colData = this.rowData[rowIndex].__config__.children
+            this.selectCell = this.colData[colIndex]
+        }
+    }
+}
+</script>
+<style lang='scss' >
+@import '../styles/index';
+@import '../styles/home';
+.drawing-board {
+    padding: 0 30px;
+}
+</style>

+ 89 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/JsonDrawer.vue

@@ -0,0 +1,89 @@
+<template>
+  <el-drawer v-bind="$attrs" v-on="$listeners" @opened="onOpen" @close="onClose"
+    :modal-append-to-body="false" append-to-body>
+    <div class="action-bar">
+      <!-- <el-button icon="el-icon-refresh" type="text" @click="refresh">刷新</el-button> -->
+      <el-button icon="el-icon-document-copy" type="text" class="copy-json-btn">复制JSON</el-button>
+      <el-button icon="el-icon-download" type="text" @click="exportJsonFile">导出JSON文件</el-button>
+      <el-button icon="el-icon-circle-close" type="text" @click="$emit('update:visible', false)"
+        class="delete-btn">关闭</el-button>
+    </div>
+    <JSONEditor v-model="template" mode="json" class="json-editor" />
+  </el-drawer>
+</template>
+
+<script>
+import ClipboardJS from 'clipboard'
+import { saveAs } from 'file-saver'
+import JSONEditor from '@/components/JsonEditor'
+
+export default {
+  components: { JSONEditor },
+  props: {
+    jsonData: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  data() {
+    return {
+      template: '',
+      clipboard: null
+    }
+  },
+  methods: {
+    onOpen() {
+      this.clipboard = new ClipboardJS('.copy-json-btn', {
+        text: trigger => {
+          this.$notify({
+            title: '复制成功',
+            message: '代码已复制到剪切板,可粘贴。',
+            type: 'success'
+          })
+          return this.template
+        }
+      })
+      this.clipboard.on('error', e => {
+        this.$message.error('代码复制失败')
+      })
+      this.template = JSON.stringify(this.jsonData, null, 2)
+    },
+    onClose() {
+      this.clipboard && this.clipboard.destroy()
+    },
+    exportJsonFile() {
+      this.$prompt('文件名:', '导出文件', {
+        inputValue: `${+new Date()}.json`,
+        closeOnClickModal: false,
+        inputPlaceholder: '请输入文件名'
+      }).then(({ value }) => {
+        if (!value) value = `${+new Date()}.json`
+        const blob = new Blob([this.template], { type: 'text/plain;charset=utf-8' })
+        saveAs(blob, value)
+      })
+    },
+    refresh() {
+      try {
+        this.$emit('refresh', this.template)
+      } catch (error) {
+        this.$notify({
+          title: '错误',
+          message: 'JSON格式错误,请检查',
+          type: 'error'
+        })
+      }
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+>>> .el-drawer__header {
+  display: none;
+}
+.action-bar {
+  text-align: left;
+}
+.json-editor {
+  height: calc(100vh - 42px);
+}
+</style>

+ 129 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/BatchEditing.vue

@@ -0,0 +1,129 @@
+<template>
+  <el-dialog :visible.sync="visible" append-to-body title="批量编辑" :closeOnClickModal="false"
+    class="JNPF-dialog JNPF-dialog_center update-dialog" lock-scroll width="700px">
+    <el-alert title="注意:每行对应一个选项;选项名和选项值之间用英文 | 隔开。参考格式如下:" type="warning" :closable="false"
+      show-icon />
+    <div class="demo-box">
+      <div class="demo-box-cell">
+        <p class="w-220px">格式一:选项名和选项值一致时</p>
+        <span class="w-100px">选项名</span>
+        <p>输入示例:<span>选项一</span></p>
+      </div>
+      <div class="demo-box-cell">
+        <p class="w-220px">格式二:选项名和选项值不一致时</p>
+        <span class="w-100px">选项名|选项值</span>
+        <p>输入示例:<span>选项一|选项一的值</span></p>
+      </div>
+    </div>
+    <el-input type="textarea" :rows="100" v-model="resultInfo" autosize class="textarea"></el-input>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="handleSure()">
+        {{$t('common.confirmButton')}}
+      </el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+export default {
+  components: {},
+  props: {
+  },
+  data() {
+    return {
+      visible: false,
+      resultInfo: '',
+      separator: '|',
+      options: []
+    }
+  },
+  computed: {
+  },
+  methods: {
+    init(options) {
+      this.options = options || []
+      this.analysis()
+      this.visible = true
+    },
+    analysis() {
+      this.resultInfo = ''
+      if (this.options.length > 0) {
+        this.options.forEach((opt) => {
+          if (opt.id === opt.fullName) {
+            this.resultInfo += opt.fullName + this.separator + opt.id + '\n'
+          } else {
+            this.resultInfo += opt.fullName + this.separator + opt.id + '\n'
+          }
+        })
+      }
+    },
+    handleSure() {
+      let lineArray = this.resultInfo.split('\n')
+      if (lineArray.length > 0) {
+        this.options = []
+        lineArray.forEach((optLine) => {
+          if (!!optLine && !!optLine.trim()) {
+            if (optLine.indexOf(this.separator) !== -1) {
+              this.options.push({
+                id: optLine.split(this.separator)[1],
+                fullName: optLine.split(this.separator)[0]
+              })
+            } else {
+              this.options.push({
+                id: optLine,
+                fullName: optLine
+              })
+            }
+          }
+        })
+      } else {
+        this.options = []
+      }
+      this.$emit('change', this.options)
+      this.visible = false
+    },
+  }
+}
+</script>
+<style lang="scss" scoped>
+.format-one {
+  border: 1px solid #dcdfe6;
+}
+.format-two {
+  border-right: 1px solid #dcdfe6;
+  border-left: 1px solid #dcdfe6;
+  border-bottom: 1px solid #dcdfe6;
+  margin-bottom: 20px;
+}
+.textarea {
+  >>> .el-textarea__inner {
+    height: 300px !important;
+    line-height: 30px;
+  }
+}
+.demo-box {
+  margin: 10px 0;
+  border: 1px solid #d9d9d9;
+  .w-220px {
+    width: 220px;
+  }
+  .w-100px {
+    width: 100px;
+  }
+  .demo-box-cell {
+    line-height: 36px;
+    border-bottom: 1px solid #d9d9d9;
+    padding: 0 15px;
+    &:last-child {
+      border-bottom: 0;
+    }
+    p,
+    span {
+      display: inline-block;
+    }
+    span {
+      color: #1890ff;
+    }
+  }
+}
+</style>

+ 76 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QCheckbox.vue

@@ -0,0 +1,76 @@
+<template>
+    <el-row>
+        <el-form-item label="题目标题">
+            <el-input type="input" v-model="activeData.title" placeholder="请输入题目标题" />
+        </el-form-item>
+        <el-form-item label="题目描述">
+            <el-input type="input" v-model="activeData.describe" placeholder="请输入题目描述" />
+        </el-form-item>
+        <el-form-item label="是否必答">
+            <el-switch v-model="activeData.__config__.required" />
+        </el-form-item>
+        <el-divider>数据选项</el-divider>
+        <draggable :list="activeData.options" :animation="340" group="selectItem"
+            handle=".option-drag">
+            <div v-for="(item, index) in activeData.options" :key="index" class="select-item">
+                <div class="select-line-icon option-drag">
+                    <i class="icon-ym icon-ym-darg" />
+                </div>
+                <el-input v-model="item.fullName" placeholder="选项名" size="small" />
+                <el-input v-model="item.id" placeholder="选项值" size="small" />
+                <div class="close-btn select-line-icon"
+                    @click="activeData.options.splice(index, 1)">
+                    <i class="el-icon-remove-outline" />
+                </div>
+            </div>
+        </draggable>
+        <div style="margin-left: 29px;">
+            <el-button style="padding-bottom: 0" icon="el-icon-circle-plus-outline" type="text"
+                @click="addSelectItem">
+                添加选项
+            </el-button>
+            <el-divider direction="vertical"></el-divider>
+            <el-button style="padding-bottom: 0" type="text" @click="updateSelectItem">
+                批量编辑
+            </el-button>
+        </div>
+        <BatchEditing v-if="updateVisible" ref="batchEditing" @change="handleSure" />
+    </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+import BatchEditing from './BatchEditing'
+import draggable from 'vuedraggable'
+
+
+export default {
+    props: ['activeData'],
+    mixins: [comMixin],
+    components: { draggable, BatchEditing },
+    data() {
+        return {
+            updateVisible: false
+        }
+    },
+    created() {
+    },
+    methods: {
+        addSelectItem() {
+            const id = this.jnpf.idGenerator()
+            this.activeData.options.push({
+                id: id,
+                fullName: '选项' + id,
+            })
+        },
+        updateSelectItem() {
+            this.updateVisible = true
+            this.$nextTick(() => {
+                this.$refs.batchEditing.init(this.activeData.options)
+            })
+        },
+        handleSure(arr) {
+            this.activeData.options = arr || []
+        },
+    }
+}
+</script>

+ 76 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QRadio.vue

@@ -0,0 +1,76 @@
+<template>
+    <el-row>
+        <el-form-item label="题目标题">
+            <el-input type="input" v-model="activeData.title" placeholder="请输入题目标题" />
+        </el-form-item>
+        <el-form-item label="题目描述">
+            <el-input type="input" v-model="activeData.describe" placeholder="请输入题目描述" />
+        </el-form-item>
+        <el-form-item label="是否必答">
+            <el-switch v-model="activeData.__config__.required" />
+        </el-form-item>
+        <el-divider>数据选项</el-divider>
+        <draggable :list="activeData.options" :animation="340" group="selectItem"
+            handle=".option-drag">
+            <div v-for="(item, index) in activeData.options" :key="index" class="select-item">
+                <div class="select-line-icon option-drag">
+                    <i class="icon-ym icon-ym-darg" />
+                </div>
+                <el-input v-model="item.fullName" placeholder="选项名" size="small" />
+                <el-input v-model="item.id" placeholder="选项值" size="small" />
+                <div class="close-btn select-line-icon"
+                    @click="activeData.options.splice(index, 1)">
+                    <i class="el-icon-remove-outline" />
+                </div>
+            </div>
+        </draggable>
+        <div style="margin-left: 29px;">
+            <el-button style="padding-bottom: 0" icon="el-icon-circle-plus-outline" type="text"
+                @click="addSelectItem">
+                添加选项
+            </el-button>
+            <el-divider direction="vertical"></el-divider>
+            <el-button style="padding-bottom: 0" type="text" @click="updateSelectItem">
+                批量编辑
+            </el-button>
+        </div>
+        <BatchEditing v-if="updateVisible" ref="batchEditing" @change="handleSure" />
+    </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+import BatchEditing from './BatchEditing'
+import draggable from 'vuedraggable'
+
+
+export default {
+    props: ['activeData'],
+    mixins: [comMixin],
+    components: { draggable, BatchEditing },
+    data() {
+        return {
+            updateVisible: false
+        }
+    },
+    created() {
+    },
+    methods: {
+        addSelectItem() {
+            const id = this.jnpf.idGenerator()
+            this.activeData.options.push({
+                id: id,
+                fullName: '选项' + id,
+            })
+        },
+        updateSelectItem() {
+            this.updateVisible = true
+            this.$nextTick(() => {
+                this.$refs.batchEditing.init(this.activeData.options)
+            })
+        },
+        handleSure(arr) {
+            this.activeData.options = arr || []
+        },
+    }
+}
+</script>

+ 30 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTextarea.vue

@@ -0,0 +1,30 @@
+<template>
+    <el-row>
+        <el-form-item label="题目标题">
+            <el-input type="input" v-model="activeData.title" placeholder="请输入题目标题" />
+        </el-form-item>
+        <el-form-item label="题目描述">
+            <el-input type="input" v-model="activeData.describe" placeholder="请输入题目描述" />
+        </el-form-item>
+        <el-form-item label="是否必答">
+            <el-switch v-model="activeData.__config__.required" />
+        </el-form-item>
+    </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+export default {
+    props: ['activeData'],
+    mixins: [comMixin],
+    components: {},
+    data() {
+        return {
+        }
+    },
+    created() {
+    },
+    methods: {
+
+    }
+}
+</script>

+ 42 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTip.vue

@@ -0,0 +1,42 @@
+<template>
+    <el-row>
+        <el-form-item label="问卷提示" label-width="90px">
+            <el-input type="textarea" :rows="5"
+                v-model="activeData.__config__.defaultValue"></el-input>
+        </el-form-item>
+
+    </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+export default {
+    props: ['activeData'],
+    mixins: [comMixin],
+    components: {},
+    data() {
+        return {
+            startOptions: []
+        }
+    },
+    created() {
+        if (this.activeData.__config__.valueType == '1') {
+            console.log('inputCreated')
+            this.getStartOptions();
+        }
+    },
+    methods: {
+        valueTypeChange(e) {
+            this.activeData.__config__.defaultValue = '';
+            if (e == '1') {
+                this.getStartOptions();
+            }
+        },
+        getStartOptions() {
+            getDictionaryDataSelector('processFormDefaultValue').then(res => {
+                this.startOptions = res.data.list
+            })
+        },
+    }
+}
+</script>

+ 37 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/QTitle.vue

@@ -0,0 +1,37 @@
+<template>
+    <el-row>
+        <el-form-item label="问卷标题" label-width="90px">
+            <el-input v-model="activeData.__config__.defaultValue"></el-input>
+        </el-form-item>
+    </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+export default {
+    props: ['activeData'],
+    mixins: [comMixin],
+    components: {},
+    data() {
+        return {
+            startOptions: []
+        }
+    },
+    created() {
+        if (this.activeData.__config__.valueType == '1') {
+            console.log('inputCreated')
+            this.getStartOptions();
+        }
+    },
+    methods: {
+        titleChange(e) {
+            console.log(e)
+        },
+        getStartOptions() {
+            getDictionaryDataSelector('processFormDefaultValue').then(res => {
+                this.startOptions = res.data.list
+            })
+        },
+    }
+}
+</script>

+ 141 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/dynamicMixin.js

@@ -0,0 +1,141 @@
+import { noAllowRelationList } from '@/components/Generator/generator/comConfig'
+import { getDrawingList } from '@/components/Generator/utils/db'
+import { getDictionaryDataSelector, getAPIOptions } from '@/api/systemData/dictionary'
+import { getDataInterfaceRes } from '@/api/systemData/dataInterface'
+import draggable from 'vuedraggable'
+import InterfaceDialog from '@/components/Process/PropPanel/InterfaceDialog'
+export default {
+  props: ['activeData', 'dictionaryOptions', 'dataInterfaceOptions'],
+  components: { draggable, InterfaceDialog },
+  data() {
+    return {
+      dictionaryId: ''
+    }
+  },
+  computed: {
+    dicOptions() {
+      return this.dictionaryOptions
+    },
+    interfaceOptions() {
+      return this.dataInterfaceOptions
+    },
+    defaultValue() {
+      let defaultValue = ''
+      if (this.activeData.__config__.jnpfKey === 'checkbox' || this.activeData.__config__.jnpfKey === 'custom-checkbox' || this.activeData.multiple) defaultValue = []
+      return defaultValue
+    },
+    formFieldsOptions() {
+      let list = []
+      const loop = (data, parent) => {
+        if (!data) return
+        if (data.__config__ && this.isIncludesTable(data) && data.__config__.children && Array.isArray(data.__config__.children)) {
+          loop(data.__config__.children, data)
+        }
+        if (Array.isArray(data)) data.forEach(d => loop(d, parent))
+        if (data.__vModel__ && !noAllowRelationList.includes(data.__config__.jnpfKey) && data.__vModel__ !== this.activeData.__vModel__) {
+          const isTableChild = parent && parent.__config__ && parent.__config__.jnpfKey === 'table'
+          list.push({
+            realVModel: isTableChild ? parent.__vModel__ + '-' + data.__vModel__ : data.__vModel__,
+            realLabel: isTableChild ? parent.__config__.label + '-' + data.__config__.label : data.__config__.label,
+            ...data
+          })
+        }
+      }
+      loop(getDrawingList())
+      return list
+    },
+  },
+  methods: {
+    isIncludesTable(data) {
+      if ((!data.__config__.layout || data.__config__.layout === 'rowFormItem') && data.__config__.jnpfKey !== 'table') return true
+      if (this.activeData.__config__.isSubTable) return this.activeData.__config__.parentVModel === data.__vModel__
+      return data.__config__.jnpfKey !== 'table'
+    },
+    onRelationFieldChange(val, row) {
+      if (!val) return row.jnpfKey = ''
+      let list = this.formFieldsOptions.filter(o => o.realVModel === val)
+      if (!list.length) return row.jnpfKey = ''
+      let item = list[0]
+      row.jnpfKey = item.__config__.jnpfKey
+    },
+    multipleChange(val) {
+      this.$set(this.activeData.__config__, 'defaultValue', val ? [] : '')
+    },
+    addSelectItem() {
+      const id = this.jnpf.idGenerator()
+      this.activeData.options.push({
+        id: id,
+        fullName: '选项' + id,
+      })
+    },
+    dataTypeChange(val) {
+      this.activeData.__config__.defaultValue = this.defaultValue
+      this.activeData.options = []
+      this.activeData.__config__.props.value = 'id'
+      this.activeData.__config__.props.label = 'fullName'
+      this.activeData.__config__.dictionaryType = ''
+      this.activeData.__config__.propsUrl = ''
+      this.activeData.__config__.propsName = ''
+      this.activeData.__config__.templateJson = []
+    },
+    dictionaryTypeChange(val) {
+      console.log(val)
+      this.dictionaryId = val
+      this.activeData.__config__.defaultValue = this.defaultValue
+
+      if (!val) {
+        this.activeData.options = []
+        return
+      }
+      getDictionaryDataSelector(val).then(res => {
+        this.activeData.options = res.data.list
+      })
+    },
+    // 获取动态接口的来源数据
+    getApiOption(path) {
+      getAPIOptions(path).then(res => {
+        this.activeData.options = res.data
+      })
+    },
+    propsUrlChange(val, row) {
+      let jnpfKey = this.activeData.__config__.jnpfKey
+      if (jnpfKey === 'cascader') {
+        this.activeData.__config__.defaultValue = []
+      } else if (jnpfKey === 'treeSelect') {
+        this.activeData.__config__.defaultValue = this.activeData.multiple ? [] : ''
+      } else {
+        this.activeData.__config__.defaultValue = this.defaultValue
+      }
+      if (!val) {
+        this.activeData.__config__.propsUrl = ''
+        this.activeData.__config__.propsName = ''
+        this.activeData.__config__.templateJson = []
+        this.activeData.options = []
+        this.getDataInterfaceInfo()
+        return
+      }
+      let list = row.parameterJson ? JSON.parse(row.parameterJson) : []
+      this.activeData.__config__.propsUrl = val
+      this.activeData.__config__.propsName = row.fullName
+      this.activeData.__config__.templateJson = list.map(o => ({ ...o, relationField: '' }))
+      let query = {
+        paramList: this.activeData.__config__.templateJson || [],
+      }
+      getDataInterfaceRes(val, query).then(res => {
+        let data = res.data
+        if (Array.isArray(data)) {
+          this.activeData.options = data
+        } else {
+          this.activeData.options = []
+        }
+        this.getDataInterfaceInfo()
+      }).catch(() => {
+        this.activeData.__config__.propsUrl = ''
+        this.activeData.__config__.propsName = ''
+        this.activeData.__config__.templateJson = []
+        this.activeData.options = []
+        this.getDataInterfaceInfo()
+      })
+    }
+  }
+}

+ 43 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/handelFlidMixin.js

@@ -0,0 +1,43 @@
+
+import { getDataInterfaceInfo } from '@/api/systemData/dataInterface'
+const useSelect = ['treeSelect', 'radio', 'checkbox', 'select', 'cascader']
+export default {
+  data() {
+    return { fieldJsonList: [], hasPage: false }
+  },
+  methods: {
+    getDataInterfaceInfo() {
+      let url = this.activeData.interfaceId ? this.activeData.interfaceId : this.activeData.__config__.propsUrl
+      getDataInterfaceInfo(url).then(res => {
+        this.fieldJsonList = res.data && res.data.fieldJson && (JSON.parse(res.data.fieldJson) || []) || []
+        this.hasPage = res.data && res.data.hasPage == 1 ? true : false
+        this.activeData.interfaceHasPage = this.hasPage
+        this.activeData.hasPage = this.hasPage
+      })
+    },
+    querySearch(queryString, cb) {
+      var restaurants = this.fieldJsonList || [];
+      var results = queryString ? restaurants.filter(this.createStateFilter(queryString)) : restaurants;
+      // 调用 callback 返回建议列表的数据
+      cb(results);
+    },
+    createStateFilter(queryString) {
+      return (state) => {
+        return (state.defaultValue.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
+      };
+    },
+    handleSelect(item, field, index) {
+      const jnpfKey = this.activeData.__config__.jnpfKey
+      if (jnpfKey === 'autoComplete') this.activeData[field] = item.defaultValue
+      if (jnpfKey === 'popupSelect' || jnpfKey === 'popupTableSelect') {
+        if (isNaN(index)) {
+          this.activeData[field] = item.defaultValue
+        } else {
+          this.activeData.columnOptions[index][field] = item.defaultValue
+        }
+      }
+      if (useSelect.includes(jnpfKey)) this.activeData.props[field] = item.defaultValue
+      this.activeData.__config__.renderKey = +new Date()
+    }
+  }
+}

+ 70 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/mixin.js

@@ -0,0 +1,70 @@
+import { isNumberStr } from '@/components/Generator/utils'
+export default {
+  inject: ["getShowType"],
+  data() {
+    return {}
+  },
+  computed: {
+    showType() {
+      return this.getShowType()
+    }
+  },
+  methods: {
+    onDefaultValueInput(str) {
+      if (Array.isArray(this.activeData.__config__.defaultValue)) {
+        // 数组
+        this.$set(
+          this.activeData.__config__,
+          'defaultValue',
+          str.split(',').map(val => (isNumberStr(val) ? +val : val))
+        )
+      } else if (['true', 'false'].indexOf(str) > -1) {
+        // 布尔
+        this.$set(this.activeData.__config__, 'defaultValue', JSON.parse(str))
+      } else {
+        // 字符串和数字
+        this.$set(
+          this.activeData.__config__,
+          'defaultValue',
+          isNumberStr(str) ? +str : str
+        )
+      }
+    },
+    onInterfaceIdChange(val, row) {
+      if (!val) {
+        this.activeData.interfaceId = ''
+        this.activeData.interfaceName = ''
+        this.activeData.templateJson = []
+        this.activeData.__config__.defaultValue = ''
+        this.activeData.interfaceHasPage = 0
+        this.getDataInterfaceInfo()
+        return
+      }
+      this.activeData.interfaceId = val
+      this.activeData.interfaceName = row.fullName
+      this.activeData.templateJson = row.parameterJson ? JSON.parse(row.parameterJson) : []
+      this.activeData.__config__.defaultValue = ''
+      this.activeData.__config__.transferList = []
+      this.activeData.columnOptions = []
+      this.getDataInterfaceInfo()
+    },
+    setDefaultValue(val) {
+      if (Array.isArray(val)) {
+        return val.join(',')
+      }
+      // if (['string', 'number'].indexOf(typeof val) > -1) {
+      //   return val
+      // }
+      if (typeof val === 'boolean') {
+        return `${val}`
+      }
+      return val
+    },
+    addReg() {
+      this.activeData.__config__.regList.push({
+        pattern: '',
+        message: ''
+      })
+    }
+  }
+}

+ 29 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightComponents/tpl.vue

@@ -0,0 +1,29 @@
+<template>
+  <el-row>
+    <el-form-item label="控件标题">
+      <el-input v-model="activeData.__config__.label" placeholder="请输入控件标题" />
+    </el-form-item>
+    <el-form-item label="占位提示">
+      <el-input v-model="activeData.placeholder" placeholder="请输入占位提示" />
+    </el-form-item>
+    <el-form-item label="默认值">
+      <el-input :value="setDefaultValue(activeData.__config__.defaultValue)" placeholder="请输入默认值"
+        @input="onDefaultValueInput" />
+    </el-form-item>
+    <!-- <el-form-item label="显示标签">
+      <el-switch v-model="activeData.__config__.showLabel" />
+    </el-form-item> -->
+  </el-row>
+</template>
+<script>
+import comMixin from './mixin';
+export default {
+  props: ['activeData'],
+  mixins: [comMixin],
+  data() {
+    return {}
+  },
+  created() { },
+  methods: {}
+}
+</script>

+ 749 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/RightPanel.vue

@@ -0,0 +1,749 @@
+<template>
+    <div class="right-board">
+        <el-tabs v-model="currentTab" class="center-tabs">
+            <el-tab-pane label="题目属性" name="field" />
+            <template v-if="!isQuestionnaire">
+                <el-tab-pane label="组件样式" name="style" />
+                <el-tab-pane label="表单属性" name="form" />
+            </template>
+        </el-tabs>
+        <div class="field-box">
+            <el-scrollbar class="right-scrollbar">
+                <!-- 组件属性 -->
+                <el-form v-show="currentTab==='field' && showField" size="small" label-width="90px"
+                    labelPosition="left">
+                    <template v-if="activeData.__config__&&activeData.__config__.jnpfKey">
+                        <template>
+                            <el-form-item label="题目类型"
+                                :label-width="activeData.__config__.jnpfKey==='card'?'76px':''">
+                                <el-input :value="getCompName" disabled />
+                            </el-form-item>
+                            <!-- <el-form-item label="控件字段"
+                                v-if="activeData.__vModel__!==undefined  && !noVModelList.includes(activeData.__config__.jnpfKey)||activeData.isStorage==1">
+                                <el-input v-model="activeData.__vModel__" disabled
+                                    placeholder="请输入数据库字段"
+                                    @change="inputFieldChange($event,activeData.__config__.formId,activeData.__config__.parentVModel)" />
+                            </el-form-item> -->
+                        </template>
+
+                        <!-- 自定义问卷组件 -->
+                        <!-- 问卷标题 -->
+                        <template v-if="activeData.__config__.jnpfKey==='question-title'">
+                            <QTitle :active-data="activeData"></QTitle>
+                        </template>
+                        <!-- 问卷头部提示 -->
+                        <template v-if="activeData.__config__.jnpfKey==='question-tip'">
+                            <QTip :active-data="activeData"></QTip>
+                        </template>
+
+                        <!-- 问卷多行文本 -->
+                        <template v-if="activeData.__config__.jnpfKey==='question-textarea'">
+                            <QTextarea :active-data="activeData"></QTextarea>
+                        </template>
+
+                        <!-- 问卷单选 -->
+                        <template v-if="activeData.__config__.jnpfKey==='question-radio'">
+                            <QRadio :active-data="activeData"></QRadio>
+                        </template>
+
+                        <!-- 问卷多选 -->
+                        <template v-if="activeData.__config__.jnpfKey==='question-checkbox'">
+                            <QCheckbox :active-data="activeData"></QCheckbox>
+                        </template>
+
+                    </template>
+                </el-form>
+            </el-scrollbar>
+        </div>
+    </div>
+</template>
+
+<script>
+import { noVModelList } from '@/components/Generator/generator/comConfig'
+
+
+//引入问卷独有组件
+import {
+    questionnairetopComponents, questionnaireinputComponents, questionnaireselectComponents
+} from './questionnaireComponentConfig'
+
+
+import { isNumberStr } from '@/components/Generator/utils'
+import { saveFormConf, getDrawingList } from '@/components/Generator/utils/db'
+import { getDictionaryTypeSelector } from "@/api/systemData/dictionary"
+import { getDataInterfaceSelector } from "@/api/systemData/dataInterface"
+import { DataModelFieldList } from '@/api/systemData/dataModel'
+import { getPrintDevSelector } from '@/api/system/printDev'
+
+import QTitle from './RightComponents/QTitle.vue'
+import QTip from './RightComponents/QTip.vue'
+import QTextarea from './RightComponents/QTextarea.vue'
+import QRadio from './RightComponents/QRadio.vue'
+import QCheckbox from './RightComponents/QCheckbox.vue'
+
+
+export default {
+    components: {
+        QTitle,
+        QTip,
+        QTextarea,
+        QRadio,
+        QCheckbox
+    },
+    inject: ["getShowType", "getFormInfo"],
+    props: ['showField', 'activeData', 'formConf', 'modelType', 'webType', 'drawingList', 'formInfo', 'isQuestionnaire'],
+    data() {
+        return {
+            currentTab: 'field',
+            activeFunc: '',
+            isConf: false,
+            noVModelList,
+            printTplList: [],
+            fieldOptions: [],
+            dictionaryOptions: [],
+            dataInterfaceOptions: [],
+            layoutTreeProps: {
+                label(data, node) {
+                    const config = data.__config__
+                    return data.componentName || `${config.label}: ${data.__vModel__}`
+                }
+            },
+        }
+    },
+    computed: {
+        showType() {
+            return this.getShowType()
+        },
+        changeName() {
+
+        },
+        activeTag() {
+            return this.activeData.__config__.tag
+        },
+        formItemList: {
+            get() {
+                return this.$store.state.generator.formItemList
+            },
+            set(val) {
+                this.$store.commit('generator/UPDATE_FORMITEM_LIST', val)
+            }
+        },
+        subTable: {
+            get() {
+                return this.$store.state.generator.subTable || []
+            },
+            set(val) {
+                this.$store.commit('generator/UPDATE_SUB_TABLE', val)
+            }
+        },
+        allTable: {
+            get() {
+                return this.$store.state.generator.allTable || []
+            },
+            set(val) {
+                this.$store.commit('generator/SET_ALL_TABLE', val)
+            }
+        },
+        mainTable() {
+            let allTable = this.$store.state.generator.allTable
+            let item = allTable.filter(o => o.typeId == '1')[0]
+            if (!item || !item.table) return ''
+            return item.table
+        },
+        drawingOptions() {
+            let list = []
+            const loop = (data, parent) => {
+                if (!data) return
+                if (data.__config__ && data.__config__.jnpfKey !== 'table' && data.__config__.children && Array.isArray(data.__config__.children)) {
+                    loop(data.__config__.children, data)
+                }
+                if (Array.isArray(data)) data.forEach(d => loop(d, parent))
+                if (data.__vModel__ && data.__config__.jnpfKey !== 'table') {
+                    list.push(data)
+                }
+            }
+            loop(this.drawingList)
+            return list
+        },
+        getCompName() {
+            const allComps = [...questionnairetopComponents, ...questionnaireinputComponents, ...questionnaireselectComponents]
+            const comp = allComps.filter(o => o.__config__.jnpfKey === this.activeData.__config__.jnpfKey);
+            if (!comp.length) return '';
+            return comp[0].__config__.label;
+        },
+        getScriptType() {
+            return ['onLoad', 'beforeSubmit', 'afterSubmit'].includes(this.activeFunc) ? 'form' : 'formField'
+        },
+        enableFlow() {
+            return this.formInfo && this.formInfo.enableFlow || false
+        }
+    },
+    watch: {
+        formConf: {
+            handler(val) {
+                // saveFormConf(val)
+                if (val.formStyle === 'word-form' && val.labelPosition === 'top') {
+                    val.labelPosition = 'left'
+                }
+            },
+            deep: true
+        },
+        activeData(val) {
+            if (!val || !val.__config__) return
+            if (val.__config__.jnpfKey === 'relationFormAttr') {
+                this.$nextTick(() => {
+                    this.$refs.relationFormAttr && this.$refs.relationFormAttr.getOptions()
+                })
+            }
+            if (val.__config__.jnpfKey === 'popupSelect' || val.__config__.jnpfKey === 'popupTableSelect') {
+                this.$nextTick(() => {
+                    this.$refs.popupAll && this.$refs.popupAll.getDataInterfaceInfo()
+                })
+            }
+            if (val.__config__.jnpfKey === 'popupAttr') {
+                this.$nextTick(() => {
+                    this.$refs.popupAttr && this.$refs.popupAttr.getOptions()
+                })
+            }
+            if (val.__config__.jnpfKey !== 'calculate') {
+                this.$nextTick(() => {
+                    this.$refs.calculate && this.$refs.calculate.reloadExpressionTemp()
+                })
+            }
+            if (!val.__config__.tableName && val.__config__.jnpfKey !== 'table') {
+                val.__config__.tableName = this.mainTable
+            }
+            if (!this.activeData.__config__.visibility) this.$set(this.activeData.__config__, "visibility", ["pc", "app"])
+            this.setDefaultOptions()
+        }
+    },
+    created() {
+        this.getDictionaryType()
+        this.getDataInterfaceSelector()
+        this.getPrintTplList()
+        if (!this.activeData || !this.activeData.__config__) return
+        if (!this.activeData.__config__.visibility) this.$set(this.activeData.__config__, "visibility", ["pc", "app"])
+        if (!this.activeData.__config__.tableName && this.activeData.__config__.jnpfKey !== 'table') {
+            this.activeData.__config__.tableName = this.mainTable
+        }
+        this.setDefaultOptions()
+    },
+    methods: {
+        refreshPrintOptions() {
+            getPrintDevSelector(2).then(res => {
+                let data = res.data.list
+                let list = data.filter(o => o.children && o.children.length)
+                this.printTplList = list.map(o => ({
+                    ...o,
+                    hasChildren: true
+                }))
+            }).catch(error => {
+                reject(error)
+            })
+        },
+        // 如果要跳转新页面,请用函数包裹打开新页签。否则会出现被拦截
+        open(url) {
+            window.open(url, "_blank");
+        },
+        // 打印模板快捷入口
+        openPrint() {
+            let routeUrl = this.$router.resolve({
+                path: '/system/printDev?open=true'
+            });
+            this.open(routeUrl.href)
+        },
+
+        updateStyleScript(func) {
+            this.formConf.classJson = func
+            this.formConf.classNames = this.spiltByDoc(func).map(o => o.key)
+        },
+        spiltByDoc(str) {
+            let func = str.trim()
+            let arr = []
+            let cut = func.split("}")
+            cut.forEach(item => {
+                if (item) {
+                    let afterCut = item.split("{")
+                    let classObject = {}
+                    let name = ''
+                    if (afterCut[0].split(" ").length > 1) {
+                        name = afterCut[0].split(" ")[0]
+                    } else {
+                        name = afterCut[0]
+                    }
+                    if (name.split("\.").length > 1) {
+                        name = name.split("\.")[1]
+                    }
+                    name = name.split("\:")[0]
+                    let matching = new RegExp(/^[a-zA-Z]-?.*[a-zA-Z0-9_]*$/)
+                    if (matching.test(name)) {
+                        classObject.key = name.replace(/\r|\n/ig, '').trim()
+                        classObject.value = item.replace(/\r|\n/ig, '') + "}"
+                        arr.push(classObject)
+                    }
+                }
+            })
+            return arr
+        },
+        addReg() {
+            this.activeData.__config__.regList.push({
+                pattern: '',
+                message: ''
+            })
+        },
+        setDefaultValue(val) {
+            if (Array.isArray(val)) {
+                return val.join(',')
+            }
+            // if (['string', 'number'].indexOf(typeof val) > -1) {
+            //   return val
+            // }
+            if (typeof val === 'boolean') {
+                return `${val}`
+            }
+            return val
+        },
+        onDefaultValueInput(str) {
+            if (Array.isArray(this.activeData.__config__.defaultValue)) {
+                // 数组
+                this.$set(
+                    this.activeData.__config__,
+                    'defaultValue',
+                    str.split(',').map(val => (isNumberStr(val) ? +val : val))
+                )
+            } else if (['true', 'false'].indexOf(str) > -1) {
+                // 布尔
+                this.$set(this.activeData.__config__, 'defaultValue', JSON.parse(str))
+            } else {
+                // 字符串和数字
+                this.$set(
+                    this.activeData.__config__,
+                    'defaultValue',
+                    isNumberStr(str) ? +str : str
+                )
+            }
+        },
+        getSubTableFiled(key) {
+            let item = {}
+            let list = this.subTable.filter(o => o.table === key)
+            if (list.length) {
+                item = list[0]
+            }
+            let arr = []
+            if (item && item.fields) arr = item.fields.filter(o => o.primaryKey != 1)
+            return arr
+        },
+        fieldChange1(val) {
+            if (!val) return
+            const drawingList = getDrawingList() || []
+            let boo = false
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    const e = list[i]
+                    const config = e.__config__
+                    if (config.jnpfKey === 'table' && config.tableName === this.activeData.__config__.relationTable) {
+                        for (let j = 0; j < config.children.length; j++) {
+                            const child = config.children[j]
+                            if (child.__vModel__ === val) {
+                                boo = true
+                                break
+                            }
+                        }
+                    }
+                    if (config && config.jnpfKey != 'table' && config.children && Array.isArray(config.children)) {
+                        loop(config.children)
+                    }
+                }
+            }
+            loop(drawingList)
+            if (boo) {
+                this.$message.warning(`子表字段【${val}】已存在,请重新选择!`)
+                this.activeData.__vModel__ = ''
+                return
+            }
+            let options = this.getSubTableFiled(this.activeData.__config__.relationTable)
+            let item = options.filter(o => o.field == val)[0]
+            if (!item || !item.fieldName) return
+            this.activeData.__config__.label = item.fieldName
+        },
+        fieldChange(val) {
+            if (!val) return
+            const drawingList = getDrawingList() || []
+            let boo = false
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    const e = list[i]
+                    const config = e.__config__
+                    if (e.__vModel__ === val) {
+                        boo = true
+                        break
+                    }
+                    if (config && config.jnpfKey != 'table' && config.children && Array.isArray(config.children)) {
+                        loop(config.children)
+                    }
+                }
+            }
+            loop(drawingList)
+            if (boo) {
+                this.$message.warning(`字段【${val}】已存在,请重新选择!`)
+                this.activeData.__vModel__ = ''
+                return
+            }
+            let item = this.fieldOptions.filter(o => o.realField == val)[0]
+            if (!item || !item.fieldName) return
+            this.activeData.__config__.label = item.fieldName
+            this.$forceUpdate()
+        },
+        inputFieldChange(val, formId, parentVModel) {
+            if (!val) return
+            let boo = false
+            let childBoo = false
+            if (parentVModel) {
+                const loop = (data, parent) => {
+                    if (!data) return
+                    if (data.__config__ && data.__config__.children && Array.isArray(data.__config__.children)) {
+                        loop(data.__config__.children, data)
+                    }
+                    if (Array.isArray(data)) data.forEach(d => loop(d, parent))
+                    if (parent && parent.__config__ && parent.__config__.jnpfKey == 'table') {
+                        if (data.__vModel__ && data.__vModel__.toLowerCase() === val.toLowerCase() && data.__config__.formId !== formId && data.__config__.parentVModel === parentVModel) {
+                            childBoo = true
+                            return
+                        }
+                    }
+                }
+                loop(getDrawingList())
+            } else {
+                const loop = (data, parent) => {
+                    if (!data) return
+                    if (data.__config__ && data.__config__.jnpfKey !== 'table' && data.__config__.children && Array.isArray(data.__config__.children)) {
+                        loop(data.__config__.children, data)
+                    }
+                    if (Array.isArray(data)) data.forEach(d => loop(d, parent))
+                    if (data.__vModel__ && data.__vModel__.toLowerCase() === val.toLowerCase() && data.__config__.formId !== formId) {
+                        boo = true
+                        return
+                    }
+                }
+                loop(getDrawingList())
+            }
+            if (boo) {
+                this.$message.warning(`字段【${val}】已存在,请重新输入!`)
+                this.activeData.__vModel__ = ''
+                return
+            }
+            if (childBoo) {
+                this.$message.warning(`子表字段【${val}】已存在,请重新输入!`)
+                this.activeData.__vModel__ = ''
+            }
+        },
+        tableChange() {
+            this.activeData.__vModel__ = ''
+            this.setDefaultOptions()
+        },
+        changeStorage() {
+            this.activeData.__vModel__ = ''
+            if (this.activeData.isStorage == 0) this.activeData.__vModel__ = ''
+            if (!this.$store.getters.hasTable && this.activeData.isStorage == 1) this.$emit('setVModel', this.activeData)
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    const e = list[i]
+                    const config = e.__config__
+                    if (config.jnpfKey === 'table' && e.__vModel__ === this.activeData.__config__.parentVModel) {
+                        this.activeData.__config__.relationTable = config.tableName
+                    }
+                    if (config && config.jnpfKey != 'table' && config.children && Array.isArray(config.children)) {
+                        loop(config.children)
+                    }
+                }
+            }
+            loop(this.drawingList)
+        },
+        setDefaultOptions() {
+            if (!this.$store.getters.hasTable) return
+            if ((this.activeData.__vModel__ === undefined && this.activeData.isStorage != 1) || this.activeData.__config__.jnpfKey === 'table') return
+            if (!this.activeData.__config__.tableName || this.activeData.__config__.tableName === this.mainTable) {
+                let fieldOptions = this.formItemList.map(o => ({ ...o, realField: o.field }))
+                this.fieldOptions = fieldOptions.filter(o => o.primaryKey != 1)
+            } else {
+                let list = this.allTable.filter(o => o.table === this.activeData.__config__.tableName)
+                if (!list.length) {
+                    this.activeData.__config__.tableName = this.mainTable
+                    let fieldOptions = this.formItemList.map(o => ({ ...o, realField: o.field }))
+                    this.fieldOptions = fieldOptions.filter(o => o.primaryKey != 1)
+                    this.activeData.__vModel__ = ''
+                } else {
+                    let item = list[0]
+                    let options = item.fields.map(o => ({
+                        ...o,
+                        realField: 'jnpf_' + this.activeData.__config__.tableName + '_jnpf_' + o.field,
+                    }))
+                    this.fieldOptions = options.filter(o => o.primaryKey != 1)
+                }
+            }
+        },
+        spanChange(val) {
+            this.formConf.span = val
+        },
+        onTableNameChange(tableName) {
+            if (!tableName) return
+            const drawingList = getDrawingList() || []
+            let boo = false
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    const e = list[i]
+                    const config = e.__config__
+                    if (config.jnpfKey === 'table' && config.tableName === tableName) {
+                        boo = true
+                        break
+                    }
+                    if (config && config.jnpfKey != 'table' && config.children && Array.isArray(config.children)) {
+                        loop(config.children)
+                    }
+                }
+            }
+            loop(drawingList)
+            if (boo) {
+                this.$message.warning(`子表【${tableName}】已存在,请重新选择!`)
+                this.activeData.__config__.tableName = ''
+                return
+            }
+            for (let i = 0; i < this.activeData.__config__.children.length; i++) {
+                this.$set(this.activeData.__config__.children[i].__config__, 'relationTable', tableName)
+                this.$set(this.activeData.__config__.children[i], '__vModel__', '')
+            }
+        },
+        onBorderWidthChange(val) {
+            if (!val) this.$nextTick(() => this.$set(this.activeData.__config__, 'borderWidth', 1))
+        },
+        getDictionaryType() {
+            getDictionaryTypeSelector().then(res => {
+                this.dictionaryOptions = res.data.list.filter(o => o.children && o.children.length);
+            })
+        },
+        getDataInterfaceSelector() {
+            getDataInterfaceSelector().then(res => {
+                this.dataInterfaceOptions = res.data
+            })
+        },
+        getPrintTplList() {
+            this.$store.dispatch('base/getPrintFormTree').then(res => {
+                let list = res.filter(o => o.children && o.children.length)
+                this.printTplList = list.map(o => ({
+                    ...o,
+                    hasChildren: true
+                }))
+            })
+        },
+        getTipText(key) {
+            let text = ''
+            switch (key) {
+                case 'change':
+                    text = '发生变化时触发'
+                    break;
+                case 'blur':
+                    text = '失去焦点时触发'
+                    break;
+                case 'click':
+                    text = '点击时触发'
+                    break;
+                case 'tabClick':
+                    text = '面板点击时触发'
+                    break;
+                default:
+                    text = ''
+                    break;
+            }
+            return text
+        },
+        updateScript(data) {
+            if (this.isConf) {
+                this.formConf.funcs[this.activeFunc] = data
+            } else {
+                this.activeData.on[this.activeFunc] = data
+            }
+        },
+
+
+        async updateFieldOptions(data) {
+            let tableName = ''
+            if (!this.activeData.__config__.isSubTable) {
+                tableName = this.activeData.__config__.tableName
+            } else {
+                tableName = this.activeData.__config__.relationTable
+            }
+            let queryType = 0, type = this.getFormInfo().type
+            if (type == 3 || type == 4 || type == 5) queryType = 1
+            let res = await DataModelFieldList(this.getFormInfo().dbLinkId, tableName, queryType)
+            let fields = res.data.list
+            for (let i = 0; i < this.allTable.length; i++) {
+                if (this.allTable[i].table === tableName) {
+                    this.allTable[i].fields = fields
+                    break
+                }
+            }
+            if (!this.activeData.__config__.isSubTable) {
+                if (this.activeData.__config__.tableName === this.mainTable) this.formItemList = fields
+                this.setDefaultOptions()
+            } else {
+                this.subTable = this.allTable.filter(o => o.typeId == '0')
+            }
+        },
+        onBarcodeTextChange(val) {
+            if (!val) return
+            let reg = /^[A-Za-z0-9]+$/
+            if (!reg.test(val)) {
+                this.$message({
+                    message: '固定值请输入数字或者英文字母',
+                    type: 'error',
+                    duration: 1500,
+                })
+            }
+        },
+        onRuleChange(id, item) {
+            if (!id) {
+                this.activeData.__config__.rule = ''
+                this.activeData.__config__.ruleName = ''
+                return
+            }
+            if (this.activeData.__config__.rule === id) return
+            this.activeData.__config__.rule = id
+            this.activeData.__config__.ruleName = item.fullName
+        },
+        addComponent(item, parent) {
+            this.$emit('copyItem', item, parent, false)
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+.printWrap {
+    padding: 10px 0;
+    text-align: center;
+    >>> .el-divider--horizontal {
+        margin: 10px 0;
+    }
+}
+.right-board {
+    width: 340px;
+    position: absolute;
+    right: 0;
+    top: 0;
+    padding-top: 3px;
+    height: 100%;
+    background: #fff;
+    border-radius: 4px;
+    .field-box {
+        position: relative;
+        height: calc(100% - 42px);
+        box-sizing: border-box;
+        overflow: hidden;
+    }
+    .el-scrollbar {
+        height: 100%;
+    }
+}
+>>> .select-item,
+.select-item {
+    display: flex;
+    border: 1px dashed #fff;
+    box-sizing: border-box;
+    & .el-input + .el-input {
+        margin-left: 4px;
+    }
+    & + .select-item {
+        margin-top: 4px;
+    }
+    &.sortable-chosen {
+        border: 1px dashed #409eff;
+    }
+    .select-line-icon {
+        line-height: 32px;
+        font-size: 22px;
+        padding: 0 4px;
+        color: #606266;
+        .icon-ym-darg {
+            font-size: 20px;
+            line-height: 31px;
+            display: inline-block;
+        }
+    }
+    .close-btn {
+        cursor: pointer;
+        color: #f56c6c;
+    }
+    .option-drag {
+        cursor: move;
+    }
+}
+.time-range {
+    .el-date-editor {
+        width: 227px;
+    }
+    ::v-deep .el-icon-time {
+        display: none;
+    }
+}
+.document-link {
+    position: absolute;
+    display: block;
+    width: 26px;
+    height: 26px;
+    top: 0;
+    left: 0;
+    cursor: pointer;
+    background: #409eff;
+    z-index: 1;
+    border-radius: 0 0 6px 0;
+    text-align: center;
+    line-height: 26px;
+    color: #fff;
+    font-size: 18px;
+}
+.node-label {
+    font-size: 14px;
+}
+.node-icon {
+    color: #bebfc3;
+}
+.right-scrollbar {
+    .el-form {
+        >>> .el-date-editor,
+        >>> .el-cascader,
+        >>> .el-select {
+            width: 100%;
+        }
+    }
+}
+.per-cell {
+    display: flex;
+    align-items: center;
+    margin-bottom: 18px;
+    .el-checkbox {
+        width: 146px;
+    }
+    &.last {
+        margin-bottom: 0;
+    }
+}
+</style>
+<style lang="scss">
+.field-select-popper {
+    .el-select-dropdown__empty {
+        span,
+        .el-link {
+            line-height: 16px;
+            vertical-align: top;
+        }
+    }
+}
+.custom-option-left {
+    float: left;
+}
+.custom-option-right {
+    float: right;
+    color: #8492a6;
+    font-size: 13px;
+    margin-left: 20px;
+}
+</style>

+ 86 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/ScriptDemo.vue

@@ -0,0 +1,86 @@
+<template>
+  <el-popover placement="top" title="参数介绍及使用示例" width="454" popper-class="script-demo-popover"
+    trigger="click" @show="onShow">
+    <div class="script-demo-popover-main" :style="{ height: editorHeight }">
+      <JNPFCodeEditor v-model="currentContent" :options="editorOptions" ref="CodeEditor"
+        :key="key" />
+    </div>
+    <template #reference>
+      <el-link type="primary" :underline="false">参数介绍及使用示例</el-link>
+    </template>
+  </el-popover>
+</template>
+
+<script>
+import JNPFCodeEditor from '@/components/JNPFEditor/monaco'
+
+const commonContent = `\n// onlineUtils--在线js工具类\n// 获取用户信息\nconst userInfo = onlineUtils.getUserInfo()\n// 获取设备信息\nconst deviceInfo = onlineUtils.getDeviceInfo()\n// 请求接口(url,method='get',data,headers)\nonlineUtils.request('url', 'get', { param: '1' }, { header: '1' })\n// 路由跳转(url,type) (type仅移动端支持)\nonlineUtils.route('url')\n// 消息提示(message,type='info',duration=3000)\nonlineUtils.toast('message', 'info', 3000)`;
+const formContent = `// formData--表单数据\nconsole.log(formData)\n// setFormData--设置表单某个组件数据(prop,value)\nsetFormData('key', 'value')\n// setShowOrHide--设置显示或隐藏(prop,value)\nsetShowOrHide('key', true)\n// setRequired--设置必填项(prop,value)\nsetRequired('key', true)\n// setDisabled--设置禁用项(prop,value)\nsetDisabled('key', true)`;
+const formFieldContent = `// data--当前组件的选中数据\nconsole.log(data)\n// rowIndex--当前行下标(仅在子表中可用)\nconsole.log(rowIndex)\n` + formContent;
+const btnEventContent = `// data--列表当前行数据\nconsole.log(data)\n// index--列表当前行下标\nconsole.log(index)\n// refresh--刷新列表\nrefresh()`;
+const afterOnloadContent = `// data--列表行数据\nconsole.log(data)\n// tableRef--表格DOM元素\nconsole.log(tableRef)`;
+const rowStyleContent = `// row--列表行数据\n// rowIndex--列表行下标\n({ row, rowIndex }) => {\r\n   return {\r\n      background: rowIndex%2==0 ? 'red' : 'blue'\r\n   }\r\n}`;
+const cellStyleContent = `// row--列表行数据\n// column--列表列数据\n// rowIndex--列表行下标\n// columnIndex--列表列下标\n({ row, column, rowIndex, columnIndex }) => {\r\n    return {\r\n        color: rowIndex%2 == 0 ? 'blue' : 'red'\r\n    }\r\n}`;
+
+export default {
+  components: { JNPFCodeEditor },
+  props: {
+    type: {
+      type: String,
+      default: 'formField',
+    },
+  },
+  data() {
+    return {
+      currentContent: '',
+      editorOptions: {
+        language: 'javascript',
+        readOnly: true,
+        lineNumbers: 'off',
+        minimap: { enabled: false },
+        scrollBeyondLastLine: false,
+        glyphMargin: 0
+      },
+      editorHeight: '500px',
+      key: 0
+    }
+  },
+  methods: {
+    onShow() {
+      if (this.type === 'form') {
+        this.currentContent = formContent + commonContent;
+        this.editorHeight = '420px';
+      } else if (this.type === 'btnEvent') {
+        this.currentContent = btnEventContent + commonContent;
+        this.editorHeight = '350px';
+      } else if (this.type === 'afterOnload') {
+        this.currentContent = afterOnloadContent + commonContent;
+        this.editorHeight = '310px';
+      } else if (this.type === 'rowStyle') {
+        this.currentContent = rowStyleContent;
+        this.editorHeight = '140px';
+      } else if (this.type === 'cellStyle') {
+        this.currentContent = cellStyleContent;
+        this.editorHeight = '180px';
+      } else {
+        this.currentContent = formFieldContent + commonContent;
+        this.editorHeight = '500px';
+      }
+      this.$nextTick(() => {
+        this.key = +new Date()
+        this.$refs.CodeEditor.changeEditor({
+          value: this.currentContent,
+          options: this.editorOptions
+        })
+      });
+    }
+  }
+}
+</script>
+<style lang="scss">
+.script-demo-popover {
+  .script-demo-popover-main {
+    width: 440px;
+  }
+}
+</style>

+ 65 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/StyleScript.vue

@@ -0,0 +1,65 @@
+<template>
+  <el-dialog :close-on-click-modal="false" class="JNPF-dialog JNPF-dialog_center form-script-dialog"
+    lock-scroll append-to-body v-bind="$attrs" width="1000px" :modal-append-to-body="false"
+    title="表单样式" v-on="$listeners" @open="onOpen">
+    <div class="form-script-dialog-body">
+      <div class="right-main">
+        <div class="codeEditor">
+          <JNPFCodeEditor v-model="text" :options="options" ref="CodeEditor" />
+        </div>
+        <div class="tips">
+          <p>示例:</p>
+          <p>.text{</p>
+          <p style="text-indent: 2em">
+            background-color: #fff; //背景颜色
+          </p>
+          <p style="text-indent: 2em">
+            font-size: 18px; //字体大小
+          </p>
+          <p>
+            }
+          </p>
+        </div>
+      </div>
+    </div>
+
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="closeDialog">{{$t('common.cancelButton')}}</el-button>
+      <el-button type="primary" @click="onClose()">{{$t('common.confirmButton')}}</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import JNPFCodeEditor from '@/components/JNPFEditor/monaco'
+export default {
+  components: { JNPFCodeEditor },
+  props: ['value'],
+  data() {
+    return {
+      text: '',
+      options: {
+        language: 'css'
+      },
+    }
+  },
+  methods: {
+    onOpen() {
+      this.text = this.value
+      this.$nextTick(() => {
+        this.$refs.CodeEditor.changeEditor({
+          value: this.text,
+          options: this.options
+        })
+      });
+    },
+    onClose() {
+      this.$emit('updateStyleScript', this.text)
+      this.closeDialog()
+    },
+    closeDialog() {
+      this.$emit('update:visible', false)
+    }
+  }
+}
+</script>

+ 108 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/index/questionnaireComponentConfig.js

@@ -0,0 +1,108 @@
+// 输入控件 【左面板】
+export const questionnairetopComponents = [
+  {
+    __config__: {
+      jnpfKey: "question-title",
+      label: "标题",
+      showLabel: true,
+      tag: "QuestionnaireInput",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "questionTopItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    placeholder: "请输入",
+    disabled: false, // 是否不可编辑
+  },
+  {
+    __config__: {
+      jnpfKey: "question-tip",
+      label: "提示信息",
+      showLabel: true,
+      tag: "QuestionnaireTopTip",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      tableAlign: 'left',
+      tableFixed: 'none',
+      layout: "questionTopItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      valueType: null, // 默认值类型(1 与发起人相关 2 字典)
+      isApi: null, // 选择项数据源类型(1 api 2 字典)
+      fieldApi: null, // 选择项数据源
+      required: false,// 是否必填
+      noShow: false,// 是否隐藏
+      isDisplay: true,// 是否提交(1 是 2否)否:字段不给前端 processFormSubmit
+    },
+    placeholder: "请输入",
+    disabled: false, // 是否不可编辑
+  },
+];
+
+
+// 输入控件 【左面板】
+export const questionnaireinputComponents = [
+  {
+    __config__: {
+      jnpfKey: "question-textarea",
+      label: "多行输入",
+      showLabel: false,
+      tag: "QuestionnaireTextarea",
+      tagIcon: "icon-ym icon-ym-generator-textarea",
+      layout: "questionItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      required: true,// 是否必填
+    },
+    title: '请输入题目标题(默认)',
+    describe: ''
+  },
+];
+
+//选择控件 【左面板】
+export const questionnaireselectComponents = [
+  {
+    __config__: {
+      jnpfKey: "question-radio",
+      label: "单选",
+      showLabel: false,
+      tag: "QuestionnaireRadio",
+      tagIcon: "icon-ym icon-ym-generator-radio",
+      layout: "questionItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: null,// 默认值
+      required: true,// 是否必填
+    },
+    options: [],
+    title: '请输入题目标题(默认)',
+    describe: ''
+  },
+  {
+    __config__: {
+      jnpfKey: "question-checkbox",
+      label: "多选",
+      showLabel: false,
+      tag: "QuestionnaireCheckbox",
+      tagIcon: "icon-ym icon-ym-generator-checkbox",
+      layout: "questionItem",
+      span: 24,
+      dragDisabled: false,
+      defaultValue: [],// 默认值
+      required: true,// 是否必填
+    },
+    options: [],
+    title: '请输入题目标题(默认)',
+    describe: ''
+  },
+];

+ 1077 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/Parser.vue

@@ -0,0 +1,1077 @@
+<script>
+import { deepClone } from '@/utils'
+import { getDateDay, getLaterData, getBeforeData, getBeforeTime, getLaterTime } from '@/components/Generator/utils/index.js'
+import jnpf from '@/utils/jnpf'
+import render from '@/components/Generator/render/render.js'
+import { dyOptionsList } from '@/components/Generator/generator/comConfig'
+import { getDataInterfaceRes } from '@/api/systemData/dataInterface'
+import { mapGetters } from "vuex"
+
+const formClass = 'form-' + jnpf.idGenerator()
+
+const layouts = {
+    colFormItem(h, scheme) {
+        console.log(scheme)
+        const config = scheme.__config__
+        const listeners = buildListeners.call(this, scheme)
+        let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
+        let label = config.label ? config.label + (this.formConf.labelSuffix || '') : ''
+        if (config.showLabel === false) labelWidth = '0'
+        const Item = config.jnpfKey === 'cascader'
+            ? <JnpfCascader props={{ props: scheme.props }} v-model={this[this.formConf.formModel][scheme.__vModel__]} placeholder={scheme.placeholder} options={scheme.options}
+                disabled={scheme.disabled} show-all-levels={scheme.showAllLevels} separator={scheme.separator}
+                style={scheme.style} clearable={scheme.clearable} filterable={scheme.filterable} multiple={scheme.multiple}
+                onChange={val => this.onCascaderChange(val, scheme.on)} onBlur={val => this.onCascaderBlur(val, scheme.on)}
+                key={scheme.__config__.renderKey}></JnpfCascader>
+            : <render formData={this[this.formConf.formModel]} conf={scheme} {...{ on: listeners }} ref={config.jnpfKey === 'table' ? scheme.__vModel__ : undefined}
+                key={scheme.__config__.renderKey} relations={config.jnpfKey === 'table' ? this.relations : undefined} />
+        const visibility = !config.visibility || (Array.isArray(config.visibility) && config.visibility.includes('pc'))
+        if (visibility && !config.noShow) {
+            let toolTip = <el-col span={config.span} class={config.className}>
+                <jnpf-form-tip-item label-width={labelWidth} prop={scheme.__vModel__} key={config.renderKey} tip-label={config.label && config.showLabel ? config.tipLabel : ""}
+                    label={config.showLabel ? label : ''}>
+                    {Item}
+                </jnpf-form-tip-item>
+            </el-col>
+            if (config.jnpfKey === 'alert') {
+                toolTip = <el-col span={config.span} class={config.className}>
+                    <jnpf-form-tip-item label-width={labelWidth} prop={scheme.__vModel__} key={config.renderKey} tip-label={config.label && config.showLabel ? config.tipLabel : ""}
+                        label={config.showLabel ? config.label : ''}>
+                        <div style="word-break: break-all">
+                            {Item}
+                        </div>
+                    </jnpf-form-tip-item>
+                </el-col>
+            }
+            if (config.jnpfKey === 'table') {
+                toolTip = <el-col span={config.span} class={config.className}>
+                    <el-form-item label-width={labelWidth} prop={scheme.__vModel__} key={config.renderKey}
+                        label={config.showLabel ? config.label : ''}>
+                        {Item}
+                    </el-form-item >
+                </el-col>
+            }
+            return (
+                toolTip
+            )
+        }
+    },
+    rowFormItem(h, scheme) {
+        const listeners = buildListeners.call(this, scheme)
+        const config = scheme.__config__
+        const visibility = !config.visibility || (Array.isArray(config.visibility) && config.visibility.includes('pc'))
+        if (!visibility || config.noShow) return
+        if (scheme.__config__.jnpfKey === 'tab') {
+            return (
+                <el-col span={scheme.__config__.span} class="mb-10">
+                    <el-tabs type={scheme.type} tab-position={scheme.tabPosition} vModel={scheme.__config__.active} {...{ on: listeners }}>
+                        {
+                            scheme.__config__.children.map((item, i) => {
+                                let child = renderChildren.call(this, h, item)
+                                return (
+                                    <el-tab-pane name={item.name} label={item.title} >
+                                        <el-row>
+                                            {child}
+                                        </el-row>
+                                    </el-tab-pane>
+                                )
+                            })
+                        }
+                    </el-tabs>
+                </el-col>
+            )
+        }
+        if (scheme.__config__.jnpfKey === 'collapse') {
+            return (
+                <el-col span={scheme.__config__.span} class="mb-20">
+                    <el-collapse vModel={scheme.__config__.active} accordion={scheme.accordion} {...{ on: listeners }}>
+                        {
+                            scheme.__config__.children.map((item, i) => {
+                                let child = renderChildren.call(this, h, item)
+                                return (
+                                    <el-collapse-item key={item.name} title={item.title} name={item.name} >
+                                        <el-row>
+                                            {child}
+                                        </el-row>
+                                    </el-collapse-item>
+                                )
+                            })
+                        }
+                    </el-collapse>
+                </el-col>
+            )
+        }
+        if (scheme.__config__.jnpfKey === 'tableGrid') {
+            return (
+                <el-col span={scheme.__config__.span} >
+                    <el-row gutter={scheme.__config__.gutter}>
+                        <table class="table-grid-box"
+                            style={{ '--borderType': scheme.__config__.borderType, '--borderColor': scheme.__config__.borderColor, '--borderWidth': scheme.__config__.borderWidth + 'px' }}>
+                            <tbody>
+                                {
+                                    scheme.__config__.children.map((item) => {
+                                        return (
+                                            <tr>
+                                                {
+                                                    item.__config__.children.map((it, colIndex) => {
+                                                        let child = renderChildren.call(this, h, it)
+                                                        return !it.__config__.merged ? (
+                                                            <td colspan={it.__config__.colspan || 1} rowspan={it.__config__.rowspan || 1} style={{ '--backgroundColor': it.__config__.backgroundColor }}>
+                                                                <el-col>
+                                                                    <el-row gutter={scheme.__config__.gutter} >
+                                                                        {child}
+                                                                    </el-row>
+                                                                </el-col>
+                                                            </td>
+                                                        ) : ''
+                                                    })
+                                                }
+                                            </tr>
+                                        )
+                                    })
+                                }
+                            </tbody>
+                        </table>
+                    </el-row>
+                </el-col>
+            )
+        }
+        let child = renderChildren.apply(this, arguments)
+        if (scheme.__config__.jnpfKey === 'table') {
+            if (!scheme.__config__.noShow) this.tableRefs[scheme.__vModel__] = scheme
+            const param = { ...scheme, config: scheme }
+            return layouts.colFormItem.call(this, h, param)
+        }
+        if (scheme.__config__.jnpfKey === 'card') {
+            let toolTip = scheme.header
+            if (scheme.__config__.tipLabel) {
+                toolTip = <span slot="label">{scheme.header}
+                    <el-tooltip placement="top" content={scheme.__config__.tipLabel}>
+                        <a class='el-icon-question tooltip-question' style='margin-left:4px'></a>
+                    </el-tooltip>
+                </span >
+            }
+            let header = ''
+            if (scheme.header) {
+                header = <div slot="header" ><span>{toolTip}</span></div>
+            }
+            return (
+                <el-col span={scheme.__config__.span} class="item-card">
+                    <el-card shadow={scheme.shadow} header={scheme.header} class="mb-20">
+                        {header}
+                        {child}
+                    </el-card>
+                </el-col>
+            )
+        }
+        if (scheme.__config__.jnpfKey === 'row') {
+            if (scheme.type === 'flex') {
+                child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}>
+                    {child}
+                </el-row>
+            }
+            return (
+                <el-col span={scheme.__config__.span}>
+                    <el-row gutter={scheme.gutter}>
+                        {child}
+                    </el-row>
+                </el-col>
+            )
+        }
+    }
+}
+
+function renderFrom(h) {
+    const { formConfCopy } = this
+    let classStyle = [formClass]
+    if (formConfCopy.formStyle) classStyle.push(formConfCopy.formStyle)
+    if (formConfCopy.className) classStyle = [...classStyle, ...formConfCopy.className]
+    return (
+        <el-row gutter={formConfCopy.gutter} class={classStyle}>
+            <el-form
+                size={formConfCopy.size}
+                label-position={formConfCopy.labelPosition}
+                disabled={formConfCopy.disabled}
+                label-width={`${formConfCopy.labelWidth}px`}
+                ref={formConfCopy.formRef}
+                // model不能直接赋值 https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
+                props={{ model: this[formConfCopy.formModel] }}
+                rules={this[formConfCopy.formRules]}
+                nativeOnSubmit={event => { event.preventDefault() }}
+            >
+                {renderFormItem.call(this, h, formConfCopy.fields)}
+                {
+                    // {formConfCopy.formBtns && formBtns.call(this, h)}
+                }
+            </el-form>
+        </el-row>
+    )
+}
+
+function formBtns(h) {
+    return <el-col>
+        <el-form-item size="large">
+            <el-button type="primary" onClick={this.submitForm}>提交</el-button>
+            <el-button onClick={this.resetForm}>重置</el-button>
+        </el-form-item>
+    </el-col>
+}
+
+function renderFormItem(h, elementList) {
+    return elementList.map(scheme => {
+        const config = scheme.__config__
+        const layout = layouts[config.layout]
+
+        if (layout) {
+            return layout.call(this, h, scheme)
+        }
+        throw new Error(`没有与${config.layout}匹配的layout`)
+    })
+}
+
+function renderChildren(h, scheme) {
+    const config = scheme.__config__
+    if (!Array.isArray(config.children)) return null
+    return renderFormItem.call(this, h, config.children)
+}
+
+function setValue(event, config, scheme) {
+    this.$set(config, 'defaultValue', event)
+    this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
+}
+
+function buildListeners(scheme) {
+
+    const config = scheme.__config__
+    const listeners = {}
+    if (scheme.on) {
+        // 响应 组件事件
+        Object.keys(scheme.on).forEach(key => {
+            const str = scheme.on[key];
+            const func = this.jnpf.getScriptFunc.call(this, str);
+            if (!func) return
+            listeners[key] = params => {
+                if (key === 'change') {
+                    let data = ''
+                    if (['select', 'radio', 'checkbox'].includes(config.jnpfKey)) {
+                        const options = scheme.options
+                        if (scheme.multiple || config.jnpfKey === 'checkbox') {
+                            let _data = []
+                            outer: for (let i = 0; i < params[0].length; i++) {
+                                inner: for (let j = 0; j < options.length; j++) {
+                                    if (params[0][i] === options[j][scheme.props.value]) {
+                                        _data.push(options[j])
+                                        break inner
+                                    }
+                                }
+                            }
+                            data = _data
+                        } else {
+                            let _data = {}
+                            for (let i = 0; i < options.length; i++) {
+                                if (params[0] === options[i][scheme.props.value]) {
+                                    _data = options[i]
+                                    break
+                                }
+                            }
+                            data = _data
+                        }
+                    } else if (config.jnpfKey === 'inputNumber') {
+                        data = params[0]
+                    } else {
+                        data = params.length > 1 ? params[1] : params[0]
+                    }
+                    if (config.jnpfKey === 'popupSelect' || config.jnpfKey === 'relationForm') this.setTransferFormData(data, config, config.jnpfKey)
+                    func.call(this, { data, ...this.parameter })
+                    this.handleRelation(scheme.__vModel__)
+                } else {
+                    func.call(this, { data: params[0], ...this.parameter })
+                }
+            }
+        })
+    }
+    // 响应 render.js 中的 vModel $emit('input', val)
+    listeners.input = event => setValue.call(this, event, config, scheme)
+
+    return listeners
+}
+
+export default {
+    components: {
+        render
+    },
+    props: {
+        formConf: {
+            type: Object,
+            required: true
+        }
+    },
+    data() {
+        const data = {
+            formConfCopy: deepClone(this.formConf),
+            [this.formConf.formModel]: {},
+            [this.formConf.formRules]: {},
+            options: {},
+            tableRefs: {},
+            relations: {},
+            isTableValid: false
+        }
+        this.initCss(data.formConfCopy)
+        this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
+        this.initRelationForm(data.formConfCopy.fields)
+        this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
+        this.buildOptions(data.formConfCopy.fields, data.options, data[this.formConf.formModel])
+        this.buildRelations(data.formConfCopy.fields, data.relations)
+        this.$nextTick(() => {
+            this.onLoad(data.formConfCopy)
+        })
+        return data
+    },
+    provide() {
+        return {
+            parameter: this.parameter
+        }
+    },
+    computed: {
+        ...mapGetters(['userInfo']),
+        formDataConf() {
+            const name = this.formConf.formModel
+            return this[name]
+        },
+        parameter() {
+            const oldFormData = this.formConfCopy.formData ? this.formConfCopy.formData : {}
+            this[this.formConf.formModel].id = oldFormData.id || ''
+            this[this.formConf.formModel].flowId = oldFormData.flowId || ''
+            return {
+                formData: this[this.formConf.formModel],
+                setFormData: this.setFormData,
+                setShowOrHide: this.setShowOrHide,
+                setRequired: this.setRequired,
+                setDisabled: this.setDisabled,
+                onlineUtils: this.jnpf.onlineUtils,
+            }
+        }
+    },
+    mounted() {
+        this.initRelationData()
+        this.$nextTick(() => {
+            this.$refs[this.formConf.formRef].clearValidate()
+        })
+    },
+    methods: {
+        setTransferFormData(data, config, jnpfKey) {
+            if (!config.transferList || !config.transferList.length) return
+            for (let index = 0; index < config.transferList.length; index++) {
+                const element = config.transferList[index];
+                this.setFormData(element.sourceValue, data[element.targetField])
+            }
+        },
+        initRelationData() {
+            const handleRelationFun = (list) => {
+                list.forEach(cur => {
+                    this.handleDefaultRelation(cur.__vModel__)
+                    if (cur.__config__.children) handleRelationFun(cur.__config__.children)
+                })
+            }
+            handleRelationFun(this.formConfCopy.fields)
+        },
+        initCss(formCopy) {
+            if (document.getElementById('styleId')) {
+                document.getElementById('styleId').remove()
+            }
+            let head = document.getElementsByTagName('head')[0]
+            let style = document.createElement('style')
+            style.type = 'text/css'
+            style.id = 'styleId'
+            style.innerText = this.buildCSS(formCopy.classJson)
+            head.appendChild(style)
+        },
+        buildCSS(str) {
+            str = str.trim();
+            let newStr = '';
+            let cut = str.split('}');
+            cut.forEach(item => {
+                if (item) {
+                    item = '.' + formClass + ' ' + item + '}';
+                    newStr += item;
+                }
+            });
+            return newStr;
+        },
+        initFormData(componentList, formData) {
+            this.$store.commit('generator/UPDATE_RELATION_DATA', {})
+            componentList.forEach(cur => {
+                const config = cur.__config__
+                if (cur.__vModel__) formData[cur.__vModel__] = config.defaultValue
+                if (cur.__config__.jnpfKey == 'table') return
+                if (config.children) this.initFormData(config.children, formData)
+            })
+        },
+        buildRelations(componentList, relations) {
+            componentList.forEach(cur => {
+                const config = cur.__config__
+                if (dyOptionsList.indexOf(config.jnpfKey) > -1) {
+                    if (config.dataType === 'dynamic') {
+                        if (config.templateJson && config.templateJson.length) {
+                            for (let i = 0; i < config.templateJson.length; i++) {
+                                const e = config.templateJson[i];
+                                if (e.relationField) {
+                                    let item = {
+                                        ...cur,
+                                        realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                                        opType: 'setOptions'
+                                    }
+                                    if (relations.hasOwnProperty(e.relationField)) {
+                                        let boo = relations[e.relationField].some(o => o.realVModel === cur.realVModel)
+                                        if (!boo) {
+                                            relations[e.relationField].push(item)
+                                        }
+                                    } else {
+                                        relations[e.relationField] = [item]
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                if (config.jnpfKey === 'userSelect' && ['dep', 'pos', 'role', 'group'].includes(cur.selectType)) {
+                    if (cur.relationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setUserOptions'
+                        }
+                        if (relations.hasOwnProperty(cur.relationField)) {
+                            let boo = relations[cur.relationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.relationField].push(item)
+                            }
+                        } else {
+                            relations[cur.relationField] = [item]
+                        }
+
+                    }
+                }
+                if (config.jnpfKey === 'areaSelect' && ['address', 'cities'].includes(cur.selectType)) {
+                    if (cur.relationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setAddressOptions'
+                        }
+                        if (relations.hasOwnProperty(cur.relationField)) {
+                            let boo = relations[cur.relationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.relationField].push(item)
+                            }
+                        } else {
+                            relations[cur.relationField] = [item]
+                        }
+
+                    }
+                }
+                if (config.jnpfKey === 'popupSelect') {
+                    if (cur.templateJson && cur.templateJson.length) {
+                        for (let i = 0; i < cur.templateJson.length; i++) {
+                            const e = cur.templateJson[i];
+                            if (e.relationField) {
+                                let item = {
+                                    ...cur,
+                                    realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                                    opType: 'setPopupOptions'
+                                }
+                                if (relations.hasOwnProperty(e.relationField)) {
+                                    let boo = relations[e.relationField].some(o => o.realVModel === cur.realVModel)
+                                    if (!boo) {
+                                        relations[e.relationField].push(item)
+                                    }
+                                } else {
+                                    relations[e.relationField] = [item]
+                                }
+                            }
+                        }
+                    }
+                }
+                if (config.jnpfKey === 'datePicker') {
+                    if (config.startTimeRule) {
+                        if (config.startTimeType == 1) {
+                            cur.startTime = config.startTimeValue
+                        } else if (config.startTimeType == 3) {
+                            cur.startTime = new Date().getTime()
+                        } else if (config.startTimeType == 4) {
+                            let previousDate = '';
+                            if (config.startTimeTarget == 1 || config.startTimeTarget == 2) {
+                                previousDate = getDateDay(config.startTimeTarget, config.startTimeType, config.startTimeValue)
+                                cur.startTime = new Date(previousDate).getTime()
+                            } else if (config.startTimeTarget == 3) {
+                                previousDate = getBeforeData(config.startTimeValue)
+                                cur.startTime = new Date(previousDate).getTime()
+                            } else {
+                                cur.startTime = getBeforeTime(config.startTimeTarget, config.startTimeValue).getTime()
+                            }
+                        } else if (config.startTimeType == 5) {
+                            let previousDate = '';
+                            if (config.startTimeTarget == 1 || config.startTimeTarget == 2) {
+                                previousDate = getDateDay(config.startTimeTarget, config.startTimeType, config.startTimeValue)
+                                cur.startTime = new Date(previousDate).getTime()
+                            } else if (config.startTimeTarget == 3) {
+                                previousDate = getLaterData(config.startTimeValue)
+                                cur.startTime = new Date(previousDate).getTime()
+                            } else {
+                                cur.startTime = getLaterTime(config.startTimeTarget, config.startTimeValue).getTime()
+                            }
+                        }
+                    }
+                    if (config.endTimeRule) {
+                        if (config.endTimeType == 1) {
+                            cur.endTime = config.endTimeValue
+                        } else if (config.endTimeType == 3) {
+                            cur.endTime = new Date().getTime()
+                        } else if (config.endTimeType == 4) {
+                            let previousDate = '';
+                            if (config.endTimeTarget == 1 || config.endTimeTarget == 2) {
+                                previousDate = getDateDay(config.endTimeTarget, config.endTimeType, config.endTimeValue)
+                                cur.endTime = new Date(previousDate).getTime()
+                            } else if (config.endTimeTarget == 3) {
+                                previousDate = getBeforeData(config.endTimeValue)
+                                cur.endTime = new Date(previousDate).getTime()
+                            } else {
+                                cur.endTime = getBeforeTime(config.endTimeTarget, config.endTimeValue).getTime()
+                            }
+                        } else if (config.endTimeType == 5) {
+                            let previousDate = '';
+                            if (config.endTimeTarget == 1 || config.endTimeTarget == 2) {
+                                previousDate = getDateDay(config.endTimeTarget, config.endTimeType, config.endTimeValue)
+                                cur.endTime = new Date(previousDate).getTime()
+                            } else if (config.endTimeTarget == 3) {
+                                previousDate = getLaterData(config.endTimeValue)
+                                cur.endTime = new Date(previousDate).getTime()
+                            } else {
+                                cur.endTime = getLaterTime(config.endTimeTarget, config.endTimeValue).getTime()
+                            }
+                        }
+                    }
+                    if (cur.__config__.startRelationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setDate'
+                        }
+                        if (relations.hasOwnProperty(cur.__config__.startRelationField)) {
+                            let boo = relations[cur.__config__.startRelationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.__config__.startRelationField].push(item)
+                            }
+                        } else {
+                            relations[cur.__config__.startRelationField] = [item]
+                        }
+                    }
+                    if (cur.__config__.endRelationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setDate'
+                        }
+                        if (relations.hasOwnProperty(cur.__config__.endRelationField)) {
+                            let boo = relations[cur.__config__.endRelationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.__config__.endRelationField].push(item)
+                            }
+                        } else {
+                            relations[cur.__config__.endRelationField] = [item]
+                        }
+                    }
+                }
+                if (config.jnpfKey === 'timePicker') {
+                    let format = cur.format === 'HH:mm' ? 'HH:mm:00' : cur.format
+                    if (config.startTimeRule) {
+                        if (config.startTimeType == 1) {
+                            cur.startTime = config.startTimeValue || '00:00:00'
+                            if (cur.startTime.split(':').length == 3) {
+                                cur.startTime = cur.startTime
+                            } else {
+                                cur.startTime = cur.startTime + ':00'
+                            }
+                        } else if (config.startTimeType == 3) {
+                            cur.startTime = this.jnpf.toDate(new Date(), format)
+                        } else if (config.startTimeType == 4) {
+                            let previousDate = '';
+                            previousDate = getBeforeTime(config.startTimeTarget, config.startTimeValue)
+                            cur.startTime = this.jnpf.toDate(previousDate, format)
+                        } else if (config.startTimeType == 5) {
+                            let previousDate = '';
+                            previousDate = getLaterTime(config.startTimeTarget, config.startTimeValue)
+                            cur.startTime = this.jnpf.toDate(previousDate, format)
+                        }
+                    }
+                    if (config.endTimeRule) {
+                        if (config.endTimeType == 1) {
+                            cur.endTime = config.endTimeValue || '23:59:59'
+                            if (cur.endTime.split(':').length == 3) {
+                                cur.endTime = cur.endTime
+                            } else {
+                                cur.endTime = cur.endTime + ':00'
+                            }
+                        } else if (config.endTimeType == 3) {
+                            cur.endTime = this.jnpf.toDate(new Date(), format)
+                        } else if (config.endTimeType == 4) {
+                            let previousDate = '';
+                            previousDate = getBeforeTime(config.endTimeTarget, config.endTimeValue)
+                            cur.endTime = this.jnpf.toDate(previousDate, format)
+                        } else if (config.endTimeType == 5) {
+                            let previousDate = '';
+                            previousDate = getLaterTime(config.endTimeTarget, config.endTimeValue)
+                            cur.endTime = this.jnpf.toDate(previousDate, format)
+                        }
+                    }
+                    if (cur.__config__.startRelationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setTime'
+                        }
+                        if (relations.hasOwnProperty(cur.__config__.startRelationField)) {
+                            let boo = relations[cur.__config__.startRelationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.__config__.startRelationField].push(item)
+                            }
+                        } else {
+                            relations[cur.__config__.startRelationField] = [item]
+                        }
+                    }
+                    if (cur.__config__.endRelationField) {
+                        let item = {
+                            ...cur,
+                            realVModel: cur.__config__.isSubTable ? cur.__config__.parentVModel + '-' + cur.__vModel__ : cur.__vModel__,
+                            opType: 'setTime'
+                        }
+                        if (relations.hasOwnProperty(cur.__config__.endRelationField)) {
+                            let boo = relations[cur.__config__.endRelationField].some(o => o.realVModel === cur.realVModel)
+                            if (!boo) {
+                                relations[cur.__config__.endRelationField].push(item)
+                            }
+                        } else {
+                            relations[cur.__config__.endRelationField] = [item]
+                        }
+                    }
+                }
+                if (config.children) this.buildRelations(config.children, relations)
+            })
+        },
+        handleRelation(field) {
+            if (!field) return
+            const currRelations = this.relations
+            for (let key in currRelations) {
+                if (key === field) {
+                    for (let i = 0; i < currRelations[key].length; i++) {
+                        const e = currRelations[key][i];
+                        let vModel = e.realVModel || e.__vModel__
+                        const config = e.__config__
+                        const jnpfKey = config.jnpfKey
+                        let defaultValue = ''
+                        if (['checkbox', 'cascader'].includes(jnpfKey) || (['select', 'treeSelect', 'popupSelect', 'popupTableSelect', 'userSelect'].includes(jnpfKey) && e.multiple)) {
+                            defaultValue = []
+                        }
+                        if (vModel.includes('-')) {
+                            // 子表字段
+                            const tableVModel = vModel.split('-')[0]
+                            this.$refs[tableVModel] && this.$refs[tableVModel].$children[0] && this.$refs[tableVModel].$children[0].handleRelationForParent(e, defaultValue)
+                        } else {
+                            this.setFormData(e.__vModel__, defaultValue)
+                            if (e.opType === 'setOptions') {
+                                let query = {
+                                    paramList: this.getParamList(config.templateJson, this[this.formConf.formModel])
+                                }
+                                getDataInterfaceRes(config.propsUrl, query).then(res => {
+                                    let data = res.data
+                                    this.setFieldOptions(e.__vModel__, data)
+                                })
+                            }
+                            if (e.opType === 'setUserOptions') {
+                                let value = this[this.formConf.formModel][e.relationField] || []
+                                this.comSet('ableRelationIds', e.__vModel__, Array.isArray(value) ? value : [value])
+                            }
+                            if (e.opType === 'setAddressOptions') {
+                                let value = this[this.formConf.formModel][e.relationField] || []
+                                this.comSet('ableAddressIds', e.__vModel__, Array.isArray(value) ? value : [value])
+                            }
+                            if (e.opType === 'setPopupOptions') { }
+
+                            if (e.opType === 'setDate') {
+                                let startTime = ''
+                                let endTime = ''
+                                if (e.__config__.startTimeType == 2) {
+                                    startTime = this[this.formConf.formModel][e.__config__.startRelationField] || 0
+                                } else {
+                                    startTime = e.startTime
+                                }
+                                if (e.__config__.endTimeType == 2) {
+                                    endTime = this[this.formConf.formModel][e.__config__.endRelationField] || 0
+                                } else {
+                                    endTime = e.endTime
+                                }
+                                this.comSet('startTime', e.__vModel__, startTime)
+                                this.comSet('endTime', e.__vModel__, endTime)
+                            }
+                            if (e.opType === 'setTime') {
+                                let startTime = ''
+                                let endTime = ''
+                                if (e.__config__.startTimeType == 2) {
+                                    startTime = this[this.formConf.formModel][e.__config__.startRelationField] || '00:00:00'
+                                    if (startTime && (startTime.split(':').length == 3)) {
+                                        startTime = startTime
+                                    } else {
+                                        startTime = startTime + ':00'
+                                    }
+                                } else {
+                                    startTime = e.startTime
+                                }
+                                if (e.__config__.endTimeType == 2) {
+                                    endTime = this[this.formConf.formModel][e.__config__.endRelationField] || '23:59:59'
+                                    if (endTime && (endTime.split(':').length == 3)) {
+                                        endTime = endTime
+                                    } else {
+                                        endTime = endTime + ':00'
+                                    }
+                                } else {
+                                    endTime = e.endTime
+                                }
+                                this.comSet('startTime', e.__vModel__, startTime)
+                                this.comSet('endTime', e.__vModel__, endTime)
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        handleDefaultRelation(field) {
+            if (!field) return
+            const currRelations = this.relations
+            for (let key in currRelations) {
+                if (key === field) {
+                    for (let i = 0; i < currRelations[key].length; i++) {
+                        const e = currRelations[key][i];
+                        let vModel = e.realVModel || e.__vModel__
+                        const config = e.__config__
+                        if (vModel.includes('-')) {
+                            const tableVModel = vModel.split('-')[0]
+                            this.$refs[tableVModel] && this.$refs[tableVModel].$children[0] && this.$refs[tableVModel].$children[0].handleRelationForParent(e, '', true)
+                        } else {
+                            if (e.opType === 'setUserOptions') {
+                                let value = this[this.formConf.formModel][e.relationField] || []
+                                this.comSet('ableRelationIds', e.__vModel__, Array.isArray(value) ? value : [value])
+                            }
+                            if (e.opType === 'setAddressOptions') {
+                                let value = this[this.formConf.formModel][e.relationField] || []
+                                this.comSet('ableAddressIds', e.__vModel__, Array.isArray(value) ? value : [value])
+                            }
+                            if (e.opType === 'setDate') {
+                                let startTime = ''
+                                let endTime = ''
+                                if (e.__config__.startTimeType == 2) {
+                                    startTime = this[this.formConf.formModel][e.__config__.startRelationField] || 0
+                                } else {
+                                    startTime = e.startTime
+                                }
+                                if (e.__config__.endTimeType == 2) {
+                                    endTime = this[this.formConf.formModel][e.__config__.endRelationField] || 0
+                                } else {
+                                    endTime = e.endTime
+                                }
+                                this.comSet('startTime', e.__vModel__, startTime)
+                                this.comSet('endTime', e.__vModel__, endTime)
+                            }
+                            if (e.opType === 'setTime') {
+                                let startTime = ''
+                                let endTime = ''
+                                if (e.__config__.startTimeType == 2) {
+                                    startTime = this[this.formConf.formModel][e.__config__.startRelationField] || '00:00:00'
+                                } else {
+                                    startTime = e.startTime
+                                }
+                                if (e.__config__.endTimeType == 2) {
+                                    endTime = this[this.formConf.formModel][e.__config__.endRelationField] || '23:59:59'
+                                    if (endTime && (endTime.split(':').length == 3)) {
+                                        endTime = endTime
+                                    } else {
+                                        endTime = endTime + ':00'
+                                    }
+                                } else {
+                                    endTime = e.endTime
+                                }
+                                if (startTime) {
+                                    if (startTime.split(':').length == 3) {
+                                        startTime = startTime
+                                    } else {
+                                        startTime = startTime + ':00'
+                                    }
+                                }
+                                if (endTime) {
+                                    if (endTime.split(':').length == 3) {
+                                        endTime = endTime
+                                    } else {
+                                        endTime = endTime + ':00'
+                                    }
+                                }
+                                this.comSet('startTime', e.__vModel__, startTime)
+                                this.comSet('endTime', e.__vModel__, endTime)
+                            }
+                        }
+                    }
+                }
+            }
+        },
+        getParamList(templateJson, formData) {
+            for (let i = 0; i < templateJson.length; i++) {
+                if (templateJson[i].relationField) {
+                    templateJson[i].defaultValue = formData[templateJson[i].relationField] || ''
+                }
+            }
+            return templateJson
+        },
+        initRelationForm(componentList) {
+            componentList.forEach(cur => {
+                const config = cur.__config__
+                if (config.jnpfKey == 'relationFormAttr' || config.jnpfKey == 'popupAttr') {
+                    const relationKey = cur.relationField.split("_jnpfTable_")[0]
+                    componentList.forEach(item => {
+                        const noVisibility = Array.isArray(item.__config__.visibility) && !item.__config__.visibility.includes('pc')
+                        if ((relationKey == item.__vModel__) && (noVisibility || !!item.__config__.noShow) && !cur.__vModel__) {
+                            cur.__config__.noShow = true
+                        }
+                    })
+                }
+                if (cur.__config__.children && cur.__config__.children.length) this.initRelationForm(cur.__config__.children)
+            })
+        },
+        buildOptions(componentList, data, formData) {
+            componentList.forEach(cur => {
+                const config = cur.__config__
+                if (dyOptionsList.indexOf(config.jnpfKey) > -1) {
+                    if (config.dataType === 'dictionary' && config.dictionaryType) {
+                        cur.options = []
+                        this.$store.dispatch('base/getDicDataSelector', config.dictionaryType).then(res => {
+                            cur.options = res
+                            data[cur.__vModel__ + 'Options'] = cur.options
+                        })
+                    } else if (config.dataType === 'dynamic' && config.propsUrl) {
+                        cur.options = []
+                        let query = {
+                            paramList: config.templateJson ? this.getParamList(config.templateJson, formData) : [],
+                        }
+                        getDataInterfaceRes(config.propsUrl, query).then(res => {
+                            cur.options = Array.isArray(res.data) ? res.data : []
+                            data[cur.__vModel__ + 'Options'] = cur.options
+                        })
+                    } else {
+                        data[cur.__vModel__ + 'Options'] = cur.options
+                    }
+                }
+                if (config.children && config.jnpfKey !== 'table') this.buildOptions(config.children, data, formData)
+            })
+        },
+        buildRules(componentList, rules) {
+            componentList.forEach(cur => {
+                const config = JSON.parse(JSON.stringify(cur.__config__))
+                if (!Array.isArray(config.regList)) config.regList = []
+                if (config.required) {
+                    const required = { required: config.required, message: cur.placeholder }
+                    if (Array.isArray(config.defaultValue)) {
+                        required.type = 'array'
+                        required.message = `请至少选择一个${config.label}`
+                    }
+                    required.message === undefined && (required.message = `${config.label}不能为空`)
+                    config.regList.push(required)
+                }
+                rules[cur.__vModel__] = config.regList.map(item => {
+                    item.pattern && this.isRegExp(item.pattern) && (item.pattern = eval(item.pattern))
+                    item.trigger = config.trigger || 'blur'
+                    return item
+                })
+                if (config.children && config.jnpfKey !== 'table') this.buildRules(config.children, rules)
+            })
+        },
+        isRegExp(val) {
+            try {
+                return Object.prototype.toString.call(eval(val)) === '[object RegExp]'
+            } catch {
+                return false
+            }
+        },
+        onLoad(formConfCopy) {
+            if (!formConfCopy || !formConfCopy.funcs || !formConfCopy.funcs.onLoad) return
+            const onLoadFunc = this.jnpf.getScriptFunc.call(this, formConfCopy.funcs.onLoad)
+            if (!onLoadFunc) return
+            onLoadFunc(this.parameter)
+        },
+        resetForm() {
+            this.$store.commit('generator/UPDATE_RELATION_DATA', {})
+            this.formConfCopy = deepClone(this.formConf)
+            this.$refs[this.formConf.formRef].resetFields()
+            Object.keys(this.tableRefs).forEach(vModel => {
+                this.$refs[vModel] && this.$refs[vModel].$children && this.$refs[vModel].$children[0].resetTable()
+            })
+        },
+        checkTableData() {
+            let valid = true
+            Object.keys(this.tableRefs).forEach(vModel => {
+                if (this.$refs[vModel] && this.$refs[vModel].$children) {
+                    const res = this.$refs[vModel].$children[0].submit()  // 返回false或表单数据
+                    res ? (this[this.formConf.formModel][vModel] = res) : (valid = false)
+                }
+            })
+            return valid
+        },
+        getFieldOptions(prop) {
+            if (!prop) return []
+            const isChildTable = prop.indexOf('.') > -1
+            if (isChildTable) {
+                const list = prop.split('.')
+                if (this.$refs[list[0]] && this.$refs[list[0]].$children[0]) {
+                    let res = this.$refs[list[0]].$children[0].getTableFieldOptions(list[1])
+                    return res
+                } else {
+                    return []
+                }
+            } else {
+                return this.options[prop + 'Options'] || []
+            }
+        },
+        setFormData(prop, value) {
+            if (!prop || this[this.formConf.formModel][prop] === value) return;
+            const isChildTable = prop.indexOf('.') > -1
+            if (isChildTable) {
+                const list = prop.split('.')
+                if (this.$refs[list[0]] && this.$refs[list[0]].$children[0]) {
+                    this.$refs[list[0]].$children[0].setTableFormData(list[1], value)
+                }
+            } else {
+                this.comSet('defaultValue', prop, value)
+                this[this.formConf.formModel][prop] = value
+            }
+            this.handleRelation(prop)
+        },
+        setShowOrHide(prop, value) {
+            const newVal = !!value
+            const isChildTable = prop.indexOf('.') > -1
+            if (!isChildTable) {
+                this.comSet('noShow', prop, !newVal)
+            } else {
+                const list = prop.split('.')
+                if (this.$refs[list[0]] && this.$refs[list[0]].$children[0]) {
+                    this.$refs[list[0]].$children[0].setTableShowOrHide(list[1], !newVal)
+                }
+            }
+        },
+        setRequired(prop, value) {
+            const newVal = !!value
+            const isChildTable = prop.indexOf('.') > -1
+            if (!isChildTable) {
+                this.comSet('required', prop, newVal)
+                this.buildRules(this.formConfCopy.fields, this[this.formConf.formRules])
+            }
+        },
+        setDisabled(prop, value) {
+            const newVal = !!value
+            const isChildTable = prop.indexOf('.') > -1
+            if (!isChildTable) {
+                this.comSet('disabled', prop, newVal)
+            }
+        },
+        setFieldOptions(prop, value) {
+            const newVal = Array.isArray(value) ? value : []
+            const isChildTable = prop.indexOf('.') > -1
+            if (!isChildTable) {
+                this.comSet('options', prop, newVal)
+            }
+        },
+        comSet(field, prop, value) {
+            if (!prop) return
+            const loop = list => {
+                for (let i = 0; i < list.length; i++) {
+                    let item = list[i]
+                    if (item.__vModel__ && item.__vModel__ === prop) {
+                        switch (field) {
+                            case 'disabled':
+                                this.$set(item, field, value)
+                                break;
+                            case 'ableRelationIds':
+                                this.$set(item, field, value)
+                                break;
+                            case 'ableAddressIds':
+                                this.$set(item, field, value)
+                                break;
+                            case 'startTime':
+                                this.$set(item, field, value)
+                                break;
+                            case 'endTime':
+                                this.$set(item, field, value)
+                                break;
+                            case 'options':
+                                if (dyOptionsList.indexOf(item.__config__.jnpfKey) > -1) {
+                                    item.options = value
+                                }
+                                break;
+                            default:
+                                this.$set(item.__config__, field, value)
+                                break;
+                        }
+                        item.__config__.renderKey = +new Date() + item.__vModel__
+                        break;
+                    }
+                    if (item.__config__ && item.__config__.jnpfKey !== 'table' && item.__config__.children && Array.isArray(item.__config__.children)) {
+                        loop(item.__config__.children)
+                    }
+                }
+            }
+            loop(this.formConfCopy.fields)
+        },
+        beforeSubmit() {
+            if (!this.formConfCopy || !this.formConfCopy.funcs || !this.formConfCopy.funcs.beforeSubmit) return Promise.resolve()
+            const func = this.jnpf.getScriptFunc.call(this, this.formConfCopy.funcs.beforeSubmit)
+            if (!func) return Promise.resolve()
+            return func(this.parameter)
+        },
+        afterSubmit() {
+            if (!this.formConfCopy || !this.formConfCopy.funcs || !this.formConfCopy.funcs.afterSubmit) return
+            const func = this.jnpf.getScriptFunc.call(this, this.formConfCopy.funcs.afterSubmit)
+            if (!func) return
+            func(this.parameter)
+        },
+        submitForm(type) {
+            this.isTableValid = this.checkTableData()
+            try {
+                this.beforeSubmit().then(() => {
+                    this.submit(type)
+                })
+            } catch (e) {
+                this.submit(type)
+            }
+        },
+        submit(type) {
+            this.$refs[this.formConf.formRef].validate(valid => {
+                if (!valid) return false
+                if (!this.isTableValid) return false
+                // 触发submit事件
+                this.$emit('submit', this[this.formConf.formModel], this.afterSubmit, type)
+                return true
+            })
+        },
+        onCascaderChange(data, on) {
+            if (!on || !on.change) return
+            const func = this.jnpf.getScriptFunc.call(this, on.change)
+            if (!func) return
+            func.call(this, { data, ...this.parameter })
+        },
+        onCascaderBlur(data, on) {
+            if (!on || !on.blur) return
+            const func = this.jnpf.getScriptFunc.call(this, on.blur)
+            if (!func) return
+            func.call(this, { data, ...this.parameter })
+        }
+    },
+    render(h) {
+        return renderFrom.call(this, h)
+    }
+}
+</script>

+ 324 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/example/Index.vue

@@ -0,0 +1,324 @@
+<template>
+  <div class="test-form">
+    <parser :form-conf="formConf" @submit="submitForm1" />
+    <parser :key="key2" :form-conf="formConf" @submit="submitForm2" />
+    <el-button @click="change">
+      change
+    </el-button>
+  </div>
+</template>
+
+<script>
+import Parser from '../Parser'
+
+// 若parser是通过安装npm方式集成到项目中的,使用此行引入
+// import Parser from 'form-gen-parser'
+
+export default {
+  components: {
+    Parser
+  },
+  props: {},
+  data() {
+    return {
+      key2: +new Date(),
+      formConf: {
+        fields: [
+          {
+            __config__: {
+              label: '单行输入框',
+              labelWidth: null,
+              showLabel: true,
+              changeTag: true,
+              tag: 'el-input',
+              tagIcon: 'input',
+              required: true,
+              layout: 'colFormItem',
+              span: 24,
+              document: 'https://element.eleme.cn/#/zh-CN/component/input',
+              regList: [
+                {
+                  pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
+                  message: '手机号格式错误'
+                }
+              ]
+            },
+            __slot__: {
+              prepend: '',
+              append: ''
+            },
+            __vModel__: 'mobile',
+            placeholder: '请输入手机号',
+            style: {
+              width: '100%'
+            },
+            clearable: true,
+            prefixIcon: 'el-icon-mobile',
+            suffixIcon: '',
+            maxlength: 11,
+            showWordLimit: true,
+            readonly: false,
+            disabled: false
+          },
+          {
+            __config__: {
+              label: '日期范围',
+              tag: 'el-date-picker',
+              tagIcon: 'date-range',
+              defaultValue: null,
+              span: 24,
+              showLabel: true,
+              labelWidth: null,
+              required: true,
+              layout: 'colFormItem',
+              regList: [],
+              changeTag: true,
+              document:
+                'https://element.eleme.cn/#/zh-CN/component/date-picker',
+              formId: 101,
+              renderKey: 1585980082729
+            },
+            style: {
+              width: '100%'
+            },
+            type: 'daterange',
+            'range-separator': '至',
+            'start-placeholder': '开始日期',
+            'end-placeholder': '结束日期',
+            disabled: false,
+            clearable: true,
+            format: 'yyyy-MM-dd',
+            'value-format': 'yyyy-MM-dd',
+            readonly: false,
+            __vModel__: 'field101'
+          },
+          {
+            __config__: {
+              layout: 'rowFormItem',
+              tagIcon: 'row',
+              label: '行容器',
+              layoutTree: true,
+              children: [
+                {
+                  __config__: {
+                    label: '评分',
+                    tag: 'el-rate',
+                    tagIcon: 'rate',
+                    defaultValue: 0,
+                    span: 24,
+                    showLabel: true,
+                    labelWidth: null,
+                    layout: 'colFormItem',
+                    required: true,
+                    regList: [],
+                    changeTag: true,
+                    document: 'https://element.eleme.cn/#/zh-CN/component/rate',
+                    formId: 102,
+                    renderKey: 1586839671259
+                  },
+                  style: {},
+                  max: 5,
+                  allowHalf: false,
+                  showText: false,
+                  showScore: false,
+                  disabled: false,
+                  __vModel__: 'field102'
+                }
+              ],
+              document: 'https://element.eleme.cn/#/zh-CN/component/layout',
+              formId: 101,
+              span: 24,
+              renderKey: 1586839668999,
+              componentName: 'row101',
+              gutter: 15
+            },
+            type: 'default',
+            justify: 'start',
+            align: 'top'
+          },
+          {
+            __config__: {
+              label: '按钮',
+              showLabel: true,
+              changeTag: true,
+              labelWidth: null,
+              tag: 'el-button',
+              tagIcon: 'button',
+              span: 24,
+              layout: 'colFormItem',
+              document: 'https://element.eleme.cn/#/zh-CN/component/button',
+              renderKey: 1594288459289
+            },
+            __slot__: {
+              default: '测试按钮1'
+            },
+            type: 'primary',
+            icon: 'el-icon-search',
+            round: false,
+            size: 'medium',
+            plain: false,
+            circle: false,
+            disabled: false,
+            on: {
+              click: 'clickTestButton1'
+            }
+          }
+        ],
+        __methods__: {
+          clickTestButton1() {
+            console.log(
+              `%c【测试按钮1】点击事件里可以访问当前表单:
+                1) formModel='formData', 所以this.formData可以拿到当前表单的model
+                2) formRef='elForm', 所以this.$refs.elForm可以拿到当前表单的ref(vue组件)
+              `,
+              'color:#409EFF;font-size: 15px'
+            )
+            console.log('表单的Model:', this.formData)
+            console.log('表单的ref:', this.$refs.elForm)
+          }
+        },
+        formRef: 'elForm',
+        formModel: 'formData',
+        size: 'small',
+        labelPosition: 'right',
+        labelWidth: 100,
+        formRules: 'rules',
+        gutter: 15,
+        disabled: false,
+        span: 24,
+        formBtns: true,
+        unFocusedComponentBorder: false
+      },
+      formConf2: {
+        fields: [
+          {
+            __config__: {
+              label: '单行输入框',
+              labelWidth: null,
+              showLabel: true,
+              changeTag: true,
+              tag: 'el-input',
+              tagIcon: 'input',
+              required: true,
+              layout: 'colFormItem',
+              span: 24,
+              document: 'https://element.eleme.cn/#/zh-CN/component/input',
+              regList: [
+                {
+                  pattern: '/^1(3|4|5|7|8|9)\\d{9}$/',
+                  message: '手机号格式错误'
+                }
+              ]
+            },
+            __slot__: {
+              prepend: '',
+              append: ''
+            },
+            __vModel__: 'mobile',
+            placeholder: '请输入手机号',
+            style: {
+              width: '100%'
+            },
+            clearable: true,
+            prefixIcon: 'el-icon-mobile',
+            suffixIcon: '',
+            maxlength: 11,
+            showWordLimit: true,
+            readonly: false,
+            disabled: false
+          },
+          {
+            __config__: {
+              label: '日期范围',
+              tag: 'el-date-picker',
+              tagIcon: 'date-range',
+              defaultValue: null,
+              span: 24,
+              showLabel: true,
+              labelWidth: null,
+              required: true,
+              layout: 'colFormItem',
+              regList: [],
+              changeTag: true,
+              document:
+                'https://element.eleme.cn/#/zh-CN/component/date-picker',
+              formId: 101,
+              renderKey: 1585980082729
+            },
+            style: {
+              width: '100%'
+            },
+            type: 'daterange',
+            'range-separator': '至',
+            'start-placeholder': '开始日期',
+            'end-placeholder': '结束日期',
+            disabled: false,
+            clearable: true,
+            format: 'yyyy-MM-dd',
+            'value-format': 'yyyy-MM-dd',
+            readonly: false,
+            __vModel__: 'field101'
+          }
+        ],
+        formRef: 'elForm',
+        formModel: 'formData',
+        size: 'small',
+        labelPosition: 'right',
+        labelWidth: 100,
+        formRules: 'rules',
+        gutter: 15,
+        disabled: false,
+        span: 24,
+        formBtns: true,
+        unFocusedComponentBorder: false
+      }
+    }
+  },
+  computed: {},
+  watch: {},
+  created() { },
+  mounted() {
+    // 表单数据回填,模拟异步请求场景
+    setTimeout(() => {
+      // 请求回来的表单数据
+      const data = {
+        mobile: '18836662555'
+      }
+      // 回填数据
+      this.fillFormData(this.formConf, data)
+      // 更新表单
+      this.key2 = +new Date()
+    }, 2000)
+  },
+  methods: {
+    fillFormData(form, data) {
+      form.fields.forEach(item => {
+        const val = data[item.__vModel__]
+        if (val) {
+          item.__config__.defaultValue = val
+        }
+      })
+    },
+    change() {
+      this.key2 = +new Date()
+      const t = this.formConf
+      this.formConf = this.formConf2
+      this.formConf2 = t
+    },
+    submitForm1(data) {
+      console.log('submitForm1提交数据:', data)
+    },
+    submitForm2(data) {
+      console.log('submitForm2提交数据:', data)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.test-form {
+  margin: 15px auto;
+  width: 800px;
+  padding: 15px;
+}
+</style>

+ 3 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/parser/index.js

@@ -0,0 +1,3 @@
+import Parser from './Parser'
+
+export default Parser

+ 71 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/preview/index.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-dialog v-bind="$attrs" :close-on-click-modal="false" :modal-append-to-body="false"
+    append-to-body v-on="$listeners" @open="onOpen" @close="close"
+    class="JNPF-dialog JNPF-dialog_center" title="预览" :width="formConf.generalWidth">
+    <parser :form-conf="formConf" @submit="submitForm" :key="key" ref="dynamicForm" />
+    <div slot="footer">
+      <el-button @click="close">{{formConf.cancelButtonText||'取 消'}}</el-button>
+      <el-button type="primary" @click="handelConfirm">{{formConf.confirmButtonText||'确 定'}}
+      </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import Parser from '@/components/Generator/parser/Parser'
+export default {
+  components: { Parser },
+  props: ['formData'],
+  data() {
+    return {
+      key: +new Date(),
+      formConf: {}
+    }
+  },
+  computed: {},
+  watch: {},
+  created() { },
+  mounted() { },
+  methods: {
+    onOpen() {
+      this.key = +new Date()
+      this.formConf = this.formData
+    },
+    onClose() {
+    },
+    close(e) {
+      this.destroyStyle()
+      this.$emit('update:visible', false)
+    },
+    handelConfirm() {
+      this.$refs.dynamicForm && this.$refs.dynamicForm.submitForm()
+    },
+    fillFormData(form, data) {
+      const loop = list => {
+        for (let i = 0; i < list.length; i++) {
+          let item = list[i]
+          if (item.__vModel__) {
+            const val = data[item.__vModel__]
+            if (val !== null && val !== undefined) item.__config__.defaultValue = val
+          }
+          if (item.__config__ && item.__config__.jnpfKey !== 'table' && item.__config__.children && Array.isArray(item.__config__.children)) {
+            loop(item.__config__.children)
+          }
+        }
+      }
+      loop(form.fields)
+    },
+    submitForm(data, callback) {
+      console.log('submitForm提交数据:', data)
+      if (callback && typeof callback === "function") {
+        callback()
+      }
+    },
+    destroyStyle() {
+      if (document.getElementById('styleId')) {
+        document.getElementById('styleId').remove()
+      }
+    }
+  }
+}
+</script>

+ 116 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/render/render.js

@@ -0,0 +1,116 @@
+import { deepClone } from '@/utils'
+
+function vModel(dataObject, defaultValue) {
+  dataObject.props.value = defaultValue
+  dataObject.on.input = val => {
+    this.$emit('input', val)
+  }
+}
+
+function emitEvents(confClone) {
+  ['on', 'nativeOn'].forEach(attr => {
+    const eventKeyList = Object.keys(confClone[attr] || {})
+    eventKeyList.forEach(key => {
+      const val = confClone[attr][key]
+      if (typeof key === 'string') {
+        confClone[attr][key] = (...arg) => this.$emit(key, arg)
+      }
+    })
+  })
+}
+
+function buildDataObject(confClone, dataObject, formData) {
+  const jnpfKey = confClone.__config__.jnpfKey
+  Object.keys(confClone).forEach(key => {
+    const val = confClone[key]
+    if (key === '__vModel__') {
+      vModel.call(this, dataObject, confClone.__config__.defaultValue)
+    } else if (key === 'props') {
+      dataObject[key][key] = val
+    } else if (dataObject[key] !== undefined) {
+      if (dataObject[key] === null ||
+        dataObject[key] instanceof RegExp || ['boolean', 'string', 'number', 'function'].includes(typeof dataObject[key])) {
+        dataObject[key] = val
+      } else if (Array.isArray(dataObject[key])) {
+        dataObject[key] = [...dataObject[key], ...val]
+      } else {
+        dataObject[key] = { ...dataObject[key], ...val }
+      }
+    } else {
+      dataObject.attrs[key] = val
+    }
+  })
+  if (['calculate', 'table', 'barcode', 'qrcode', 'popupSelect', 'popupTableSelect', 'autoComplete'].includes(jnpfKey)) {
+    dataObject.attrs['formData'] = formData
+  }
+  if (['table'].includes(jnpfKey)) {
+    dataObject.attrs['relations'] = this.relations
+    dataObject.attrs['vModel'] = confClone.__vModel__
+  }
+  if (['popupAttr', 'calculate', 'relationFormAttr'].includes(jnpfKey)) {
+    dataObject.attrs['defaultValue'] = confClone.__config__.defaultValue
+  }
+  if (['relationForm', 'popupSelect'].includes(jnpfKey)) {
+    dataObject.attrs['field'] = confClone.__config__.tableName ? confClone.__vModel__ + '_jnpfTable_' + confClone.__config__.tableName + (confClone.__config__.isSubTable ? '0' : "1") : confClone.__vModel__
+  }
+  // 清理属性
+  clearAttrs(dataObject)
+}
+
+function clearAttrs(dataObject) {
+  delete dataObject.attrs.__config__
+  delete dataObject.attrs.__slot__
+  delete dataObject.attrs.__methods__
+}
+
+function makeDataObject() {
+  return {
+    class: {},
+    attrs: {},
+    props: {},
+    domProps: {},
+    nativeOn: {},
+    on: {},
+    style: {},
+    directives: [],
+    scopedSlots: {},
+    slot: null,
+    key: null,
+    ref: null,
+    refInFor: true
+  }
+}
+
+export default {
+  props: {
+    conf: {
+      type: Object,
+      required: true
+    },
+    formData: {
+      type: Object,
+      default: () => { }
+    },
+    relations: Object,
+  },
+  render(h) {
+    const dataObject = makeDataObject()
+    const confClone = deepClone(this.conf)
+    const children = this.$slots.default || []
+
+    // 将字符串类型的事件,发送为消息
+    emitEvents.call(this, confClone)
+
+    // 将json表单配置转化为vue render可以识别的 “数据对象(dataObject)”
+    buildDataObject.call(this, confClone, dataObject, this.formData)
+
+
+
+    // 单独给table组件做特殊处理
+    if (this.conf.__config__.jnpfKey == 'custom-table') {
+      dataObject.props.columns = this.conf.children
+    }
+
+    return h(this.conf.__config__.tag, dataObject, children)
+  }
+}

+ 581 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/styles/home.scss

@@ -0,0 +1,581 @@
+$selectedColor: #F3F9FF;
+$lighterBlue: #409EFF;
+
+.container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.components-list {
+  box-sizing: border-box;
+  height: 100%;
+
+  .components-part {
+    background: #fff;
+    border-radius: 4px;
+    padding: 10px 10px 0;
+    margin-bottom: 10px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .components-draggable {
+    margin-right: -10px;
+  }
+
+  .components-item {
+    display: inline-block;
+    width: 110px;
+    margin: 0 10px 10px 0;
+    transition: transform 0ms !important;
+
+    &.disabled {
+      .components-body {
+        background: #E4E7ED;
+        cursor: not-allowed;
+        border: none !important;
+        color: #999 !important;
+
+        .icon-ym {
+          color: #999 !important;
+        }
+
+        &:hover {
+          border: none !important;
+          color: #999 !important;
+
+          .icon-ym {
+            color: #999 !important;
+          }
+        }
+      }
+    }
+  }
+}
+
+.components-title {
+  font-size: 14px;
+  color: #043254;
+  line-height: 30px;
+  margin-bottom: 10px;
+  font-weight: bold;
+}
+
+.components-body {
+  padding-left: 8px;
+  background: $selectedColor;
+  font-size: 12px;
+  height: 36px;
+  cursor: move;
+  border: 1px dashed $selectedColor;
+  border-radius: 3px;
+  color: #043254;
+  line-height: 36px;
+  display: flex;
+  align-items: center;
+
+  i {
+    color: #043254;
+    line-height: 16px;
+    height: 16px;
+    margin-right: 4px;
+  }
+
+  &:hover {
+    border: 1px dashed #409EFF;
+    color: #409EFF;
+
+    i {
+      color: #409EFF;
+    }
+  }
+}
+
+.left-board {
+  width: 250px;
+  position: absolute;
+  left: 0;
+  top: 0;
+  height: 100%;
+  overflow: hidden;
+  border-radius: 4px;
+}
+
+.custom-left-board {
+  width: 250px;
+  position: absolute;
+  left: 0;
+  top: 0;
+  height: 100%;
+  overflow: hidden;
+  border-radius: 4px;
+  background-color: #fff;
+
+  .el-table {
+    padding: 10px;
+    padding-bottom: 30px;
+  }
+
+  .tab {
+    .el-tabs__item {
+      padding: 0 20px !important;
+    }
+  }
+
+  .el-tabs__header {
+    margin-bottom: 0 !important;
+  }
+}
+
+.left-scrollbar {
+  height: 100%;
+  overflow: hidden;
+
+  .el-scrollbar__wrap {
+    overflow-x: auto;
+  }
+}
+
+.center-scrollbar {
+  height: calc(100% - 42px);
+  overflow: hidden;
+  box-sizing: border-box;
+
+  .el-scrollbar__wrap {
+    overflow-x: auto;
+  }
+
+  .el-scrollbar__view {
+    overflow-x: hidden;
+  }
+}
+
+.center-board {
+  height: 100%;
+  width: auto;
+  margin: 0 350px 0 260px;
+  box-sizing: border-box;
+  background: #fff;
+  border-radius: 4px;
+  overflow: hidden;
+
+  .jnpf-editor-quill {
+    .ql-editor {
+      min-height: 100px !important;
+    }
+  }
+
+  .table-tip {
+    width: 100%;
+    color: #999;
+    text-align: center;
+    position: absolute;
+    top: 60px;
+    font-size: 14px;
+
+    &.tab-tip {
+      top: 45px;
+    }
+
+    &.card-tip {
+      top: 30px;
+    }
+  }
+
+  #ipad {
+    height: calc(100% - 42px);
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    min-height: 711px;
+
+    .outeripad {
+      background: url('../../../../../../assets/images/iphoneBg.png');
+      width: 389px;
+      height: 711px;
+      padding: 65px 40px;
+
+      .ipadHead {
+        background: #f7f8f9;
+        text-align: center;
+
+        .ipadHead-img {
+          margin: 0 auto;
+          height: 20px;
+        }
+      }
+
+      .ipadbody {
+        height: 100%;
+
+        .center-board-row>.el-form {
+          height: 550px;
+        }
+
+        .center-scrollbar {
+          height: 100%;
+          overflow: hidden;
+        }
+      }
+    }
+  }
+
+}
+
+.column-empty-info {
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+
+  .empty-img {
+    width: 180px;
+    height: 120px;
+  }
+
+  p {
+    padding: 15px 0;
+  }
+}
+
+.empty-info {
+  position: absolute;
+  top: 20%;
+  left: calc(50% - 250px);
+
+  &.app-empty-info {
+    top: calc(50% - 150px);
+    left: calc(50% - 150px);
+
+    .empty-img {
+      width: 300px;
+      height: 300px;
+    }
+  }
+
+  .empty-img {
+    width: 500px;
+    height: 500px;
+  }
+}
+
+.action-bar {
+  position: relative;
+  height: 42px;
+  text-align: center;
+  padding: 0 15px;
+  box-sizing: border-box;
+  border-bottom: 1px solid #dcdfe6;
+
+  .delete-btn {
+    color: #F56C6C !important;
+  }
+
+  .unActive-btn {
+    color: #606266 !important;
+
+    &:hover {
+      color: #1890ff !important;
+    }
+  }
+
+  .action-bar-right {
+    position: absolute;
+    right: 15px;
+    top: 0;
+    display: flex;
+    align-items: center;
+    height: 42px;
+  }
+}
+
+.logo {
+  position: absolute;
+  left: 12px;
+  top: 6px;
+  line-height: 30px;
+  color: #00afff;
+  font-weight: 600;
+  font-size: 17px;
+  white-space: nowrap;
+
+  >img {
+    width: 30px;
+    height: 30px;
+    vertical-align: top;
+  }
+
+  .github {
+    display: inline-block;
+    vertical-align: sub;
+    margin-left: 15px;
+
+    >img {
+      height: 22px;
+    }
+  }
+}
+
+.center-board-row {
+  padding: 12px 12px 15px 12px;
+  box-sizing: border-box;
+  height: 100%;
+
+  &>.el-form {
+    height: calc(100vh - 150px);
+  }
+
+  .el-tabs__header {
+    margin: 0;
+  }
+}
+
+.drawing-board {
+  height: 100%;
+  position: relative;
+
+  .el-select {
+    width: 100%;
+  }
+
+  .components-body {
+    padding: 0;
+    margin: 0;
+    font-size: 0;
+  }
+
+  .sortable-ghost {
+    position: relative;
+    display: block;
+    overflow: hidden;
+
+    i {
+      display: none;
+    }
+
+    .el-form-item {
+      i {
+        display: inline;
+      }
+    }
+
+    &::before {
+      content: " ";
+      position: absolute;
+      left: 0;
+      right: 0;
+      top: 0;
+      height: 3px;
+      background: #409EFF;
+      z-index: 2;
+    }
+  }
+
+  .components-item.sortable-ghost {
+    width: 100%;
+    height: 60px;
+    background-color: $selectedColor;
+  }
+
+  .active-from-item {
+    &>.el-form-item {
+      background: $selectedColor;
+      border: 1px solid #409EFF;
+    }
+
+    &>.drawing-item-copy,
+    &>.drawing-item-delete,
+    &>.drawing-item-add-row,
+    &>.drawing-item-add-col,
+    &>.drawing-item-cell {
+      display: block;
+    }
+
+    &>.component-name {
+      color: $lighterBlue;
+    }
+
+  }
+
+  .el-form-item {
+    margin-bottom: 10px !important;
+  }
+}
+
+.drawing-item {
+  position: relative;
+  cursor: move;
+
+  &.unfocus-bordered:not(.active-from-item)>div:first-child {
+    border: 1px dashed #ccc;
+  }
+
+  .el-form-item {
+    border: 1px dashed #e2e0e0;
+    padding: 10px;
+  }
+}
+
+.drawing-row-item {
+  position: relative;
+  cursor: move;
+  box-sizing: border-box;
+  border: 1px dashed #ccc;
+  padding: 10px 2px;
+  margin-bottom: 10px;
+
+  .drawing-item-copy {
+    right: 48px !important;
+  }
+
+  .drawing-item-delete {
+    right: 16px !important;
+  }
+
+  .drawing-row-item {
+    margin-bottom: 2px;
+  }
+
+  &.drawing-row-item-table {
+    padding-top: 15px;
+
+    .el-col {
+      margin-top: 15px;
+    }
+  }
+
+  .el-form-item {
+    margin-bottom: 0;
+  }
+
+  .drag-wrapper {
+    min-height: 80px;
+  }
+
+  &.active-from-item {
+    border-color: $lighterBlue;
+  }
+
+  .component-name {
+    position: absolute;
+    top: 0;
+    left: 0;
+    font-size: 18px;
+    color: #bbb;
+    display: inline-block;
+    padding: 5px 6px 0;
+  }
+
+  .content-name {
+    margin-left: 4px;
+  }
+}
+
+.drawing-item,
+.drawing-row-item {
+  &:hover {
+    &>.el-form-item {
+      background: $selectedColor;
+    }
+
+    &>.drawing-item-copy,
+    &>.drawing-item-delete,
+    &>.drawing-item-add-row,
+    &>.drawing-item-add-col,
+    &>.drawing-item-cell {
+      display: block;
+    }
+  }
+
+  &>.drawing-item-copy,
+  &>.drawing-item-delete,
+  &>.drawing-item-add-row,
+  &>.drawing-item-add-col,
+  &>.drawing-item-cell {
+    display: none;
+    position: absolute;
+    top: -10px;
+    width: 22px;
+    height: 22px;
+    line-height: 22px;
+    text-align: center;
+    border-radius: 50%;
+    font-size: 12px;
+    border: 1px solid;
+    cursor: pointer;
+    z-index: 1;
+  }
+
+  &>.drawing-item-copy,
+  &>.drawing-item-add-row,
+  &>.drawing-item-add-col,
+  &>.drawing-item-cell {
+    right: var(--rightDistance);
+    border-color: $lighterBlue;
+    color: $lighterBlue;
+    background: #fff;
+
+    &:hover {
+      background: $lighterBlue;
+      color: #fff;
+    }
+  }
+
+  &>.drawing-item-add-row {
+    right: 108px;
+  }
+
+  &>.drawing-item-add-col {
+    right: 78px;
+  }
+
+  &>.drawing-item-cell {
+    top: unset;
+    bottom: 0;
+    right: 0;
+    border-radius: unset;
+    background: $lighterBlue;
+
+    i {
+      color: #fff;
+    }
+  }
+
+  &>.drawing-item-delete {
+    right: var(--rightDistance);
+    border-color: #F56C6C;
+    color: #F56C6C;
+    background: #fff;
+
+    &:hover {
+      background: #F56C6C;
+      color: #fff;
+    }
+  }
+}
+
+.drawing-row-item .el-card__body>.el-col {
+  margin-top: 0 !important;
+  margin-bottom: 10px;
+}
+
+.table-wrapper-web {
+  overflow: auto;
+  display: flex;
+  width: 100%;
+
+  &>.el-col {
+    width: 200px !important;
+    flex-shrink: 0;
+
+    .el-form-item {
+      margin-bottom: 0 !important;
+    }
+  }
+}

+ 136 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/styles/index.scss

@@ -0,0 +1,136 @@
+$editorTabsborderColor: #121315;
+
+input,
+textarea {
+  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
+}
+
+.editor-tabs {
+  background: $editorTabsborderColor;
+
+  .el-tabs__header {
+    margin: 0;
+    border-bottom-color: $editorTabsborderColor;
+
+    .el-tabs__nav {
+      border-color: $editorTabsborderColor;
+    }
+  }
+
+  .el-tabs__item {
+    height: 32px;
+    line-height: 32px;
+    color: #888a8e;
+    border-left: 1px solid $editorTabsborderColor !important;
+    background: #363636;
+    margin-right: 5px;
+    user-select: none;
+  }
+
+  .el-tabs__item.is-active {
+    background: #1e1e1e;
+    border-bottom-color: #1e1e1e !important;
+    color: #fff;
+  }
+
+  .el-icon-edit {
+    color: #f1fa8c;
+  }
+
+  .el-icon-document {
+    color: #a95812;
+  }
+}
+
+// home
+.right-scrollbar {
+  .el-scrollbar__view {
+    padding: 10px;
+
+    .el-input-number {
+      width: 100%;
+    }
+  }
+}
+
+.el-scrollbar__wrap {
+  box-sizing: border-box;
+  overflow-x: hidden !important;
+  margin-bottom: 0 !important;
+}
+
+.center-tabs {
+  .el-tabs__header {
+    margin-bottom: 0 !important;
+  }
+
+  .el-tabs__item {
+    width: 33.33%;
+    text-align: center;
+  }
+
+  .el-tabs__nav {
+    width: 100%;
+  }
+}
+
+.reg-item {
+  padding: 12px 6px;
+  background: #f8f8f8;
+  position: relative;
+  border-radius: 4px;
+
+  .close-btn {
+    position: absolute;
+    right: -6px;
+    top: -6px;
+    display: block;
+    width: 16px;
+    height: 16px;
+    line-height: 16px;
+    background: rgba(0, 0, 0, 0.2);
+    border-radius: 50%;
+    color: #fff;
+    text-align: center;
+    z-index: 1;
+    cursor: pointer;
+    font-size: 12px;
+
+    &:hover {
+      background: rgba(210, 23, 23, 0.5)
+    }
+  }
+
+  &+.reg-item {
+    margin-top: 18px;
+  }
+}
+
+.action-bar {
+  & .el-button+.el-button {
+    margin-left: 15px;
+  }
+
+  & i {
+    font-size: 16px;
+    vertical-align: middle;
+    position: relative;
+    top: -1px;
+  }
+}
+
+
+.main {
+  .el-scrollbar__view {
+    overflow-x: hidden;
+  }
+
+  .el-rate {
+    display: inline-block;
+    vertical-align: text-top;
+  }
+
+  .el-upload__tip {
+    line-height: 1.2;
+  }
+}

+ 54 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/db.js

@@ -0,0 +1,54 @@
+const DRAWING_ITEMS = 'drawingItems'
+const DRAWING_ITEMS_VERSION = '1.1'
+const DRAWING_ITEMS_VERSION_KEY = 'DRAWING_ITEMS_VERSION'
+const DRAWING_ID = 'idGlobal'
+const TREE_NODE_ID = 'treeNodeId'
+const FORM_CONF = 'formConf'
+
+export function getDrawingList() {
+  // 加入缓存版本的概念,保证缓存数据与程序匹配
+  const version = localStorage.getItem(DRAWING_ITEMS_VERSION_KEY)
+  if (version !== DRAWING_ITEMS_VERSION) {
+    localStorage.setItem(DRAWING_ITEMS_VERSION_KEY, DRAWING_ITEMS_VERSION)
+    saveDrawingList([])
+    return null
+  }
+
+  const str = localStorage.getItem(DRAWING_ITEMS)
+  if (str) return JSON.parse(str)
+  return null
+}
+
+export function saveDrawingList(list) {
+  localStorage.setItem(DRAWING_ITEMS, JSON.stringify(list))
+}
+
+export function getIdGlobal() {
+  const str = localStorage.getItem(DRAWING_ID)
+  if (str) return parseInt(str, 10)
+  return 100
+}
+
+export function saveIdGlobal(id) {
+  localStorage.setItem(DRAWING_ID, `${id}`)
+}
+
+export function getTreeNodeId() {
+  const str = localStorage.getItem(TREE_NODE_ID)
+  if (str) return parseInt(str, 10)
+  return 100
+}
+
+export function saveTreeNodeId(id) {
+  localStorage.setItem(TREE_NODE_ID, `${id}`)
+}
+
+export function getFormConf() {
+  const str = localStorage.getItem(FORM_CONF)
+  if (str) return JSON.parse(str)
+  return null
+}
+
+export function saveFormConf(obj) {
+  localStorage.setItem(FORM_CONF, JSON.stringify(obj))
+}

+ 512 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/index.js

@@ -0,0 +1,512 @@
+/* eslint-disable no-nested-ternary */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable guard-for-in */
+/**
+ * num 小于0,左缩进num*2个空格; 大于0,右缩进num*2个空格。
+ * @param {string} str 代码
+ * @param {number} num 缩进次数
+ * @param {number} len 【可选】缩进单位,空格数
+ */
+export function indent(str, num, len = 2) {
+  if (num === 0) return str
+  const isLeft = num < 0;
+  const result = [];
+  let reg;
+  let
+    spaces = ''
+  if (isLeft) {
+    num *= -1
+    reg = new RegExp(`(^\\s{0,${num * len}})`, 'g')
+  } else {
+    for (let i = 0; i < num * len; i++) spaces += ' '
+  }
+
+  str.split('\n').forEach(line => {
+    line = isLeft ? line.replace(reg, '') : spaces + line
+    result.push(line)
+  })
+  return result.join('\n')
+}
+
+// 首字母大小
+export function titleCase(str) {
+  return str.replace(/( |^)[a-z]/g, L => L.toUpperCase())
+}
+
+// 下划转驼峰
+export function camelCase(str) {
+  return str.replace(/-[a-z]/g, str1 => str1.substr(-1).toUpperCase())
+}
+
+export function isNumberStr(str) {
+  return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
+}
+
+export const exportDefault = 'export default '
+
+export const beautifierConf = {
+  html: {
+    indent_size: '2',
+    indent_char: ' ',
+    max_preserve_newlines: '-1',
+    preserve_newlines: false,
+    keep_array_indentation: false,
+    break_chained_methods: false,
+    indent_scripts: 'separate',
+    brace_style: 'end-expand',
+    space_before_conditional: true,
+    unescape_strings: false,
+    jslint_happy: false,
+    end_with_newline: true,
+    wrap_line_length: '110',
+    indent_inner_html: true,
+    comma_first: false,
+    e4x: true,
+    indent_empty_lines: true
+  },
+  js: {
+    indent_size: '2',
+    indent_char: ' ',
+    max_preserve_newlines: '-1',
+    preserve_newlines: false,
+    keep_array_indentation: false,
+    break_chained_methods: false,
+    indent_scripts: 'normal',
+    brace_style: 'end-expand',
+    space_before_conditional: true,
+    unescape_strings: false,
+    jslint_happy: true,
+    end_with_newline: true,
+    wrap_line_length: '110',
+    indent_inner_html: true,
+    comma_first: false,
+    e4x: true,
+    indent_empty_lines: true
+  }
+}
+
+function stringify(obj) {
+  return JSON.stringify(obj, (key, val) => {
+    if (typeof val === 'function') {
+      return `${val}`
+    }
+    return val
+  })
+}
+
+function parse(str) {
+  JSON.parse(str, (k, v) => {
+    if (v.indexOf && v.indexOf('function') > -1) {
+      return eval(`(${v})`)
+    }
+    return v
+  })
+}
+
+export function jsonClone(obj) {
+  return parse(stringify(obj))
+}
+
+// 深拷贝对象
+export function deepClone(obj) {
+  const _toString = Object.prototype.toString
+
+  // null, undefined, non-object, function
+  if (!obj || typeof obj !== 'object') {
+    return obj
+  }
+
+  // DOM Node
+  if (obj.nodeType && 'cloneNode' in obj) {
+    return obj.cloneNode(true)
+  }
+
+  // Date
+  if (_toString.call(obj) === '[object Date]') {
+    return new Date(obj.getTime())
+  }
+
+  // RegExp
+  if (_toString.call(obj) === '[object RegExp]') {
+    const flags = []
+    if (obj.global) { flags.push('g') }
+    if (obj.multiline) { flags.push('m') }
+    if (obj.ignoreCase) { flags.push('i') }
+
+    return new RegExp(obj.source, flags.join(''))
+  }
+
+  const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}
+
+  for (const key in obj) {
+    result[key] = deepClone(obj[key])
+  }
+
+  return result
+}
+
+/**
+ * 金额转中文
+ * 思路:                       
+ *                              个
+ *      十     百      千       万
+ *      十万   百万    千万     亿 
+ *      十亿   百亿    千亿    
+ *                              
+ *                              1
+ *      2      3       4        5
+ *      6      7       8        9   
+ *      10
+ * 
+ * 计算步骤
+ * 1. 获取当前数值大小
+ * 2. 排除个位后 数值按个,十,百,千有规律的重复 所以计算其和4的余数 pos % 4
+ * 3. pos = 0 ~ 3 没有最大单位
+ *    pos = 4 ~ 7 最大单位是万
+ *    pos = 8 ~ 11 最大单位是亿
+ * pos / 4 的整数就是最大单位
+ * 
+ */
+export function getAmountChinese(val) {
+  let regexp = /[a-zA-Z]/
+  if (!val && val !== 0) return ''
+  if (Number(val) === 0) return '零元整'
+  if (regexp.test(val)) return '数字较大溢出'
+  const value = val
+  if (val < 0) {
+    val = Number(val.toString().split('-')[1])
+  }
+  const amount = +val
+  if (Number.isNaN(amount)) return ''
+  const NUMBER = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
+  const N_UNIT1 = ['', '拾', '佰', '仟']
+  const N_UNIT2 = ['', '万', '亿', '兆']
+  const D_UNIT = ['角', '分', '厘', '毫']
+  let [integer, decimal] = amount.toString().split('.')
+  if (integer && integer.length > 15) return '数字较大溢出'
+  let res = ''
+  // 整数部分
+  if (integer) {
+    let zeroCount = 0;
+    for (let i = 0, len = integer.length; i < len; i++) {
+      const num = integer.charAt(i);
+      const pos = len - i - 1; // 排除个位后 所处的索引位置
+      const q = pos / 4;
+      const m = pos % 4;
+      if (num === '0') {
+        zeroCount++;
+      } else {
+        if (zeroCount > 0 && m !== 3) res += NUMBER[0];
+        zeroCount = 0;
+        res += NUMBER[parseInt(num)] + N_UNIT1[m];
+      }
+      if (m == 0 && zeroCount < 4) res += N_UNIT2[Math.floor(q)];
+    }
+  }
+  if (Number(integer) != 0) res += '元'
+  // 小数部分
+  if (parseInt(decimal)) {
+    for (let i = 0; i < 4; i++) {
+      const num = decimal.charAt(i)
+      if (parseInt(num)) res += NUMBER[num] + D_UNIT[i]
+    }
+  } else {
+    res += '整'
+  }
+  if (value < 0) res = '负数' + res
+  return res
+}
+
+/**
+ * 将用户输入的连续单个数字合并为一个数
+ * @param {Array} expressions - 记录计算表达式的数组
+ * @returns {Array} 新的数组
+ */
+export const mergeNumberOfExps = expressions => {
+  const res = []
+  const isNumChar = n => /^[\d|\.]$/.test(n)
+  for (let i = 0; i < expressions.length; i++) {
+    if (i > 0 && isNumChar(expressions[i - 1]) && isNumChar(expressions[i])) {
+      res[res.length - 1] += expressions[i]
+      continue
+    }
+    res.push(expressions[i])
+  }
+  return res
+}
+/**
+ * 校验表达式是否符合计算法则
+ * @param {Array} expressions - 合并数字后的表达式数组
+ * @returns {Boolean}
+ */
+export const validExp = (expressions, mergeNum = true) => {
+  const temp = mergeNum ? mergeNumberOfExps(expressions) : expressions
+  const arr = temp.filter(t => !'()'.includes(t))
+  // 去括号后 length应该为奇数  并且第一个字符和最后一个字符应该为数字而非计算符号
+  if (temp.length % 2 === 0 || arr.length % 2 === 0 || Number.isNaN(+arr[0]) || Number.isNaN(+arr[arr.length - 1])) {
+    return false
+  }
+  for (let i = 0; i < arr.length - 1; i += 2) {
+    if (typeof (+arr[i]) !== 'number' || !Number.isNaN(+arr[i + 1])) return false
+  }
+  return true
+}
+/**
+ * 中缀转后缀(逆波兰 Reverse Polish Notation)
+ * @param {Array} exps - 中缀表达式数组
+ */
+export const toRPN = exps => {
+  const s1 = [] // 符号栈
+  const s2 = [] // 输出栈
+  const getTopVal = (stack) => stack.length > 0 ? stack[stack.length - 1] : null
+  const levelCompare = (c1, c2) => {
+    const getIndex = c => ['+-', '×÷', '()'].findIndex(t => t.includes(c))
+    return getIndex(c1) - getIndex(c2)
+  }
+  exps.forEach(t => {
+    if (typeof t === 'string' && Number.isNaN(Number(t))) { // 是符号
+      if (t === '(') {
+        s1.push(t)
+      } else if (t === ')') {
+        let popVal
+        do {
+          popVal = s1.pop()
+          popVal !== '(' && s2.push(popVal)
+        } while (s1.length && popVal !== '(')
+      } else {
+        let topVal = getTopVal(s1)
+        if (!topVal) { // s1 为空 直接push
+          s1.push(t)
+        } else {
+          while (topVal && topVal !== '(' && levelCompare(topVal, t) >= 0) { // 优先级 >= t 弹出到s2
+            s2.push(s1.pop())
+            topVal = getTopVal(s1)
+          }
+          s1.push(t)
+        }
+      }
+      return
+    }
+    s2.push(t) // 数字直接入栈
+  })
+  while (s1.length) {
+    s2.push(s1.pop())
+  }
+  return s2
+}
+/**
+ * 计算后缀表达式的值
+ * @param {Array} rpnExps - 后缀表达式
+ */
+export const calcRPN = rpnExps => {
+  rpnExps = rpnExps.concat()
+  const calc = (x, y, type) => {
+    let a1 = Number(x),
+      a2 = Number(y)
+    switch (type) {
+      case '+':
+        return a1 + a2;
+      case '-':
+        return a1 - a2;
+      case '×':
+        return a1 * a2;
+      case '÷':
+        return a1 / a2;
+    }
+  }
+  for (let i = 2; i < rpnExps.length; i++) {
+    if ('+-×÷'.includes(rpnExps[i])) {
+      let val = calc(rpnExps[i - 2], rpnExps[i - 1], rpnExps[i])
+      rpnExps.splice(i - 2, 3, val)
+      i = i - 2
+    }
+  }
+  return rpnExps[0]
+}
+/**
+ * 简易防抖函数
+ * @param {Function} func -防抖目标函数
+ * @param {Number} gap - 防抖时间间隔
+ */
+export const debounce = (func, gap) => {
+  let timer
+  return function () {
+    timer && clearTimeout(timer)
+    timer = setTimeout(() => {
+      func.apply(this, arguments)
+    }, gap)
+  }
+}
+
+
+
+
+//计算年或者月
+export function getDateDay(Target, type, monthNum) {
+  let date = new Date()
+  let year = date.getFullYear() //获取当前日期的年份
+  let month = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) //获取当前日期的月份
+  let day = date.getDate() //获取当前日期的日
+  let hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
+  let minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
+  let seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
+  let days = new Date(year, month, 0)
+  days = days.getDate(); //获取当前日期中的月的天数
+  let year2 = year;
+  let month2;
+  if (Target == 2) {
+    if (type == 5) {
+      month2 = parseInt(month) + parseInt(monthNum)
+      if (month2 > 12) {
+        year2 = parseInt(year2) + parseInt((parseInt(month2) / 12 == 0 ? 1 : parseInt(month2) / 12));
+        month2 = parseInt(month2) % 12;
+      }
+    } else if (type == 4) {
+      month2 = parseInt(month) - monthNum;
+      if (month2 <= 0) {
+        let absM = Math.abs(month2);
+        year2 = parseInt(year2) - Math.ceil(absM / 12 == 0 ? 1 : parseInt(absM) / 12);
+        month2 = 12 - (absM % 12);
+      }
+    }
+  } else if (Target == 1) {
+    month2 = parseInt(month)
+    if (type == 5) {
+      year2 = parseInt(year) + parseInt(monthNum)
+    } else if (type == 4) {
+      year2 = parseInt(year) - parseInt(monthNum)
+    }
+  }
+  let day2 = day;
+  let days2 = new Date(year2, month2, 0);
+  days2 = days2.getDate();
+  if (day2 > days2) {
+    day2 = days2;
+  }
+  if (month2 < 10) {
+    month2 = '0' + month2;
+  }
+  let t2 = year2 + '-' + month2 + '-' + day2 + ' ' + hours + ':' + minutes + ':' + seconds;
+  return t2;
+}
+
+//计算日
+export function getLaterData(days) {
+
+  let date = new Date();
+  date.setDate(date.getDate() + Number(days));
+  let month = date.getMonth() + 1;
+  let day = date.getDate();
+  let hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
+  let minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
+  let seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
+  return date.getFullYear() + '-' + ('0' + month).slice(-2) + '-' + ('0' + day).slice(-2) + ' ' + hours + ':' + minutes + ':' + seconds;
+}
+export function getBeforeData(num) {
+  let dateArray = []
+  //获取今天日期
+  let myDate = new Date()
+  let hours = myDate.getHours() < 10 ? '0' + myDate.getHours() : myDate.getHours()
+  let minutes = myDate.getMinutes() < 10 ? '0' + myDate.getMinutes() : myDate.getMinutes()
+  let seconds = myDate.getSeconds() < 10 ? '0' + myDate.getSeconds() : myDate.getSeconds()
+  let today = myDate.getFullYear() + '-' + (myDate.getMonth() + 1) + "-" + myDate.getDate();
+  myDate.setDate(myDate.getDate() - num)
+  let dateTemp;  // 临时日期数据
+  let flag = 1;
+  for (let i = 0; i < num; i++) {
+    dateTemp = myDate.getFullYear() + '-' + (myDate.getMonth() + 1) + "-" + myDate.getDate()
+    dateArray.push({
+      date: dateTemp
+    })
+    myDate.setDate(myDate.getDate() + flag);
+  }
+  dateArray.push({
+    date: today
+  })
+  let arr = []
+  let newArr = []
+  dateArray.forEach(item => {
+    arr.push(item.date.split('-'))
+  })
+  for (let i = 0; i < arr.length; i++) {
+    if (arr[i][1] < 10) {
+      arr[i][1] = "0" + arr[i][1]
+    }
+    if (arr[i][2] < 10) {
+      arr[i][2] = "0" + arr[i][2]
+    }
+  }
+  for (let j = 0; j < arr.length; j++) {
+    newArr.push(arr[j].join("-"))
+  }
+  return newArr[0] + ' ' + hours + ':' + minutes + ':' + seconds
+}
+
+export function getBeforeTime(type, val) {
+  let date = new Date()
+  if (type == 4 || type == 1) {
+    date.setHours((Number(date.getHours()) - Number(val)))
+  } else if (type == 5 || type == 2) {
+    date.setMinutes((Number(date.getMinutes()) - Number(val)))
+  } else if (type == 6 || type == 3) {
+    date.setSeconds((Number(date.getSeconds()) - Number(val)))
+  }
+  return date
+}
+export function getLaterTime(type, val) {
+  let date = new Date()
+  if (type == 4 || type == 1) {
+    date.setHours((Number(date.getHours()) + Number(val)))
+  } else if (type == 5 || type == 2) {
+    date.setMinutes((Number(date.getMinutes()) + Number(val)))
+  } else if (type == 6 || type == 3) {
+    date.setSeconds((Number(date.getSeconds()) + Number(val)))
+  }
+  return date
+}
+export function numberThousandth(number, precision) {
+  if (!number) {
+    return null
+  } else {
+    let dec_point = '.'
+    let thousands_sep = ','
+    number = (number + '').replace(/[^0-9+-Ee.]/g, '')
+    let roundtag = 'round' // "ceil","floor","round"
+    const n = !isFinite(+number) ? 0 : Number(number) // 检查number是否是无穷大
+    const prec = !isFinite(+precision) ? 0 : Math.abs(precision)
+    const sep = typeof thousands_sep === 'undefined' ? ',' : thousands_sep
+    const dec = typeof dec_point === 'undefined' ? '.' : dec_point
+    let s = ''
+    const toFixedFix = function (n, prec) {
+      n = Number(n)
+      prec = Number(prec)
+      const k = Math.pow(10, prec) // 10 的 prec 次幂
+      return (
+        '' +
+        parseFloat(
+          Math[roundtag](parseFloat((n * k).toFixed(prec * 2))).toFixed(
+            prec * 2
+          )
+        ) /
+        k
+      ) // 解析一个字符串,并返回一个浮点数。
+    }
+    s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.')
+    const re = /(-?\d+)(\d{3})/
+    while (re.test(s[0])) {
+      s[0] = s[0].replace(re, '$1' + sep + '$2')
+    }
+    if ((s[1] || '').length < prec) {
+      s[1] = s[1] || ''
+      s[1] += new Array(prec - s[1].length + 1).join('0')
+    }
+    // 当数字位数过长去除科学计数法
+    return s.join(dec)
+  }
+}
+export function thousandsFormat(num) {
+  if (!num && num !== 0) return ''
+  const numArr = num.toString().split('.');
+  numArr[0] = numArr[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
+  return numArr.join('.');
+}

+ 154 - 0
src/views/governmentCloud/questionnaireInvestigation/components/Generator/utils/useTextMask.js

@@ -0,0 +1,154 @@
+
+/**
+ * maskType
+ * 1 - 全掩盖
+ * 2 - 姓名-显示前1个字,后1个字
+ * 3 - 手机号-显示前3位,后4位
+ * 4 - 邮箱-显示前3个字,@和之后的字
+ * 5 - 身份证-显示前6位,后3位,虚拟为4位
+ * 6 - IP地址-显示第1段IP
+ * 7 - 车牌号-显示前1个字,后2位
+ * 8 - 银行卡号-显示前6位,后4位
+ * 0 - 自定义规则
+ */
+export const defaultMaskOptions = {
+  filler: '*', // 填充符号
+  maskType: 1, // 掩码规则
+  prefixType: 1, // 开头显示
+  prefixLimit: 0, // 开头字数
+  prefixSpecifyChar: '', // 开头字符
+  suffixType: 1, // 结尾显示
+  suffixLimit: 0, // 结尾字数
+  suffixSpecifyChar: '', // 结尾字符
+  ignoreChar: '', // 显示字符
+  useUnrealMask: false, // 虚拟掩码
+  unrealMaskLength: 1, // 虚拟掩码长度
+};
+
+export function useTextMask(options) {
+  const config = { ...defaultMaskOptions, ...(options || {}) };
+
+  // 全掩盖
+  function maskAll(str) {
+    return config.filler.repeat(str.length);
+  }
+  //姓名 显示前1个字,后1个字
+  function maskName(str) {
+    if (str.length <= 1) return str;
+    const prefix = str[0];
+    if (str.length === 2) return prefix + config.filler;
+    const suffix = str.slice(-1);
+    const maskedChars = config.filler.repeat(str.length - 2);
+    return prefix + maskedChars + suffix;
+  }
+  // 手机号 - 显示前3位,后4位
+  function maskPhoneNumber(str) {
+    if (str.length <= 7) return str;
+    const prefix = str.slice(0, 3);
+    const suffix = str.slice(-4);
+    const maskedChars = config.filler.repeat(str.length - 7);
+    return prefix + maskedChars + suffix;
+  }
+  // 邮箱 - 显示前3个字,@和之后的字
+  function maskEmailAddress(str) {
+    const atIndex = str.indexOf('@');
+    if (str.length <= 3 || (atIndex > -1 && atIndex < 3)) return str;
+    let suffixLength = 0;
+    let maskedCharsLength = str.length - 3;
+    if (atIndex > 0) {
+      suffixLength = atIndex;
+      maskedCharsLength = atIndex - 3;
+    }
+    const prefix = str.slice(0, 3);
+    const suffix = suffixLength ? str.slice(suffixLength) : '';
+    const maskedChars = config.filler.repeat(maskedCharsLength);
+    return prefix + maskedChars + suffix;
+  }
+  // 身份证 - 显示前6位,后3位,虚拟为4位
+  function maskIdNumber(str) {
+    if (str.length <= 9) return str;
+    const prefix = str.slice(0, 6);
+    const suffix = str.slice(-3);
+    const maskedChars = config.filler.repeat(4);
+    return prefix + maskedChars + suffix;
+  }
+  // IP地址-显示第1段IP
+  function maskIPAddress(str) {
+    const segments = str.split('.');
+    if (segments.length < 1) return str;
+    const maskedChars = ('.' + config.filler.repeat(3)).repeat(3);
+    return segments[0] + maskedChars;
+  }
+  // 车牌号-显示前1个字,后2位
+  function maskLicensePlate(str) {
+    if (str.length <= 3) return str;
+    const prefix = str[0];
+    const suffix = str.slice(-2);
+    const maskedChars = config.filler.repeat(str.length - 3);
+    return prefix + maskedChars + suffix;
+  }
+  // 银行卡号-显示前6位,后4位
+  function maskBankCard(str) {
+    if (str.length <= 10) return str;
+    const prefix = str.slice(0, 6);
+    const suffix = str.slice(-4);
+    const maskedChars = config.filler.repeat(str.length - 10);
+    return prefix + maskedChars + suffix;
+  }
+  // 自定义掩码规则
+  function maskCustom(str) {
+    let prefixLength = 0,
+      suffixLength = 0;
+    if (config.prefixType === 2) prefixLength = config.prefixLimit || 0;
+    if ((config.prefixType === 3 || config.prefixType === 4) && config.prefixSpecifyChar) {
+      let specifyCharIndex = str.indexOf(config.prefixSpecifyChar);
+      if (specifyCharIndex > -1) prefixLength = config.prefixType === 3 ? specifyCharIndex : specifyCharIndex + config.prefixSpecifyChar.length;
+    }
+    if (config.suffixType === 2) suffixLength = config.suffixLimit || 0;
+    if ((config.suffixType === 3 || config.suffixType === 4) && config.suffixSpecifyChar) {
+      let specifyCharIndex = str.indexOf(config.suffixSpecifyChar);
+      if (specifyCharIndex > -1)
+        suffixLength = config.suffixType === 3 ? str.length - specifyCharIndex - config.suffixSpecifyChar.length : str.length - specifyCharIndex;
+    }
+    if (prefixLength + suffixLength >= str.length) return str;
+    const prefix = prefixLength ? str.slice(0, prefixLength) : '';
+    const suffix = suffixLength ? str.slice(-suffixLength) : '';
+    let middleChar = '';
+    if (!config.ignoreChar) {
+      const maskedLength = config.useUnrealMask ? config.unrealMaskLength || 1 : str.length - prefixLength - suffixLength;
+      middleChar = config.filler.repeat(maskedLength);
+    } else {
+      const ignoreCharList = config.ignoreChar.split(',');
+      const middleStr = str.slice(prefixLength, str.length - suffixLength);
+      const reg = new RegExp('(' + ignoreCharList.map(o => o.replace(/\*/g, '\\*')).join('|') + ')', 'g');
+      let list = middleStr.split(reg);
+      list = list.map(o => {
+        if (o && !ignoreCharList.includes(o)) {
+          const maskedLength = config.useUnrealMask ? config.unrealMaskLength || 1 : o.length;
+          o = config.filler.repeat(maskedLength);
+        }
+        return o;
+      });
+      middleChar = list.join('');
+    }
+
+    return prefix + middleChar + suffix;
+  }
+
+  // 获取掩码后文本
+  function getMaskedText(str) {
+    if (!str) return '';
+    if (config.maskType === 1) return maskAll(str);
+    if (config.maskType === 2) return maskName(str);
+    if (config.maskType === 3) return maskPhoneNumber(str);
+    if (config.maskType === 4) return maskEmailAddress(str);
+    if (config.maskType === 5) return maskIdNumber(str);
+    if (config.maskType === 6) return maskIPAddress(str);
+    if (config.maskType === 7) return maskLicensePlate(str);
+    if (config.maskType === 8) return maskBankCard(str);
+    if (config.maskType === 0) return maskCustom(str);
+    return str;
+  }
+
+  return { maskAll, maskName, maskPhoneNumber, maskEmailAddress, maskIdNumber, maskIPAddress, maskLicensePlate, maskBankCard, maskCustom, getMaskedText };
+}

+ 213 - 0
src/views/governmentCloud/questionnaireInvestigation/index.vue

@@ -0,0 +1,213 @@
+<template>
+    <div class="JNPF-common-layout">
+        <div class="JNPF-common-layout-center">
+            <el-row class="JNPF-common-search-box" :gutter="16">
+                <el-form @submit.native.prevent>
+                    <el-col :span="6">
+                        <el-form-item label="问卷标题">
+                            <el-input v-model="query.title" placeholder="请输入问卷标题" clearable>
+                            </el-input>
+                        </el-form-item>
+                    </el-col>
+                    <!-- <el-col :span="6">
+                        <el-form-item label="状态">
+                            <JnpfSelect v-model="query.method" placeholder="请选择" clearable
+                                :options="methodOptions" :props="methodProps" multiple>
+                            </JnpfSelect>
+                        </el-form-item>
+                    </el-col> -->
+                    <el-col :span="6">
+                        <el-form-item>
+                            <el-button type="primary" icon="el-icon-search"
+                                @click="getList()">查询</el-button>
+                            <el-button icon="el-icon-refresh-right" @click="reset()">重置</el-button>
+                        </el-form-item>
+                    </el-col>
+                </el-form>
+            </el-row>
+            <div class="JNPF-common-layout-main JNPF-flex-main">
+                <div class="JNPF-common-head">
+                    <div>
+                        <el-button type="primary" icon="icon-ym icon-ym-btn-add"
+                            @click="openAllocation()">新增
+                        </el-button>
+                    </div>
+                    <div class="JNPF-common-head-right">
+                        <el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
+                            <el-link icon="icon-ym icon-ym-Refresh JNPF-common-head-icon"
+                                :underline="false" @click="getList()" />
+                        </el-tooltip>
+                    </div>
+                </div>
+                <JNPF-table v-loading="listLoading" :data="tableData">
+                    <el-table-column prop="topic" label="问卷标题" align="left" show-overflow-tooltip>
+                    </el-table-column>
+                    <el-table-column prop="createBy" label="创建人" align="left" show-overflow-tooltip>
+                    </el-table-column>
+                    <el-table-column prop="anonymity" label="是否匿名" align="left"
+                        show-overflow-tooltip>
+                        <template slot-scope="scope" sortable>
+                            <el-tag v-if="scope.row.anonymity" type="success">是</el-tag>
+                            <el-tag v-else-if="scope.row.anonymity !== null" type="info">否</el-tag>
+                            <el-tag v-else type="info">--</el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="startTime" label="开始时间" align="left"
+                        show-overflow-tooltip>
+                        <template slot-scope="scope" sortable>
+                            {{ getDate(scope.row.startTime, 'YY-MM-DD HH:mm:ss') }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="startTime" label="结束时间" align="left"
+                        show-overflow-tooltip>
+                        <template slot-scope="scope" sortable>
+                            {{ getDate(scope.row.endTime, 'YY-MM-DD HH:mm:ss') }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="status" label="状态" align="left" show-overflow-tooltip>
+                    </el-table-column>
+                    <el-table-column label="操作" fixed="right" width="200">
+                        <template slot-scope="scope">
+                            <el-button type="text" @click="openAllocation(scope.row.id)">编辑
+                            </el-button>
+                            <el-button type="text" style="color: #f56c6c;"
+                                @click="deleteQuestionnaire(scope.row.id)">删除
+                            </el-button>
+                            <el-button type="text" style="color: #e6a23c;"
+                                @click="openIssuedForm(scope.row.id)">下发
+                            </el-button>
+                            <el-button type="text" @click="openStatistics(scope.row.id)">统计
+                            </el-button>
+                        </template>
+                    </el-table-column>
+                </JNPF-table>
+                <pagination :total="total" :page.sync="query.currentPage"
+                    :limit.sync="query.pageSize" @pagination="getList" />
+            </div>
+        </div>
+        <Allocation v-if="allocationVisible" ref="Allocation" @close="closeAllocation" />
+        <Statistics v-if="statisticsVisible" ref="Statistics" @close="closeStatistics" />
+        <IssuedForm v-if="issuedFormVisible" ref="IssuedForm" @close='closeIssuedForm' />
+
+    </div>
+</template>
+
+<script>
+
+import dayjs from 'dayjs'
+import { getDictionaryDataSelector } from '@/api/systemData/dictionary'
+
+import {
+    getQuestionnaireList
+} from "@/api/governmentCloud/questionnaireInvestigation/questionnaireInvestigation";
+import Allocation from './Allocation'
+import Statistics from './Statistics'
+import IssuedForm from './IssuedForm'
+
+export default {
+    components: {
+        Allocation,
+        Statistics,
+        IssuedForm
+    },
+    data() {
+        return {
+            query: {
+                currentPage: 1,
+                pageSize: 10,
+                dataType: "0",
+                title: "",
+            },
+            selectProps: { "label": "fullName", "value": "enCode" },
+            total: 0,
+            listLoading: false,
+            tableData: [],
+
+
+            typeList: [],
+            statusList: [],
+            allocationVisible: false,
+            statisticsVisible: false,
+            issuedFormVisible: false
+        }
+    },
+    async created() {
+        this.getList()
+    },
+    methods: {
+        openStatistics(id) {
+            this.statisticsVisible = true
+            this.$nextTick(() => {
+                this.$refs.Statistics.init(id || '')
+            })
+        },
+        openAllocation(id) {
+            this.allocationVisible = true
+            this.$nextTick(() => {
+                this.$refs.Allocation.init(id || '')
+            })
+        },
+
+        openIssuedForm(id) {
+            this.issuedFormVisible = true
+            this.$nextTick(() => {
+                this.$refs.IssuedForm.init(id)
+            })
+        },
+        closeStatistics() {
+            this.statisticsVisible = false
+            this.getList()
+        },
+        closeAllocation() {
+            this.allocationVisible = false
+            this.getList()
+        },
+        closeIssuedForm() {
+            this.issuedFormVisible = false
+            this.getList()
+        },
+        getDate(time) {
+            let str = time ? dayjs(time).format('YYYY-MM-DD HH:mm:ss') : '--'
+            return str
+        },
+        getList() {
+            this.listLoading = true;
+            let params = {
+                title: this.query.title
+            }
+            getQuestionnaireList(params).then(res => {
+                if (res.code == 200) {
+                    this.tableData = res.data.list
+                    this.total = res.data.pagination.total
+                    this.listLoading = false
+                } else {
+                    this.listLoading = false
+                }
+            })
+        },
+    }
+
+}
+
+</script>
+
+<style lang="scss" scoped>
+.button {
+    padding: 0px 5px;
+    border-radius: 3px;
+    background-color: #e3eefd;
+    color: #1c77f2;
+    margin-right: 5px;
+    height: 20px;
+    line-height: 20px;
+    display: inline-block;
+}
+.suggest-content {
+    overflow: hidden; /* 确保内容不会溢出容器 */
+    display: -webkit-box; /* 作为弹性伸缩盒子模型显示 */
+    -webkit-line-clamp: 2; /* 限制在两行 */
+    -webkit-box-orient: vertical; /* 垂直排列盒子 */
+    text-overflow: ellipsis; /* 多出的文字用省略号表示 */
+    white-space: normal; /* 允许文本换行 */
+}
+</style>