diff --git a/report-ui/src/components/AnjiPlus/anji-autocomplete.vue b/report-ui/src/components/AnjiPlus/anji-autocomplete.vue
new file mode 100644
index 00000000..c2d9a70b
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-autocomplete.vue
@@ -0,0 +1,139 @@
+<template>
+  <el-autocomplete
+    v-model.trim="inputValue"
+    :debounce="500"
+    class="inline-input"
+    :fetch-suggestions="querySearch"
+    :disabled="disabled"
+    @select="handleSelect"
+    @input="changeInput"
+  >
+    <template slot-scope="{ item }">
+      <div class="name">{{ getItemLabel(item, item.value) }}</div>
+      <span class="addr">{{ item[option] }}</span>
+    </template>
+  </el-autocomplete>
+</template>
+<script>
+import request from "@/utils/request";
+export default {
+  props: {
+    disabled: {
+      type: Boolean,
+      default: () => {
+        return false;
+      }
+    },
+    value: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    url: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    appointValue: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    label: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    option: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    }
+  },
+  data() {
+    return {
+      restaurants: [],
+      inputValue: ""
+    };
+  },
+  watch: {
+    value(val) {
+      this.echoInput(val);
+    }
+  },
+  mounted() {
+    this.echoInput(this.value);
+  },
+  methods: {
+    getItemLabel(item, value) {
+      if (this.label.indexOf("${") < 0 && this.label.indexOf("}" < 0)) {
+        return item[this.label];
+      }
+      let reg = /\$\{[a-zA-Z0-9]*\}/g;
+      let list = this.label.match(reg);
+      console.log(list);
+      // ["${id}", "${text}"]
+      let result = this.label;
+      for (let i = 0; i < list.length; i++) {
+        let sub = list[i];
+        let key = sub.replace("${", "").replace("}", "");
+        result = result.replace(sub, item[key]);
+      }
+      return value + " " + result;
+    },
+    querySearch(queryString, cb) {
+      request({ url: this.url }).then(res => {
+        if (res.code == 200 && res.data) {
+          this.restaurants = res.data;
+        } else {
+          this.restaurants = [];
+        }
+        this.restaurants = JSON.parse(
+          JSON.stringify(this.restaurants).replace(
+            new RegExp(this.appointValue, "g"),
+            "value"
+          )
+        );
+        let results = queryString
+          ? this.restaurants.filter(this.createFilter(queryString))
+          : this.restaurants;
+        cb(results);
+      });
+    },
+    createFilter(queryString) {
+      return restaurant => {
+        return (
+          restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) ===
+          0
+        );
+      };
+    },
+    handleSelect(item) {
+      this.$emit("input", item.value);
+      this.$emit("change", item.value, item);
+    },
+    changeInput(val) {
+      this.$emit("input", val);
+      this.$emit("change", val);
+    },
+    // 回显
+    echoInput(value) {
+      if (!value) {
+        this.inputValue = "";
+      } else {
+        this.inputValue = value;
+      }
+    }
+  }
+};
+</script>
+<style lang="less" scoped>
+.inline-input {
+  width: 100%;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus/anji-cascader.vue b/report-ui/src/components/AnjiPlus/anji-cascader.vue
new file mode 100644
index 00000000..13c08e51
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-cascader.vue
@@ -0,0 +1,314 @@
+<template>
+  <div>
+    <el-row>
+      <el-col v-if="selectIsHide('country')" :span="4">
+        <el-select
+          v-model.trim="countryCode"
+          filterable
+          :placeholder="$lang('GLOBAL.countryName')"
+          :disabled="disabled"
+          clearable
+          @change="countryChange"
+        >
+          <el-option
+            v-for="(option, i) in countryArr"
+            :key="i"
+            :value="option.value"
+            :label="option.label"
+          />
+        </el-select>
+      </el-col>
+      <el-col v-if="selectIsHide('province')" :span="7">
+        <el-select
+          v-model.trim="provinceCode"
+          filterable
+          :placeholder="$lang('GLOBAL.provinceName')"
+          :disabled="disabled"
+          clearable
+          @change="provinceChange"
+        >
+          <el-option
+            v-for="(option, i) in casCaredArr"
+            :key="i"
+            :value="option.value"
+            :label="option.label"
+          />
+        </el-select>
+      </el-col>
+      <el-col v-if="selectIsHide('city')" :span="7">
+        <el-select
+          v-model.trim="cityCode"
+          filterable
+          :placeholder="$lang('GLOBAL.cityName')"
+          :disabled="disabled"
+          clearable
+          @change="cityChange"
+        >
+          <el-option
+            v-for="(option, x) in cityArr"
+            :key="x"
+            :value="option.value"
+            :label="option.label"
+          />
+        </el-select>
+      </el-col>
+      <el-col v-if="selectIsHide('area')" :span="6">
+        <el-select
+          v-model.trim="areaCode"
+          filterable
+          :placeholder="$lang('GLOBAL.districtName')"
+          :disabled="disabled"
+          clearable
+          @change="districtChange"
+        >
+          <el-option
+            v-for="(option, y) in districtArr"
+            :key="y"
+            :value="option.value"
+            :label="option.label"
+          />
+        </el-select>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+<script>
+import axios from "axios";
+import { getToken } from "@/utils/auth";
+import Cookies from "js-cookie";
+export default {
+  name: "Cselect",
+  props: {
+    url: {
+      type: String,
+      default: () => "/meta/metaAreaInfo/countryTree"
+    },
+    value: null,
+    singleDisplay: String,
+    disabled: Boolean
+  },
+  data() {
+    return {
+      countryArr: [],
+      casCaredArr: [],
+      cityArr: [],
+      districtArr: [],
+      countryCode: "", // 国code
+      provinceCode: "", // 省code
+      cityCode: "", // 州市code
+      areaCode: "", // 区县code
+      countryName: "",
+      provinceName: "",
+      cityName: "",
+      areaName: ""
+    };
+  },
+  watch: {
+    value(value) {
+      this.echoSelect(value);
+      this.initData();
+    }
+  },
+  mounted() {
+    this.initData();
+    this.echoSelect(this.value);
+  },
+  methods: {
+    // singleDisplay 不配显示 country province city area 配那个那个不显示
+    selectIsHide(val) {
+      if (this.singleDisplay === undefined) {
+        return true;
+      } else {
+        return !(this.singleDisplay.indexOf(val) >= 0);
+      }
+    },
+    // 查询数据
+    initData() {
+      this.queryData();
+    },
+    queryData() {
+      axios({
+        url: process.env.VUE_APP_BASE_API + this.url,
+        methods: "get",
+        headers: {
+          Authorization: getToken(),
+          systemCode: process.env.VUE_APP_SYSTEM_CODE
+        }
+      }).then(res => {
+        const data = res.data;
+        if (data.code != 200 || data.data.length == 0) return;
+        this.countryArr = data.data;
+        !this.selectIsHide("country") &&
+          (this.casCaredArr = data.data[0].children);
+        this.updateCountry();
+        this.updateCity();
+        this.updateDistrict();
+      });
+    },
+    updateCountry() {
+      for (let i in this.countryArr) {
+        let obj = this.countryArr[i];
+        if (obj.value == this.countryCode) {
+          this.casCaredArr = obj.children;
+          break;
+        }
+      }
+    },
+    updateCity() {
+      for (let i in this.casCaredArr) {
+        let obj = this.casCaredArr[i];
+        if (obj.value) {
+          if (obj.value == this.provinceCode) {
+            this.cityArr = obj.children;
+            break;
+          }
+        }
+      }
+    },
+    updateDistrict() {
+      for (let i in this.cityArr) {
+        let obj = this.cityArr[i];
+        if (obj.value == this.cityCode) {
+          this.districtArr = obj.children;
+          break;
+        }
+      }
+    },
+
+    // 国
+    countryChange(val) {
+      if (val) {
+        this.updateCountry();
+        this.provinceCode = "";
+        this.cityCode = "";
+        this.areaCode = "";
+        const casCared = this.countryArr.find((n, index) => {
+          if (n.value == val) {
+            return n;
+          }
+        });
+        this.countryName = casCared.label;
+        const obj = {
+          countryName: this.countryName,
+          countryCode: this.countryCode,
+          provinceCode: this.provinceCode,
+          cityCode: this.cityCode,
+          areaCode: this.areaCode,
+          provinceName: this.provinceName,
+          cityName: this.cityName,
+          areaName: this.areaName
+        };
+        this.$emit("input", obj);
+        this.$emit("change", obj);
+      } else {
+        this.$emit("input", {});
+        this.$emit("change", {});
+      }
+    },
+    // 省
+    provinceChange(val) {
+      if (val) {
+        this.updateCity();
+        this.cityCode = "";
+        this.areaCode = "";
+        const casCared = this.casCaredArr.find((n, index) => {
+          if (n.value == val) {
+            return n;
+          }
+        });
+        this.provinceName = casCared.label;
+        const obj = {
+          countryName: this.countryName,
+          countryCode: this.countryCode,
+          provinceCode: this.provinceCode,
+          cityCode: this.cityCode,
+          areaCode: this.areaCode,
+          provinceName: this.provinceName,
+          cityName: this.cityName,
+          areaName: this.areaName
+        };
+        this.$emit("input", obj);
+        this.$emit("change", obj);
+      } else {
+        this.$emit("input", {});
+        this.$emit("change", {});
+      }
+    },
+    // 市
+    cityChange(val) {
+      if (val) {
+        this.areaCode = "";
+        this.updateDistrict();
+        const city = this.cityArr.find((n, index) => {
+          if (n.value == val) {
+            return n;
+          }
+        });
+        this.cityName = city.label;
+        const obj = {
+          countryName: this.countryName,
+          countryCode: this.countryCode,
+          provinceCode: this.provinceCode,
+          cityCode: this.cityCode,
+          areaCode: this.areaCode,
+          provinceName: this.provinceName,
+          cityName: this.cityName,
+          areaName: this.areaName
+        };
+        this.$emit("input", obj);
+        this.$emit("change", obj);
+      } else {
+        this.$emit("input", {});
+        this.$emit("change", {});
+      }
+    },
+    // 区
+    districtChange(val) {
+      if (val) {
+        const district = this.districtArr.find((n, index) => {
+          if (n.value == val) {
+            return n;
+          }
+        });
+        this.areaName = district.label;
+        const obj = {
+          countryName: this.countryName,
+          countryCode: this.countryCode,
+          provinceCode: this.provinceCode,
+          cityCode: this.cityCode,
+          areaCode: this.areaCode,
+          provinceName: this.provinceName,
+          cityName: this.cityName,
+          areaName: this.areaName
+        };
+        this.$emit("input", obj);
+        this.$emit("change", obj);
+      } else {
+        this.$emit("input", {});
+        this.$emit("change", {});
+      }
+    },
+    echoSelect(value) {
+      if (!value) {
+        this.countryCode = "";
+        this.provinceCode = "";
+        this.cityCode = "";
+        this.areaCode = "";
+        this.countryName = "";
+        this.provinceName = "";
+        this.cityName = "";
+        this.areaName = "";
+      } else {
+        this.countryName = this.value.countryName;
+        this.countryCode = this.value.countryCode;
+        this.provinceCode = this.value.provinceCode;
+        this.cityCode = this.value.cityCode;
+        this.areaCode = this.value.areaCode || this.value.regionCode;
+        this.provinceName = this.value.provinceName;
+        this.cityName = this.value.cityName;
+        this.areaName = this.value.areaName || this.value.regionName;
+      }
+    }
+  }
+};
+</script>
diff --git a/report-ui/src/components/AnjiPlus/anji-checkbox.vue b/report-ui/src/components/AnjiPlus/anji-checkbox.vue
new file mode 100644
index 00000000..e1a649df
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-checkbox.vue
@@ -0,0 +1,127 @@
+<template>
+  <el-checkbox-group v-model="selectValue" class="filter-item" @change="change">
+    <el-checkbox
+      v-for="item in options"
+      :key="item.id"
+      :label="item.id"
+      :disabled="disabled"
+      >{{ item.text }}</el-checkbox
+    >
+  </el-checkbox-group>
+</template>
+
+<script>
+import request from "@/utils/request";
+import { valueOf } from "screenfull";
+export default {
+  props: {
+    dictCode: null, // 当传入dictCode时,可以不用传递url
+    url: null,
+    value: null,
+    placeholder: null,
+    label: {
+      type: String,
+      default: "text"
+    },
+    option: {
+      type: String,
+      default: "id"
+    },
+    multiple: null,
+    localOptions: null,
+    disabled: null,
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+    collapseTags: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      options: null,
+      selectValue: []
+    };
+  },
+  computed: {
+    // 根据dictCode和url拼出最终的请求url
+    requestUrl() {
+      if (this.url != null && this.url.trim() != "") {
+        return this.url;
+      }
+      if (this.dictCode != null && this.dictCode.trim() != "") {
+        return `/tms/gaeaDict/select/${this.dictCode}`;
+      }
+      return null;
+    }
+  },
+  watch: {
+    value: function(val, oldVal) {
+      this.echoCheckboxValue(val);
+    }
+  },
+  mounted() {
+    this.echoCheckboxValue(this.value);
+    if (this.requestUrl == null) {
+      this.options = this.localOptions;
+      return;
+    }
+    this.queryData();
+  },
+  methods: {
+    change(value) {
+      const strValue = value.join(",");
+      if (value === "") {
+        value = null;
+      }
+      this.$emit("input", strValue);
+      this.$emit("change", strValue, this.options);
+    },
+    // 从本地localStorage取 gaeaDict
+    getOptionsFromLocalStorage() {
+      let dicts = JSON.parse(localStorage.getItem("gaeaDict"));
+      let options = [];
+      if (!dicts.hasOwnProperty(this.dictCode)) {
+        return [];
+      }
+      let dictItems = dicts[this.dictCode];
+      for (let i = 0; i < dictItems.length; i++) {
+        let dictItem = dictItems[i];
+        options.push({ id: dictItem.id.toString(), text: dictItem.text });
+      }
+      return options;
+    },
+    queryData() {
+      // 所有从本地localStorage取,因为在App.vue中已经请求远程保存到本地了
+      let options = this.getOptionsFromLocalStorage();
+      if (this.isNotBlank(options)) {
+        this.options = options;
+        return;
+      }
+      // 本地localStorage取不到,再从远程接口取
+      if (this.requestUrl == null) {
+        return;
+      }
+      request({
+        url: this.requestUrl,
+        params: {
+          multiple: this.multiple == null ? null : 1
+        }
+      }).then(response => {
+        this.options = response.data;
+      });
+    },
+    // 回显
+    echoCheckboxValue(val) {
+      if (!val) {
+        this.selectValue = [];
+      } else {
+        const arr = val.split(",");
+        this.selectValue = arr;
+      }
+    }
+  }
+};
+</script>
diff --git a/report-ui/src/components/AnjiPlus/anji-contextMenu.vue b/report-ui/src/components/AnjiPlus/anji-contextMenu.vue
new file mode 100644
index 00000000..f04babbe
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-contextMenu.vue
@@ -0,0 +1,50 @@
+<!--
+ * @Descripttion: 右键菜单
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-10-21 15:52:03
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2021-10-21 19:40:05
+-->
+<template>
+  <div v-show="visible" class="contentmenu" :style="styleObj">
+    <slot />
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    styleObj: Object,
+    visible: Boolean,
+  },
+  data() {
+    return {}
+  },
+  watch: {
+    visible(value) {
+      if (value) {
+        document.body.addEventListener('click', this.closeMenu)
+      } else {
+        document.body.removeEventListener('click', this.closeMenu)
+      }
+    },
+  },
+  methods: {
+    closeMenu() {
+      this.$emit('update:visible', false)
+    },
+  },
+}
+</script>
+<style lang="scss" scoped>
+.contentmenu {
+  position: fixed;
+  z-index: 99999;
+  list-style: none;
+  -webkit-box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
+  padding: 0;
+  background: #fff;
+  cursor: pointer;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus/anji-countryCity.vue b/report-ui/src/components/AnjiPlus/anji-countryCity.vue
new file mode 100644
index 00000000..f98a3465
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-countryCity.vue
@@ -0,0 +1,97 @@
+<template>
+  <el-cascader v-model="selectValue" style="width: 100%" :props="{ lazy: true, lazyLoad: lazyLoad, label: 'text', value: 'id' }" :options="countryCity" />
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+
+export default {
+  props: {
+    value: null,
+  },
+  computed: {
+    ...mapGetters(['countryCity']),
+    selectValue: {
+      get: function() {
+        return [...this.value]
+      },
+      set: function(val) {
+        this.$emit('update:value', val)
+      },
+    },
+  },
+  mounted() {
+    if (this.value && this.value.length > 0) {
+      this.initCity()
+    } else {
+      this.$store.dispatch('dict/add_countryCity', { level: 0 })
+    }
+  },
+  methods: {
+    getCityName() {
+      const value = this.selectValue
+      const country = this.countryCity.find((item) => item.id === value[0])
+      const province = country.children.find((item) => item.id === value[1])
+      const city = province.children.find((item) => item.id === value[2])
+      const region = city.children.find((item) => item.id === value[3])
+      return [country.text, province.text, city.text, region.text]
+    },
+    //   初始化数据
+    initCity() {
+      const value = this.value
+      // console.log(value)
+      if (!value) {
+        return
+      }
+      const country = this.countryCity.find((item) => item.id === value[0])
+      console.log(country)
+      if (!country || !country.children) {
+        this.getData().then((list) => {
+          this.getData({ country: value[0], level: 1 }).then((list) => {
+            this.getData({ country: value[0], province: value[1], level: 2 }).then((list) => {
+              this.getData({ country: value[0], province: value[1], city: value[2], level: 3, update: true }).then((list) => {})
+            })
+          })
+        })
+      } else {
+        const province = country.children.find((item) => item.id === value[1])
+        if (!province || !province.children) {
+          this.getData({ country: value[0], level: 1 }).then((list) => {
+            this.getData({ country: value[0], province: value[1], level: 2 }).then((list) => {
+              this.getData({ country: value[0], province: value[1], city: value[2], level: 3, update: true }).then((list) => {})
+            })
+          })
+        } else {
+          const city = province.children.find((item) => item.id === value[2])
+          if (!city || !city.children) {
+            this.getData({ country: value[0], province: value[1], level: 2 }).then((list) => {
+              this.getData({ country: value[0], province: value[1], city: value[2], level: 3, update: true }).then((list) => {})
+            })
+          } else {
+            const region = city.children.find((item) => item.id === value[3])
+            if (!region) {
+              this.getData({ country: value[0], province: value[1], city: value[2], level: 3, update: true }).then((list) => {})
+            }
+          }
+        }
+      }
+    },
+    getData(params) {
+      return this.$store.dispatch('dict/add_countryCity', params)
+    },
+    lazyLoad(node, resolve) {
+      console.log(node)
+      const { level, path, data } = node
+      if (data && data.children) {
+        resolve(data.children)
+      } else {
+        if (level === 0) {
+          return
+        }
+        const params = { country: path[0], province: path[1], city: path[2], level }
+        this.$store.dispatch('dict/add_countryCity', params).then((list) => resolve(list))
+      }
+    },
+  },
+}
+</script>
diff --git a/report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue b/report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue
index bcc89183..059796f5 100644
--- a/report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue
+++ b/report-ui/src/components/AnjiPlus/anji-crud/anji-crud.vue
@@ -12,330 +12,357 @@
       />
     </div>
     <div class="right-container">
-      <!-- 查询表单开始 -->
-      <el-form
-        ref="formSearch"
-        :model="queryParams"
-        label-width="100px"
-        v-permission="option.buttons.query.permission"
-      >
-        <el-row>
-          <el-col
-            v-for="(item, index) in queryFormFieldExcludeTree"
-            :key="item.field"
-            :span="queryFormFieldSpan(item)"
-          >
-            <el-form-item
-              v-if="index <= 2 || (index > 2 && queryParams.showMoreSearch)"
-              :label="item.label"
-              :rules="item.rules"
-              :prop="item.field"
-            >
-              <!-- 输入框 -->
-              <el-input
-                v-if="
-                  item.inputType == 'input' || item.inputType == 'input-number'
-                "
-                v-model.trim="queryParams[item.field]"
-                :placeholder="item.placeholder || '请输入'"
-                :clearable="item.clearable !== false"
-                :disabled="item.disabled"
-                @change="value => queryFormChange(item.field, value)"
-              />
-              <!-- 开关 -->
-              <el-switch
-                v-else-if="item.inputType == 'switch'"
-                v-model.trim="queryParams[item.field]"
-                :disabled="item.disabled"
-                :active-value="item.switchOption.disableValue"
-                :inactive-value="item.switchOption.enableValue"
-                active-color="#5887fb"
-                inactive-color="#ccc"
-                @change="value => queryFormChange(item.field, value)"
-              />
-              <!-- 下拉框 -->
-              <anji-select
-                v-else-if="item.inputType == 'anji-select'"
-                v-model.trim="queryParams[item.field]"
-                :multiple="item.anjiSelectOption.multiple"
-                :dict-code="item.anjiSelectOption.dictCode"
-                :url="item.anjiSelectOption.url"
-                :method="item.anjiSelectOption.method"
-                :query-param="item.anjiSelectOption.queryParam"
-                :option="item.anjiSelectOption.option"
-                :label="item.anjiSelectOption.label"
-                :disabled-options="item.anjiSelectOption.disabledOptions"
-                :disabled="item.disabled"
-                :merge-label="item.anjiSelectOption.mergeLabel"
-                @change="value => queryFormChange(item.field, value)"
-              />
-              <!-- 日期时间框  -->
-              <el-date-picker
-                v-else-if="item.inputType.indexOf('date') >= 0"
-                v-model="queryParams[item.field]"
-                style="width: 100%"
-                :placeholder="item.placeholder || '请选择'"
-                :type="item.inputType"
-                :clearable="item.clearable !== false"
-                @change="value => queryFormChange(item.field, value)"
-              />
-              <!-- 待扩展的表单类型,请自行扩展 -->
-              <el-input
-                v-else
-                placeholder="组件不支持此类型表单请至组件内部自行扩展"
-                disabled
-              />
-            </el-form-item>
-          </el-col>
-
-          <el-col :span="6" style="text-align: center">
-            <el-button type="primary" @click="handleQueryForm('query')"
-              >查询</el-button
-            >
-            <el-button type="danger" @click="handleResetForm()">重置</el-button>
-            <a
-              v-if="queryFormFieldExcludeTree.length > 3"
-              style="margin-left: 8px"
-              @click="handleToggleMoreSearch"
-            >
-              {{ queryParams.showMoreSearch == true ? "收起" : "展开" }}
-              <i
-                :class="
-                  queryParams.showMoreSearch
-                    ? 'el-icon-arrow-up'
-                    : 'el-icon-arrow-down'
-                "
-              />
-            </a>
-          </el-col>
-        </el-row>
-      </el-form>
-      <!-- 查询表单结束 -->
+      <div class="top_part">
+        <!-- 查询表单开始 -->
+        <el-form
+          v-if="queryFormTreeHide == false"
+          ref="formSearch"
+          :model="queryParams"
+          label-width="100px"
+          style="border-bottom: 1px solid #dbdde3; margin-bottom: 10px"
+        >
+          <div class="screenFlex">
+            <div class="searchFormFlex">
+              <el-row>
+                <template v-for="(item, index) in queryFormFieldExcludeTree">
+                  <el-col
+                    v-if="index < 3 || queryParams.showMoreSearch"
+                    :key="item.field"
+                    :span="8"
+                  >
+                    <el-form-item
+                      :label="item.label"
+                      :rules="item.rules"
+                      :prop="item.field"
+                    >
+                      <!-- 输入框 -->
+                      <el-input
+                        v-if="
+                          item.inputType == 'input' ||
+                            item.inputType == 'input-number'
+                        "
+                        v-model.trim="queryParams[item.field]"
+                        :placeholder="item.placeholder || '请输入'"
+                        :clearable="item.clearable !== false"
+                        :disabled="item.disabled"
+                        @change="value => queryFormChange(item.field, value)"
+                      />
+                      <!-- 开关 -->
+                      <el-switch
+                        v-else-if="item.inputType == 'switch'"
+                        v-model.trim="queryParams[item.field]"
+                        :disabled="item.disabled"
+                        :active-value="item.switchOption.disableValue"
+                        :inactive-value="item.switchOption.enableValue"
+                        active-color="#5887fb"
+                        inactive-color="#ccc"
+                        @change="value => queryFormChange(item.field, value)"
+                      />
+                      <!-- 下拉框 -->
+                      <anji-select
+                        v-else-if="item.inputType == 'anji-select'"
+                        v-model.trim="queryParams[item.field]"
+                        :multiple="item.anjiSelectOption.multiple"
+                        :dict-code="item.anjiSelectOption.dictCode"
+                        :url="item.anjiSelectOption.url"
+                        :method="item.anjiSelectOption.method"
+                        :query-param="item.anjiSelectOption.queryParam"
+                        :option="item.anjiSelectOption.option"
+                        :label="item.anjiSelectOption.label"
+                        :disabled-options="
+                          item.anjiSelectOption.disabledOptions
+                        "
+                        :disabled="item.disabled"
+                        :merge-label="item.anjiSelectOption.mergeLabel"
+                        :local-options="item.anjiSelectOption.localOptions"
+                        @change="value => queryFormChange(item.field, value)"
+                      />
+                      <!-- 日期时间框  -->
+                      <el-date-picker
+                        v-else-if="item.inputType.indexOf('date') >= 0"
+                        v-model="queryParams[item.field]"
+                        style="width: 100%"
+                        :placeholder="item.placeholder || '请选择'"
+                        :type="item.inputType"
+                        :format="item.format"
+                        :value-format="item.valueFormat"
+                        :clearable="item.clearable !== false"
+                        @change="value => queryFormChange(item.field, value)"
+                      />
+                      <anji-cascader
+                        v-else-if="item.inputType == 'anji-cascader'"
+                        v-model.trim="queryParams[item.field]"
+                        :disabled="item.disabled"
+                        :single-display="item.anjiCascader.singleDisplay"
+                        :url="item.anjiCascader.url"
+                        @change="
+                          value => queryFormChange(item.field, value, null)
+                        "
+                      />
+                      <!-- 待扩展的表单类型,请自行扩展 -->
+                      <el-input
+                        v-else
+                        placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                        disabled
+                      />
+                    </el-form-item>
+                  </el-col>
+                </template>
+              </el-row>
+            </div>
 
-      <!-- 批量操作 -->
-      <slot name="buttonLeftOnTable" />
-      <el-button
-        v-if="
-          option.buttons.add.isShow == undefined
-            ? true
-            : option.buttons.add.isShow
-        "
-        v-permission="option.buttons.add.permission"
-        type="primary"
-        icon="el-icon-plus"
-        @click="handleOpenEditView('add')"
-        >新增</el-button
-      >
-      <el-button
-        v-if="
-          option.buttons.delete.isShow == undefined
-            ? true
-            : option.buttons.delete.isShow
-        "
-        v-permission="option.buttons.delete.permission"
-        :disabled="disableBatchDelete"
-        type="danger"
-        icon="el-icon-delete"
-        @click="handleDeleteBatch()"
-        >删除</el-button
-      >
+            <div class="screenButtons">
+              <el-button
+                v-if="queryFormFieldExcludeTree.length > 3"
+                class="button"
+                size="mini"
+                plain
+                @click="handleToggleMoreSearch"
+              >
+                <i
+                  :class="
+                    queryParams.showMoreSearch
+                      ? 'el-icon-arrow-up'
+                      : 'el-icon-arrow-down'
+                  "
+                />
+                {{ queryParams.showMoreSearch == true ? "展开" : "收起" }}
+              </el-button>
+              <el-button
+                class="button"
+                size="mini"
+                plain
+                @click="handleResetForm()"
+                >重置</el-button
+              >
+              <el-button
+                class="button"
+                size="mini"
+                type="primary"
+                @click="handleQueryForm('query')"
+                >查询</el-button
+              >
+            </div>
+          </div>
+        </el-form>
+        <!-- 查询表单结束 -->
+        <!-- 批量操作 -->
+        <div style="padding-bottom: 8px">
+          <slot name="buttonLeftOnTable" :selection="checkRecords" />
+          <el-button
+            v-if="
+              option.buttons.add.isShow == undefined
+                ? true
+                : option.buttons.add.isShow
+            "
+            v-permission="option.buttons.add.permission"
+            class="button"
+            plain
+            icon="el-icon-plus"
+            @click="handleOpenEditView('add')"
+            >新增</el-button
+          >
+          <el-button
+            v-if="
+              option.buttons.delete.isShow == undefined
+                ? true
+                : option.buttons.delete.isShow
+            "
+            v-permission="option.buttons.delete.permission"
+            class="button"
+            plain
+            :disabled="disableBatchDelete"
+            type="danger"
+            icon="el-icon-delete"
+            @click="handleDeleteBatch()"
+            >删除</el-button
+          >
+          <slot name="buttonRightOnTable" :selection="checkRecords" />
+        </div>
+      </div>
 
       <!-- 表格开始 -->
-      <el-table
-        class="anji_curd_table"
-        :data="records"
-        border
-        @selection-change="handleSelectionChange"
-        @sort-change="handleSortChange"
-      >
-        <!--多选-->
-        <el-table-column fixed type="selection" width="50" align="center" />
-        <!--隐藏列-->
-        <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
-          <template slot-scope="scope">
-            <p
-              v-for="item in tableExpandColumns"
-              :key="item.field"
-              class="table-expand-item"
-            >
-              <span class="titel"> {{ item.label }}: </span>
-              <span>{{ scope.row[item.field] }}</span>
-            </p>
-          </template>
-        </el-table-column>
-        <!--序号-->
-        <el-table-column label="序号" min-width="50" align="center">
-          <template slot-scope="scope">
-            {{
-              queryParams.pageSize * (queryParams.pageNumber - 1) +
-                scope.$index +
-                1
-            }}
-          </template>
-        </el-table-column>
-
-        <template v-for="item in option.columns">
-          <el-table-column
-            v-if="item.tableHide != true && item.columnType != 'expand'"
-            :key="item.field"
-            :prop="item.field"
-            :label="fieldLabel(item)"
-            :min-width="item.minWidth || 110"
-            :sortable="item.sortable"
-            :show-overflow-tooltip="true"
+      <div class="main_part">
+        <div class="page_main">
+          <el-table
+            ref="tables"
+            class="elTable"
             align="center"
+            :data="records"
+            :row-class-name="tableRowClassName"
+            border
+            @selection-change="handleSelectionChange"
+            @sort-change="handleSortChange"
+            @row-dblclick="handleSelectionDblclick"
+            @row-contextmenu="rowContextMenu"
           >
-            <template slot-scope="scope">
-              <div v-if="item.columnType == 'imgPreview'">
-                <!-- 图片缩略图-->
-                <el-image
-                  style="width: 25%; height: 50%"
-                  fit="contain"
-                  :src="scope.row[item.field]"
-                  :preview-src-list="[scope.row[item.field]]"
-                />
-              </div>
-              <div v-else>
-                <span v-if="item.inputType == 'switch' && !item.colorStyle">
-                  <el-switch
-                    v-model.trim="scope.row[item.field]"
-                    :active-value="1"
-                    :inactive-value="0"
-                    active-color="#5887fb"
-                    inactive-color="#ccc"
-                    @change="switchChange(scope.row, item.switchOption)"
-                  />
-                </span>
-                <!-- 带单位 -->
-                <span v-else-if="item.inputType == 'anji-input'">{{
-                  fieldValueByAnjiInput(scope.row[item.field], item)
-                }}</span>
-                <!--表格 a 合并 b上-->
-                <span v-else-if="item.mergeColumn"
-                  >{{ scope.row[item.field] }}({{
-                    scope.row[item.mergeColumn]
-                  }})</span
-                >
-                <!-- 没有单位 -->
-                <span
-                  v-else-if="item.colorStyle"
-                  :class="item.colorStyle[scope.row[item.editField]]"
-                  >{{ fieldValueByRowRenderer(scope.row, item) }}</span
+            <!--多选-->
+            <el-table-column fixed type="selection" width="50" align="center" />
+            <!--隐藏列-->
+            <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
+              <template slot-scope="scope">
+                <p
+                  v-for="item in tableExpandColumns"
+                  :key="item.field"
+                  class="table-expand-item"
                 >
-                <span v-else>{{
-                  fieldValueByRowRenderer(scope.row, item)
-                }}</span>
-                <!-- 正常展示模式
-                <div v-if="!item.custom">
-                  是第一列数据 && 需要高亮字段不为false 高亮并且可以点击
-                  <span v-if="!index && item.operate !== false" class="view" @click="handleOpenEditView('view', scope.row)">{{ scope.row[item.field] }}</span>
-                  <span v-else>{{ scope.row[item.field] }}</span>
-                </div>
-                -->
-                <!-- 自定义展示数据
-                <div v-else v-html="item.renderer(scope.row)" />-->
-              </div>
+                  <span class="titel"> {{ item.label }}: </span>
+                  <span>{{ scope.row[item.field] }}</span>
+                </p>
+              </template>
+            </el-table-column>
+            <!--序号-->
+            <el-table-column label="序号" min-width="50" align="center">
+              <template slot-scope="scope">
+                {{
+                  queryParams.pageSize * (queryParams.pageNumber - 1) +
+                    scope.$index +
+                    1
+                }}
+              </template>
+            </el-table-column>
+
+            <template v-for="(item, index) in option.columns">
+              <el-table-column
+                v-if="item.tableHide != true && item.columnType != 'expand'"
+                :key="index"
+                :prop="item.field"
+                :label="fieldLabel(item)"
+                :min-width="item.minWidth || 110"
+                :sortable="item.sortable"
+                :show-overflow-tooltip="true"
+                align="center"
+              >
+                <template slot-scope="scope">
+                  <div v-if="item.columnType == 'imgPreview'">
+                    <!-- 图片缩略图-->
+                    <el-image
+                      style="width: 25%; height: 50%"
+                      fit="contain"
+                      :src="scope.row[item.field]"
+                      :preview-src-list="[scope.row[item.field]]"
+                    />
+                  </div>
+                  <div v-else>
+                    <span v-if="item.inputType == 'switch' && !item.colorStyle">
+                      <el-switch
+                        v-model.trim="scope.row[item.field]"
+                        :active-value="1"
+                        :inactive-value="0"
+                        active-color="#5887fb"
+                        inactive-color="#ccc"
+                        @change="switchChange(scope.row, item.switchOption)"
+                      />
+                    </span>
+                    <!-- 带单位 -->
+                    <span v-else-if="item.inputType == 'anji-input'">{{
+                      fieldValueByAnjiInput(scope.row[item.field], item)
+                    }}</span>
+                    <!--表格 a 合并 b上-->
+                    <span v-else-if="item.mergeColumn"
+                      >{{ scope.row[item.field] }}({{
+                        scope.row[item.mergeColumn]
+                      }})</span
+                    >
+                    <!-- 没有单位 -->
+                    <span
+                      v-else-if="item.colorStyle"
+                      :class="item.colorStyle[scope.row[item.editField]]"
+                      >{{ fieldValueByRowRenderer(scope.row, item) }}</span
+                    >
+                    <span v-else>{{
+                      fieldValueByRowRenderer(scope.row, item)
+                    }}</span>
+                  </div>
+                </template>
+              </el-table-column>
             </template>
-          </el-table-column>
-        </template>
-        <!--操作栏-->
-        <el-table-column
-          align="center"
-          fixed="right"
-          label="操作"
-          :width="
-            option.buttons.customButton &&
-            option.buttons.customButton.operationWidth
-              ? option.buttons.customButton.operationWidth
-              : 100
-          "
-        >
-          <template slot-scope="scope">
-            <slot name="edit" :msg="scope.row" />
-            <el-button
-              v-if="
-                (option.buttons.query.isShow == undefined
-                  ? true
-                  : option.buttons.query.isShow) &&
-                  hasPermission(option.buttons.edit.permission) == false
-              "
-              type="text"
-              size="small"
-              @click="handleOpenEditView('view', scope.row)"
-              v-permission="option.buttons.query.permission"
-              >查看</el-button
-            >
-            <el-button
-              v-if="
-                option.buttons.edit.isShow == undefined
-                  ? true
-                  : option.buttons.edit.isShow
-              "
-              type="text"
-              size="small"
-              @click="handleOpenEditView('edit', scope.row)"
-              v-permission="option.buttons.edit.permission"
-              >编辑</el-button
-            >
-            <el-button
-              v-if="
-                hasRowCustomButton == false &&
-                option.buttons.delete.isShow == undefined
-                  ? true
-                  : option.buttons.edit.isShow
+            <!--操作栏-->
+            <el-table-column
+              align="center"
+              fixed="right"
+              label="操作"
+              :width="
+                option.buttons.customButton &&
+                option.buttons.customButton.operationWidth
+                  ? option.buttons.customButton.operationWidth
+                  : 100
               "
-              type="text"
-              size="small"
-              @click="handleDeleteBatch(scope.row)"
-              v-permission="option.buttons.delete.permission"
-              >删除</el-button
             >
-            <el-dropdown v-if="hasRowCustomButton" trigger="click">
-              <span class="el-dropdown-link">
-                更多<i class="el-icon-caret-bottom el-icon--right" />
-              </span>
-              <el-dropdown-menu slot="dropdown">
-                <el-dropdown-item class="clearfix">
-                  <slot name="rowButton" :msg="scope.row" />
-                  <el-button
-                    v-if="
-                      option.buttons.delete.isShow == undefined
-                        ? true
-                        : option.buttons.edit.isShow
-                    "
-                    type="text"
-                    size="small"
-                    @click="handleDeleteBatch(scope.row)"
-                    v-permission="option.buttons.delete.permission"
-                    >删除</el-button
-                  >
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </el-dropdown>
-          </template>
-        </el-table-column>
-      </el-table>
-      <div class="pagination">
-        <el-pagination
-          v-show="total > 0"
-          background
-          :current-page.sync="queryParams.pageNumber"
-          :page-sizes="$pageSizeAll"
-          :page-size="queryParams.pageSize"
-          layout="total, prev, pager, next, jumper, sizes"
-          :total="total"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-        />
-        <div>
-          <slot name="tableSelectionBtn" :selection="checkRecords" />
+              <template slot-scope="scope">
+                <slot name="rowButton" :msg="scope.row" />
+                <el-button
+                  v-if="
+                    option.buttons.edit.isShow == undefined
+                      ? true
+                      : option.buttons.edit.isShow
+                  "
+                  type="text"
+                  size="small"
+                  @click="handleOpenEditView('edit', scope.row)"
+                  >编辑</el-button
+                >
+                <el-button
+                  v-if="
+                    hasCustomButtonInRowMore == false &&
+                    option.buttons.delete.isShow == undefined
+                      ? true
+                      : option.buttons.edit.isShow
+                  "
+                  type="text"
+                  size="small"
+                  @click="handleDeleteBatch(scope.row)"
+                  >删除</el-button
+                >
+                <el-dropdown v-if="hasCustomButtonInRowMore" trigger="click">
+                  <span class="el-dropdown-link"
+                    >更多<i class="el-icon-caret-bottom el-icon--right" />
+                  </span>
+                  <el-dropdown-menu slot="dropdown">
+                    <el-dropdown-item class="clearfix">
+                      <slot name="rowButtonInMore" :msg="scope.row" />
+                      <el-button
+                        v-if="
+                          option.buttons.delete.isShow == undefined
+                            ? true
+                            : option.buttons.edit.isShow
+                        "
+                        type="text"
+                        size="small"
+                        @click="handleDeleteBatch(scope.row)"
+                        >删除</el-button
+                      >
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </el-dropdown>
+              </template>
+            </el-table-column>
+          </el-table>
+          <div class="page_bottom">
+            <div class="pagination">
+              <div>
+                <slot name="tableSelectionBtn" :selection="checkRecords" />
+              </div>
+              <el-pagination
+                v-show="total > 0"
+                background
+                :current-page.sync="queryParams.pageNumber"
+                :page-sizes="$pageSizeAll"
+                :page-size="queryParams.pageSize"
+                layout="total, prev, pager, next, jumper, sizes"
+                :total="total"
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+              />
+            </div>
+          </div>
         </div>
       </div>
-
       <!-- 表格结束 -->
-
       <EditDialog
         ref="edit"
+        :submit-detail-data="option.submitDetailData"
+        :handle-detail-data="option.handleDetailData"
         :option="option"
         :model-type="editDialogModelType"
         :visible="editDialogOpen"
@@ -351,16 +378,37 @@
       </EditDialog>
     </div>
     <slot name="pageSection" />
+    <!-- 右键菜单 -->
+    <anji-contextMenu
+      :visible.sync="isShowRowContextMenu"
+      :style-obj="contextMenuConfigStyle"
+    >
+      <div
+        v-for="(item, index) in option.contextMenu"
+        :key="index"
+        class="contextMenu"
+      >
+        <el-button
+          type="text"
+          :disabled="item.disabled"
+          class="contextMenuItem"
+          @click="handleContextMenuItem(item)"
+          >{{ item.label }}</el-button
+        >
+      </div>
+    </anji-contextMenu>
   </div>
 </template>
 <script>
 import AnjiTree from "@/components/AnjiPlus/anji-tree.vue";
 import EditDialog from "./edit";
 import request from "@/utils/request";
+import anjiContextMenu from "@/components/AnjiPlus/anji-contextMenu";
 export default {
   components: {
     EditDialog,
-    AnjiTree
+    AnjiTree,
+    anjiContextMenu
   },
   props: {
     option: {
@@ -368,6 +416,11 @@ export default {
       type: Object,
       default: () => {
         return {
+          formHeight: this.$refs.formSearch.$el.offsetHeight,
+          // 详情页中输入框左边文字宽度
+          labelWidth: "120px",
+          // 查询区隐藏
+          queryFormHide: false,
           // 查询表单条件
           queryFormFields: [],
           // 按钮
@@ -404,13 +457,24 @@ export default {
       editDialogRowData: {}, // 编辑时的主键
       editDialogModelType: "view", // 编辑 查看
 
-      hasRowCustomButton: false // 除了编辑删除外,还有自定义的行按钮
+      hasCustomButtonInRowMore: false, // 除了编辑删除外,还有自定义的行按钮
+
+      isShowRowContextMenu: false,
+      contextMenuConfigStyle: {},
+      contextMenuRow: {}
     };
   },
+
   computed: {
+    // 查询区隐藏
+    queryFormTreeHide() {
+      const flag =
+        this.option.queryFormHide != null && this.option.queryFormHide == true;
+      return flag;
+    },
     // 左侧树形查询条件
     queryFormTreeField() {
-      var treeField = this.option.queryFormFields.find(
+      const treeField = this.option.queryFormFields.find(
         item => item["inputType"] == "anji-tree"
       );
       return treeField;
@@ -421,29 +485,26 @@ export default {
     },
     // 不包含树形控件的查询条件
     queryFormFieldExcludeTree() {
-      var treeFields = this.option.queryFormFields.filter(
+      let treeFields = this.option.queryFormFields.filter(
         item => item["inputType"] != "anji-tree"
       );
       return treeFields;
     },
     // 主键的列名
     primaryKeyFieldName() {
-      var primaryKey = this.option.columns.find(
+      let primaryKey = this.option.columns.find(
         item => item["primaryKey"] == true
       );
       if (primaryKey != null) {
         return primaryKey["field"];
       } else {
         return null;
-        console.warn(
-          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
-        );
       }
     },
 
     // 表格中可展开的列
     tableExpandColumns() {
-      var expandColumns = this.option.columns.filter(
+      let expandColumns = this.option.columns.filter(
         item => item["columnType"] == "expand"
       );
       return expandColumns;
@@ -461,36 +522,26 @@ export default {
       this.$set(this.queryParams, item.field, item.defaultValue || null);
     });
     // 查询列表
-    this.handleQueryForm("query");
+    if (this.option.skipQuery || this.option.skipQuery == undefined) {
+      this.handleQueryForm("query");
+    }
     this.queryFormChange();
   },
   mounted() {
-    if (this.$scopedSlots["rowButton"] != null) {
-      this.hasRowCustomButton = true;
+    if (this.$scopedSlots["rowButtonInMore"] != null) {
+      this.hasCustomButtonInRowMore = true;
     } else {
-      this.hasRowCustomButton = false;
+      this.hasCustomButtonInRowMore = false;
     }
+    console.log(`是否有自定义行按钮: ${this.hasCustomButtonInRowMore}`);
   },
   methods: {
     queryFormFieldSpan(item) {
-      // console.log(item)
-
       if (item.span != null) {
         return item.span;
       } else {
         return 6;
       }
-      // let rowLength = this.option.queryFormFields.length;
-      // console.log(rowLength, "ss")
-      // console.log(rowLength % 3)
-      // if (rowLength <= 3) {
-      //   return 6
-      // }
-      // else if (rowLength % 3 == 0) {
-      //   return 8
-      // } else if (rowLength > 6) {
-      //   return 8
-      // }
     },
     // 切换更多搜索条件
     handleToggleMoreSearch() {
@@ -503,8 +554,8 @@ export default {
         console.warn("排序字段名prop为空,无法排序");
         return;
       }
-      var sort = column.prop; // 列表查询默认排序列
-      var order = column.order == "ascending" ? "ASC" : "DESC";
+      let sort = column.prop; // 列表查询默认排序列
+      let order = column.order == "ascending" ? "ASC" : "DESC";
       this.queryParams["sort"] = sort;
       this.queryParams["order"] = order;
       this.handleQueryForm("query");
@@ -520,7 +571,7 @@ export default {
       // 如果是点树查询,把查询区里的属性去掉
       if (from == "tree") {
         if (this.hasTreeFieldInQueryForm) {
-          var treeVal = this.queryParams[this.queryFormTreeField.field];
+          let treeVal = this.queryParams[this.queryFormTreeField.field];
           this.queryParams = {
             pageNumber: 1,
             pageSize: 10
@@ -537,12 +588,25 @@ export default {
         this.queryParams["order"] = this.option.buttons.query.order;
       }
       this.queryParams.pageNumber = 1;
+
+      // 添加一些其他的查询参数
+      if (this.option.buttons.query.params) {
+        const params = this.option.buttons.query.params;
+        for (const key in params) {
+          this.queryParams[key] = params[key];
+        }
+      }
+
       this.handleQueryPageList();
     },
+    // 外部追加参数进来
+    handleAddQueryParams(k, v) {
+      this.queryParams[k] = v;
+    },
     // 列表查询
     async handleQueryPageList() {
-      var params = this.queryParams;
-      // 将特殊参数值urlcode处理 var params = this.urlEncodeObject(this.queryParams, 'order,sort')
+      // 将特殊参数值urlcode处理
+      let params = this.urlEncodeObject(this.queryParams, "order,sort");
       const { data, code } = await this.option.buttons.query.api(params);
       if (code != "200") return;
       this.records = data.records;
@@ -550,21 +614,30 @@ export default {
     },
     // 重置
     handleResetForm() {
+      const showMoreSearch = this.queryParams.showMoreSearch;
       this.queryParams = {
+        order: "",
+        sort: "",
         pageNumber: 1,
-        pageSize: 10
+        pageSize: 10,
+        showMoreSearch
       };
-      // this.$refs['queryForm'].resetFields()
-      // this.records = []
-      // this.total = 0
+
+      // 查询条件表单只读模式下不重置默认值
+      const queryFormFieldsOption = this.option.queryFormFields;
+      queryFormFieldsOption.forEach(el => {
+        if (el.disabled) {
+          this.queryParams[el.field] = el.defaultValue;
+        }
+      });
     },
     // 树形查询条件点击回调
     handleTreeNodeCheck() {
       this.handleQueryForm("tree");
       // 为新建页面的对应属性值,绑定上对应的默认值
-      var treeFieldName = this.queryFormTreeField["field"];
-      for (var i = 0; i < this.option.columns.length; i++) {
-        var item = this.option.columns[i];
+      let treeFieldName = this.queryFormTreeField["field"];
+      for (let i = 0; i < this.option.columns.length; i++) {
+        let item = this.option.columns[i];
         if (
           item["editField"] == treeFieldName ||
           item["field"] == treeFieldName
@@ -606,13 +679,14 @@ export default {
       ) {
         this.$refs.queryFormTree.queryData();
       }
+      this.$emit("handleCustomClose");
       this.handleQueryPageList();
       // 关闭时 清空表单的验证规则
       this.$refs.edit.$refs.mainForm.$refs.editForm.resetFields();
     },
     // 批量删除
     handleDeleteBatch(row) {
-      var ids = [];
+      let ids = [];
       if (row != null) {
         ids.push(row[this.primaryKeyFieldName]); // 删除指定的行
       } else {
@@ -645,8 +719,59 @@ export default {
 
     // 选择项改变时
     handleSelectionChange(val) {
+      if (val.length > 0) {
+        val.forEach(el => {
+          this.records.forEach(ev => {
+            if (el.id == ev.id) {
+              ev.dblClickFlag = true;
+              ev.bgColor = true;
+            }
+          });
+        });
+      } else {
+        this.records.forEach(ev => {
+          ev.dblClickFlag = false;
+          ev.bgColor = false;
+        });
+      }
+      this.isShowRowContextMenu = false;
       this.checkRecords = val;
     },
+    // 双击
+    handleSelectionDblclick(row) {
+      row.dblClickFlag = !row.dblClickFlag;
+      row.bgColor = row.dblClickFlag;
+      this.$refs.tables.toggleRowSelection(row, row.dblClickFlag);
+    },
+    // 右键
+    rowContextMenu(row, column, event) {
+      event.preventDefault();
+      this.option.contextMenu.forEach(el => {
+        if (el.flag == "all") {
+          el.disabled = !(this.checkRecords.length > 1);
+        } else {
+          el.disabled = this.checkRecords.length > 1;
+        }
+      });
+
+      // 设置右键位置
+      this.contextMenuConfigStyle = {
+        left: event.clientX + "px",
+        top: event.clientY + "px",
+        display: "block"
+      };
+      // 获取当前右键行数据
+      this.contextMenuRow = row;
+      // 获取我们自定义的右键菜单
+      this.isShowRowContextMenu = true;
+    },
+    handleContextMenuItem(item) {
+      if (item.flag == "all") {
+        item.click({ selection: this.checkRecords });
+      } else {
+        item.click({ msg: this.contextMenuRow });
+      }
+    },
     // 页码改变
     handleCurrentChange(pageNumber) {
       this.queryParams.pageNumber = pageNumber;
@@ -674,6 +799,19 @@ export default {
     },
     // 带单位的列,需要转换
     fieldLabel(columnConfig) {
+      let objUnitText = "";
+      const localStorageUnit = this.getSettingByName("unit_conversion");
+      const objUnit = {};
+      for (const key in localStorageUnit) {
+        columnConfig.anjiInput &&
+          key
+            .toLowerCase()
+            .indexOf(columnConfig.anjiInput.unit.toLowerCase()) != -1 &&
+          (objUnit[key] = localStorageUnit[key]);
+      }
+      for (const key in objUnit) {
+        key.toLowerCase().indexOf("text") != -1 && (objUnitText = objUnit[key]);
+      }
       if (columnConfig == null) {
         return "";
       }
@@ -681,13 +819,38 @@ export default {
         columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
       ) {
-        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`;
+        return columnConfig.anjiInput["defaultUnit"] == undefined
+          ? `${columnConfig.label}(${objUnitText})`
+          : `${columnConfig.label}(${columnConfig.anjiInput["defaultUnit"]})`;
       } else {
         return columnConfig.label;
       }
     },
     // 带单位的输入框
     fieldValueByAnjiInput(value, columnConfig) {
+      const localStorageUnit = this.getSettingByName("unit_conversion");
+      let objUnitConversion = "";
+      let objUnitKeepPoint = "";
+      let objUnitConversionRadioGroup = [];
+      const objUnit = {};
+      for (const key in localStorageUnit) {
+        columnConfig.anjiInput &&
+          key
+            .toLowerCase()
+            .indexOf(columnConfig.anjiInput.unit.toLowerCase()) != -1 &&
+          (objUnit[key] = localStorageUnit[key]);
+      }
+      for (const key in objUnit) {
+        key.toLowerCase().indexOf("conversion") != -1 &&
+          key.toLowerCase().indexOf("conversiontext") == -1 &&
+          key.toLowerCase().indexOf("conversionradiogroup") == -1 &&
+          (objUnitConversion = objUnit[key]);
+        key.toLowerCase().indexOf("keeppoint") != -1 &&
+          (objUnitKeepPoint = objUnit[key]);
+        Array.isArray(objUnit[key]) &&
+          (objUnitConversionRadioGroup = objUnit[key]);
+      }
+
       if (columnConfig == null) {
         return value;
       }
@@ -695,7 +858,15 @@ export default {
         columnConfig.inputType == "anji-input" &&
         columnConfig.anjiInput != null
       ) {
-        return value / columnConfig.anjiInput.conversion;
+        if (columnConfig.anjiInput["defaultUnit"] == undefined) {
+          // 不存在
+          return (value / objUnitConversion).toFixed(objUnitKeepPoint || 2);
+        } else {
+          const coversion = objUnitConversionRadioGroup.find(
+            item => item.label == columnConfig.anjiInput["defaultUnit"]
+          )["value"];
+          return (value / coversion).toFixed(objUnitKeepPoint || 2);
+        }
       } else {
         return value;
       }
@@ -732,12 +903,78 @@ export default {
       if (typeof this.option.queryFormChange == "function") {
         this.option.queryFormChange(this.queryParams, fileName, fieldVal);
       }
+    },
+    // 给表格某行改变颜色
+    tableRowClassName({ row, rowIndex }) {
+      if ((row.urgentFlag || row.tableRow) && !row.bgColor) {
+        return "warning-row";
+      } else if ((row.urgentFlag || row.tableRow) && row.bgColor) {
+        return "warning-row bgColor";
+      } else if ((!row.urgentFlag || !row.tableRow) && row.bgColor) {
+        return "bgColor";
+      }
+      if ((rowIndex + 1) % 2 === 0) {
+        return "success-row";
+      }
     }
   }
 };
 </script>
 
 <style scoped lang="scss">
+.right-container {
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  .main_part {
+    display: flex;
+    flex-direction: column;
+    flex: 1;
+    background: #fff;
+    border-radius: 4px;
+    padding: 12px 20px 12px;
+    margin-top: 4px;
+    height: 100%;
+    position: relative;
+  }
+}
+/deep/.button {
+  box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.08);
+  border-radius: 6px;
+}
+/deep/.el-table th > .cell {
+  text-align: center;
+}
+/deep/.el-table .cell {
+  text-align: center;
+}
+/deep/.elTable {
+  margin: 10px 0 50px;
+}
+.el-table {
+  // 看这里!!!
+  // 深度选择器,去除默认的padding
+  /deep/ th {
+    padding: 0;
+    height: 36px;
+  }
+  /deep/ td {
+    padding: 0;
+    height: 36px;
+  }
+}
+/deep/.el-table--striped .el-table__body tr.el-table__row--striped td {
+  background: #f9fbfd;
+}
+.screenFlex {
+  display: flex;
+}
+.searchFormFlex {
+  flex: 1;
+}
+.screenButtons {
+  padding-left: 20px;
+}
 .style-btn {
   pointer-events: none;
 }
@@ -788,21 +1025,87 @@ export default {
   &:hover {
     background: none !important;
   }
+  /deep/.el-button--text:hover,
+  .el-button--text:focus {
+    color: #799ffc !important;
+    background-color: #ecf5ff !important;
+    border-color: transparent;
+  }
   .el-button--mini {
     float: right;
   }
+  .el-button--text:hover,
+  .el-button--text:focus {
+    color: #799ffc;
+    border-color: transparent;
+    // background-color: transparent;
+    // background: red !important;
+  }
   .el-button + .el-button {
     margin-left: 0 !important;
     float: right;
   }
 }
+/deep/.el-pagination.is-background .btn-prev,
+.el-pagination.is-background .btn-next,
+.el-pagination.is-background .el-pager li {
+  margin: 0 5px;
+  border-radius: 2px;
+  background-color: #f4f4f5;
+  color: #606266;
+  min-width: 30px;
+  border-radius: 2px;
+}
+/deep/.el-pagination.is-background .el-pager li:not(.disabled).active {
+  background-color: #5887fb;
+  color: #ffffff;
+  border-radius: 4px;
+}
+.page_bottom {
+  width: 100%;
+  position: absolute;
+  bottom: 0;
+}
 .pagination {
   display: flex;
   flex-direction: row;
   justify-content: space-between;
-  margin-top: 20px;
+  margin-bottom: 14px;
+  margin-right: 20px;
+}
+.el-table /deep/ .warning-row {
+  color: red !important;
+}
+.el-table /deep/ .bgColor {
+  background: #d9ecff;
+}
+/deep/.edit_button {
+  width: 22px;
+  height: 22px;
+  padding: 0;
+  margin: 0;
+  margin-right: 5px;
+  /deep/ i {
+    font-size: 30px;
+  }
+}
+.el-form-item--mini.el-form-item {
+  margin-bottom: 12px;
 }
-.anji_curd_table {
-  margin-top: 20px;
+.el-table /deep/ .success-row {
+  background: #f9fbfd !important;
+}
+
+.contextMenu {
+  width: 100px;
+  .contextMenuItem {
+    width: 100%;
+    font-size: 12px;
+    line-height: 20px;
+    padding: 5px;
+  }
+  &:hover {
+    background: #f9fbfd;
+  }
 }
 </style>
diff --git a/report-ui/src/components/AnjiPlus/anji-crud/edit-form.vue b/report-ui/src/components/AnjiPlus/anji-crud/edit-form.vue
index d186a84e..89ab2559 100644
--- a/report-ui/src/components/AnjiPlus/anji-crud/edit-form.vue
+++ b/report-ui/src/components/AnjiPlus/anji-crud/edit-form.vue
@@ -1,11 +1,8 @@
-<!--
- * @Author: lide1202@hotmail.com
- * @Date: 2021-5-4 11:04:24
- * @Last Modified by:   lide1202@hotmail.com
- * @Last Modified time: 2021-5-6 11:04:24
- !-->
 <template>
-  <div v-if="option['hide'] == null || option['hide'] == false" class="anji-card">
+  <div
+    v-if="option['hide'] == null || option['hide'] == false"
+    class="anji-card"
+  >
     <div class="card-head">
       {{ option.title }}
       <div class="main-card-header-button">
@@ -15,30 +12,97 @@
       </div>
     </div>
     <div class="card-body">
-      <el-form ref="editForm" :model="editForm" :label-width="option.labelWidth || '100px'">
+      <el-form
+        ref="editForm"
+        :model="editForm"
+        :label-width="option.labelWidth || '100px'"
+      >
         <!--:disabled="modelType == 'view'"-->
         <template v-for="group in columnGroups">
-          <el-divider v-if="groupModel" :key="group" content-position="left">{{ group }}</el-divider>
+          <el-divider v-if="groupModel" :key="group" content-position="left">{{
+            group
+          }}</el-divider>
           <el-row :key="group" class="form_table">
             <template v-for="item in groupFormFields[group]">
-              <el-col v-if="fieldIsHide(item.editHide) == false" :key="item.editField" :span="item.rowColSpan == null ? cardRowColSpan : item.rowColSpan">
-                <el-form-item :label="item.label" :rules="item.rules" :prop="item.editField" :disabled="item.disabled">
+              <el-col
+                v-if="fieldIsHide(item.editHide) == false"
+                :key="item.editField"
+                :span="
+                  item.rowColSpan == null ? cardRowColSpan : item.rowColSpan
+                "
+              >
+                <el-form-item
+                  :label="item.label"
+                  :rules="item.rules"
+                  :prop="item.editField"
+                  :disabled="item.disabled"
+                >
                   <!-- 输入框 -->
-                  <span v-if="item.tips != '' && item.tips != null" :style="{ 'margin-left': '-13px' }" class="input_tips">
-                    <el-tooltip class="item" effect="dark" :content="item.tips" placement="top-start">
+                  <span
+                    v-if="item.tips != '' && item.tips != null"
+                    :style="{ 'margin-left': '-13px' }"
+                    class="input_tips"
+                  >
+                    <el-tooltip
+                      class="item"
+                      effect="dark"
+                      :content="item.tips"
+                      placement="top-start"
+                    >
                       <svg-icon icon-class="tishi-yiwen" />
                     </el-tooltip>
                   </span>
-                  <el-input v-if="item.inputType == 'input'" v-model.trim="editForm[item.editField]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <el-input
+                    v-if="item.inputType == 'input'"
+                    v-model.trim="editForm[item.editField]"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- 开关 -->
-                  <el-switch v-else-if="item.inputType == 'switch'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :active-value="1" :inactive-value="0" inactive-color="#ccc" active-color="#5887fb" @change="(value) => formChange(item.editField, value, null)" />
-                  <el-input-number v-else-if="item.inputType == 'input-number'" v-model.trim="editForm[item.editField]" :min="item.inputNumberOption.min" :max="item.inputNumberOption.max" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <el-switch
+                    v-else-if="item.inputType == 'switch'"
+                    v-model.trim="editForm[item.editField]"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    :active-value="1"
+                    :inactive-value="0"
+                    inactive-color="#ccc"
+                    active-color="#5887fb"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
+                  <el-input-number
+                    v-else-if="item.inputType == 'input-number'"
+                    v-model.trim="editForm[item.editField]"
+                    :min="item.inputNumberOption.min"
+                    :max="item.inputNumberOption.max"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- 自定义input -->
-                  <anji-input v-else-if="item.inputType == 'anji-input'" v-model.trim="editForm[item.editField]" :unit="item.anjiInput.unit" :conversion="item.anjiInput.conversion" :keep-point="item.anjiInput.keepPoint" :rounding="item.anjiInput.rounding" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <anji-input
+                    v-else-if="item.inputType == 'anji-input'"
+                    v-model.trim="editForm[item.editField]"
+                    :unit="item.anjiInput.unit"
+                    :default-unit="item.anjiInput.defaultUnit"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- 下拉框 -->
                   <anji-select
                     v-else-if="item.inputType == 'anji-select'"
                     v-model.trim="editForm[item.editField]"
+                    :allow-create="item.anjiSelectOption.allowCreate"
                     :multiple="item.anjiSelectOption.multiple"
                     :disabled="fieldIsDisable(item.disabled)"
                     :dict-code="item.anjiSelectOption.dictCode"
@@ -51,21 +115,97 @@
                     :label="item.anjiSelectOption.label"
                     :remote-filter="item.anjiSelectOption.remoteFilter"
                     :disabled-options="item.anjiSelectOption.disabledOptions"
-                    @change="(value, option) => formChange(item.editField, value, option)"
+                    :select-keyword="item.anjiSelectOption.selectKeyword"
+                    @change="
+                      (value, option) =>
+                        formChange(item.editField, value, option)
+                    "
                   />
                   <!-- 日期时间框  -->
-                  <el-date-picker v-else-if="item.inputType.indexOf('date') >= 0" v-model="editForm[item.editField]" style="width: 100%" :placeholder="item.placeholder || '请选择'" :type="item.inputType" :format="item.format" :value-format="item.valueFormat" :disabled="fieldIsDisable(item.disabled)" :clearable="item.clearable !== false" @change="(value) => formChange(item.editField, value, null)" />
+                  <el-date-picker
+                    v-else-if="item.inputType.indexOf('date') >= 0"
+                    v-model="editForm[item.editField]"
+                    style="width: 100%"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_select')
+                    "
+                    :type="item.inputType"
+                    :format="item.format"
+                    :value-format="item.valueFormat"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    :clearable="item.clearable !== false"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- checkbox -->
-                  <anji-checkbox v-else-if="item.inputType == 'checkbox'" v-model.trim="editForm[item.editField]" :dict-code="item.anjiCheckbox.dictCode" :label="item.anjiCheckbox.label" :disabled="fieldIsDisable(item.disabled)" @change="(value, options) => formChange(item.editField, value, options)" />
+                  <anji-checkbox
+                    v-else-if="item.inputType == 'checkbox'"
+                    v-model.trim="editForm[item.editField]"
+                    :dict-code="item.anjiCheckbox.dictCode"
+                    :url="item.anjiCheckbox.url"
+                    :label="item.anjiCheckbox.label"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    @change="
+                      (value, options) =>
+                        formChange(item.editField, value, options)
+                    "
+                  />
                   <!-- 城市三级联动 -->
-                  <anji-cascader v-else-if="item.inputType == 'anji-cascader'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :url="item.anjiCascader.url" @change="(value) => formChange(item.editField, value, null)" />
+                  <anji-cascader
+                    v-else-if="item.inputType == 'anji-cascader'"
+                    v-model.trim="editForm[item.editField]"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    :single-display="item.anjiCascader.singleDisplay"
+                    :url="item.anjiCascader.url"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- 上传组件 -->
-                  <anji-upload v-else-if="item.inputType == 'anji-upload'" v-model.trim="editForm[item.editField]" :up-load-url="item.anjiUpload.upLoadUrl" :view-url="item.anjiUpload.viewUrl" :upload-type="item.anjiUpload.uploadType" :limit="item.anjiUpload.limit" @change="(value) => formChange(item.editField, value, null)" />
+                  <anji-upload
+                    v-else-if="item.inputType == 'anji-upload'"
+                    v-model.trim="editForm[item.editField]"
+                    :up-load-url="item.anjiUpload.upLoadUrl"
+                    :view-url="item.anjiUpload.viewUrl"
+                    :upload-type="item.anjiUpload.uploadType"
+                    :limit="item.anjiUpload.limit"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
                   <!-- input自带输入建议 -->
-                  <anji-autocomplete v-else-if="item.inputType == 'anji-autocomplete'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :label="item.anjiAutocomplete.label" :option="item.anjiAutocomplete.option" :appoint-value="item.anjiAutocomplete.appointValue" :url="item.anjiAutocomplete.url" @change="(value, option) => formChange(item.editField, value, option)" />
+                  <anji-autocomplete
+                    v-else-if="item.inputType == 'anji-autocomplete'"
+                    v-model.trim="editForm[item.editField]"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    :label="item.anjiAutocomplete.label"
+                    :option="item.anjiAutocomplete.option"
+                    :appoint-value="item.anjiAutocomplete.appointValue"
+                    :url="item.anjiAutocomplete.url"
+                    @change="
+                      (value, option) =>
+                        formChange(item.editField, value, option)
+                    "
+                  />
                   <!-- textarea -->
-                  <el-input v-else-if="item.inputType == 'textarea'" v-model.trim="editForm[item.editField]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" type="textarea" :rows="2" @change="(value) => formChange(item.editField, value, null)" />
-                  <el-input v-else placeholder="组件不支持此类型表单请至组件内部自行扩展" disabled />
+                  <el-input
+                    v-else-if="item.inputType == 'textarea'"
+                    v-model.trim="editForm[item.editField]"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    type="textarea"
+                    :rows="2"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
+                  <!-- 城市四级联动 -->
+                  <anji-fourlevel
+                    v-else-if="item.inputType == 'anji-country'"
+                    v-model.trim="editForm[item.editField]"
+                    @change="value => formChange(item.editField, value, null)"
+                  />
+                  <el-input
+                    v-else
+                    placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                    disabled
+                  />
                 </el-form-item>
               </el-col>
             </template>
@@ -87,97 +227,103 @@ export default {
       type: [Object],
       default: () => {
         return {
-          title: '', // 页面标题
-          labelWidth: '', // 表单输入框label宽度
+          title: "", // 页面标题
+          labelWidth: "", // 表单输入框label宽度
           queryFormFields: [], // 查询表单条件
           buttons: {
             // 按钮
             query: {},
             edit: {},
             delete: {},
-            add: {},
+            add: {}
           },
           columns: [], // 表格列
-          formChange: (formData, fieldName, fieldVal, fieldExtend) => {}, // 弹出框表单修改回调
-        }
-      },
+          formChange: (formData, fieldName, fieldVal, fieldExtend) => {} // 弹出框表单修改回调
+        };
+      }
     },
     // 当relateData不为空时,该组件是渲染与relateData一对一关联的子表信息
     relateData: {
       type: [Object],
       default: () => {
-        return {}
-      },
+        return {};
+      }
     },
     value: {
       type: [Object],
       default: () => {
-        return {}
-      },
-    },
+        return {};
+      }
+    }
   },
   data() {
     return {
-      cardRowColNum: 2, // 主信息一行显示几列
-      editForm: {}, // 提交表单的数据
-    }
+      cardRowColNum: this.option.rowColForm || 2, // 主信息一行显示几列
+      editForm: {} // 提交表单的数据
+    };
   },
   computed: {
     // 主键的列名
     primaryKeyFieldName() {
-      var primaryKey = this.option.columns.find((item) => item['primaryKey'] == true)
+      let primaryKey = this.option.columns.find(
+        item => item["primaryKey"] == true
+      );
       if (primaryKey != null) {
-        return primaryKey['field']
+        return primaryKey["field"];
       } else {
-        return null
-        console.warn('在columns中查找primaryKey=true失败,会导致查询详情和删除失败')
+        return null;
+        console.warn(
+          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
+        );
       }
     },
     // 指定当前实体关联主表的
     joinColumn() {
-      var columnName = this.option.joinColumn
+      let columnName = this.option.joinColumn;
       if (this.isBlank(columnName)) {
-        console.warn('在joinEntity中查找joinColumn属性失败,会导致查询详情和删除失败')
-        columnName = ''
+        console.warn(
+          "在joinEntity中查找joinColumn属性失败,会导致查询详情和删除失败"
+        );
+        columnName = "";
       }
-      return columnName
+      return columnName;
     },
     // 提交表单中所有的列,因部分翻译字段,表格中和表单中的key不一样,field editField不全,这里补全下,
     formFields() {
       if (this.option.columns == null) {
-        return []
+        return [];
       }
-      var fields = this.deepClone(this.option.columns)
-      fields = fields.map((item) => {
-        if (this.isBlank(item['editField'])) {
-          item['editField'] = item['field']
+      let fields = this.deepClone(this.option.columns);
+      fields = fields.map(item => {
+        if (this.isBlank(item["editField"])) {
+          item["editField"] = item["field"];
         }
         // 没有设定分组的,全部补全成 其它信息
-        if (this.isBlank(item['group'])) {
-          item['group'] = '其它'
+        if (this.isBlank(item["group"])) {
+          item["group"] = this.$lang("crudEditDialogGroupOther");
         }
-        return item
-      })
-      return fields
+        return item;
+      });
+      return fields;
     },
     // 卡片设定中一行显示几列,每列的col数
     cardRowColSpan() {
-      return 24 / this.cardRowColNum
+      return 24 / this.cardRowColNum;
     },
     // 如果表单内容较多,启用了分组,这里先算出所有的分组
     columnGroups() {
       if (this.isBlank(this.formFields)) {
-        return []
+        return [];
       } else {
         // 找出所有hide != true的关联表
-        var groups = this.formFields
-          .map((item) => {
-            return item['group']
+        let groups = this.formFields
+          .map(item => {
+            return item["group"];
           })
           .filter((currentValue, index, arr) => {
-            return arr.indexOf(currentValue) == index
-          })
-        return groups
+            return arr.indexOf(currentValue) == index;
+          });
+        return groups;
       }
     },
     /* {
@@ -187,167 +333,176 @@ export default {
     groupFormFields() {
       if (this.showDialog) {
         // 将每个分组初始化
-        var groupFormFields = {}
+        let groupFormFields = {};
         this.columnGroups.forEach((value, index, array) => {
-          groupFormFields[value] = []
-        })
+          groupFormFields[value] = [];
+        });
         // 将所有编辑列,按分组存放
         this.formFields.forEach((item, index, array) => {
-          groupFormFields[item['group']].push(item)
-        })
-        return groupFormFields
+          groupFormFields[item["group"]].push(item);
+        });
+        return groupFormFields;
       }
-      return []
+      return [];
     },
     groupModel() {
-      return this.columnGroups.length > 1
-    },
+      return this.columnGroups.length > 1;
+    }
   },
   created() {
     // 如果表单,是做为主表的编辑页面,如果是通过v-model传递进来的值,通过监听value,更新this.editForm
     this.$watch(
-      function () {
-        return this.value
+      function() {
+        return this.value;
       },
-      function (newVal, oldVal) {
-        this.editForm = newVal
+      function(newVal, oldVal) {
+        this.editForm = newVal;
         // 通过v-model传递值进来时,说明当前form是主表信息
-        this.formChange()
+        this.formChange();
       }
-    )
+    );
     // 如果表单,是做为子表的编辑页面,当relateData中的关联字段发生更新时,触发查询,比如在goods详情中,goodsCode
     this.$watch(
-      function () {
-        return this.relateData[this.joinColumn]
+      function() {
+        return this.relateData[this.joinColumn];
       },
-      function (newVal, oldVal) {
+      function(newVal, oldVal) {
         // 如果是父组件(弹出框)关闭时,设置this.relateData = {}时触发,清空本组件的数据
         if (this.isBlank(this.relateData)) {
-          this.cardRowColNum = 2
-          this.editForm = {}
-          return
+          this.cardRowColNum = 2;
+          this.editForm = {};
+          return;
         }
         // 如果是关联字段发生更新,触发查询
         if (this.isNotBlank(newVal)) {
-          this.queryDetail()
+          this.queryDetail();
         }
       }
-    )
+    );
   },
   mounted() {
     // 如果表单,是做为主表的编辑页面,如果是通过v-model传递进来的值
     if (this.isNotBlank(this.value)) {
-      this.editForm = this.value
-      this.formChange()
+      this.editForm = this.value;
+      this.formChange();
     }
     // 如果表单,是做为子表的编辑页面,首次打开时,根据关联属性,加载详情数据
-    if (this.isNotBlank(this.relateData) && this.isNotBlank(this.relateData[this.joinColumn])) {
-      this.queryDetail()
+    if (
+      this.isNotBlank(this.relateData) &&
+      this.isNotBlank(this.relateData[this.joinColumn])
+    ) {
+      this.queryDetail();
     }
   },
   methods: {
     // 该行是否显示 true/false/ 'hideOnAdd hideOnView hideOnEdit'
     fieldIsHide(editHide) {
-      if (typeof editHide == 'boolean') {
-        return editHide
+      if (typeof editHide == "boolean") {
+        return editHide;
       }
-      if (typeof editHide == 'string') {
-        if (this.modelType == 'add') {
-          return editHide.indexOf('hideOnAdd') >= 0
+      if (typeof editHide == "string") {
+        if (this.modelType == "add") {
+          return editHide.indexOf("hideOnAdd") >= 0;
         }
-        if (this.modelType == 'view') {
-          return editHide.indexOf('hideOnView') >= 0
+        if (this.modelType == "view") {
+          return editHide.indexOf("hideOnView") >= 0;
         }
-        if (this.modelType == 'edit') {
-          return editHide.indexOf('hideOnEdit') >= 0
+        if (this.modelType == "edit") {
+          return editHide.indexOf("hideOnEdit") >= 0;
         }
       }
-      return false
+      return false;
     },
     // 该行是否禁用 true/false/ 'disableOnAdd disableOnView disableOnEdit'
     fieldIsDisable(disable) {
-      if (typeof disable == 'boolean') {
-        return disable
+      if (typeof disable == "boolean") {
+        return disable;
       }
-      if (typeof disable == 'string') {
-        if (this.modelType == 'add') {
-          return disable.indexOf('disableOnAdd') >= 0
+      if (typeof disable == "string") {
+        if (this.modelType == "add") {
+          return disable.indexOf("disableOnAdd") >= 0;
         }
-        if (this.modelType == 'view') {
-          return disable.indexOf('disableOnView') >= 0
+        if (this.modelType == "view") {
+          return disable.indexOf("disableOnView") >= 0;
         }
-        if (this.modelType == 'edit') {
-          return disable.indexOf('disableOnEdit') >= 0
+        if (this.modelType == "edit") {
+          return disable.indexOf("disableOnEdit") >= 0;
         }
       }
-      return false
+      return false;
     },
     // 设置一行显示几列
     handleSetRowColNum(num) {
-      this.cardRowColNum = num
-      this.$emit('changeRowColNum', num)
+      this.cardRowColNum = num;
+      this.$emit("changeRowColNum", num);
     },
 
     async queryDetail() {
-      var queryParams = this.relateData
-      const { data, code } = await this.option.buttons.queryByPrimarykey.api(queryParams)
-      if (code != '200') return
-      this.editForm = data
-      this.formChange()
+      let queryParams = this.relateData;
+      const { data, code } = await this.option.buttons.queryByPrimarykey.api(
+        queryParams
+      );
+      if (code != "200") return;
+      this.editForm = data;
+      this.formChange();
     },
     // 校验表单
     validate(callback) {
       this.$refs.editForm.validate(async (valid, obj) => {
         if (callback != null) {
-          callback(valid)
+          callback(valid);
         }
-      })
+      });
     },
     handleSave(callback) {
       this.$refs.editForm.validate(async (valid, obj) => {
         if (valid) {
-          if (this.modelType == 'add') {
+          if (this.modelType == "add") {
             // 当edit-from是作为关联子表的界面,补全关联属性
-            if (typeof this.option.beforeInsert == 'function') {
-              this.option.beforeInsert(this.relateData, this.editForm)
+            if (typeof this.option.beforeInsert == "function") {
+              this.option.beforeInsert(this.relateData, this.editForm);
             }
-            const { code, message } = await this.option.buttons.add.api(this.editForm)
-            if (code == '200') {
+            const { code, message } = await this.option.buttons.add.api(
+              this.editForm
+            );
+            if (code == "200") {
               if (callback != null) {
-                callback()
+                callback();
               }
             } else {
-              console.log(`提交表单调用新增接口失败:${message}`)
+              console.log(`提交表单调用新增接口失败:${message}`);
             }
           } else {
             // 当edit-from是作为关联子表的界面,补全关联属性
-            if (typeof this.option.beforeUpdate == 'function') {
-              this.option.beforeUpdate(this.relateData, this.editForm)
+            if (typeof this.option.beforeUpdate == "function") {
+              this.option.beforeUpdate(this.relateData, this.editForm);
             }
-            const { code, message } = await this.option.buttons.edit.api(this.editForm)
-            if (code == '200') {
+            const { code, message } = await this.option.buttons.edit.api(
+              this.editForm
+            );
+            if (code == "200") {
               if (callback != null) {
-                callback()
+                callback();
               }
             } else {
-              console.log(`提交表单调用更新接口失败:${message}`)
+              console.log(`提交表单调用更新接口失败:${message}`);
             }
           }
         } else {
-          console.log('表单校验失败')
+          console.log("表单校验失败");
         }
-      })
+      });
     },
     // 表单任何一个变动时,通知外部v-model
     formChange(fieldName, fieldVal, fieldExtend) {
-      this.$emit('input', this.editForm)
+      this.$emit("input", this.editForm);
       // 表单变动后,回调option中的formChange事件
-      if (typeof this.option.formChange == 'function') {
-        this.option.formChange(this.editForm, fieldName, fieldVal, fieldExtend)
+      if (typeof this.option.formChange == "function") {
+        this.option.formChange(this.editForm, fieldName, fieldVal, fieldExtend);
       }
-    },
-  },
-}
+    }
+  }
+};
 </script>
 
 <style scoped lang="scss">
diff --git a/report-ui/src/components/AnjiPlus/anji-crud/edit-table.vue b/report-ui/src/components/AnjiPlus/anji-crud/edit-table.vue
index df3b030b..14bc0b48 100644
--- a/report-ui/src/components/AnjiPlus/anji-crud/edit-table.vue
+++ b/report-ui/src/components/AnjiPlus/anji-crud/edit-table.vue
@@ -1,138 +1,207 @@
-<!--
- * @Author: lide1202@hotmail.com
- * @Date: 2021-5-4 11:04:24
- * @Last Modified by:   lide1202@hotmail.com
- * @Last Modified time: 2021-5-6 11:04:24
- !-->
 <template>
-  <div v-if="option['hide'] == null || option['hide'] == false"
-       class="anji-card">
+  <div
+    v-if="option['hide'] == null || option['hide'] == false"
+    class="anji-card"
+  >
     <div class="card-head">{{ option.title }}</div>
     <div class="card-body">
-      <el-form ref="form"
-               :model="form">
+      <el-form ref="form" :model="form">
         <!-- 表格开始 -->
-        <el-table :data="formRecordsUndelete"
-                  border
-                  :row-class-name="tableRowClassAdapter"
-                  @selection-change="handleSelectionChange"
-                  @row-click="handleTableRowClick">
+        <el-table
+          :data="formRecordsUndelete"
+          border
+          :row-class-name="tableRowClassAdapter"
+          @selection-change="handleSelectionChange"
+          @row-click="handleTableRowClick"
+        >
           <!-- <el-table-column fixed type="selection" width="50" align="center" /> -->
 
-          <el-table-column label="序号"
-                           min-width="50"
-                           align="center">
+          <el-table-column
+            :label="$lang('GLOBAL.table_index')"
+            min-width="50"
+            align="center"
+          >
             <template slot-scope="scope">
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
 
           <template v-for="item in option.columns">
-            <el-table-column v-if="fieldIsHide(item.tableHide) != true && item.columnType != 'expand'"
-                             :key="item.field"
-                             :label="item.label"
-                             :min-width="item.minWidth || 110"
-                             align="center">
+            <el-table-column
+              v-if="
+                fieldIsHide(item.tableHide) != true &&
+                  item.columnType != 'expand'
+              "
+              :key="item.field"
+              :label="item.label"
+              :min-width="item.minWidth || 110"
+              align="center"
+            >
               <template slot-scope="scope">
-                <el-form-item :prop="'records.' + scope.$index + '.' + item.field"
-                              :rules="item.rules">
+                <el-form-item
+                  :prop="'records.' + scope.$index + '.' + item.field"
+                  :rules="item.rules"
+                >
                   <!-- 输入框 -->
-                  <el-input v-if="item.inputType == 'input'"
-                            v-model="scope.row[item.field]"
-                            size="small"
-                            :placeholder="item.placeholder || '请输入'"
-                            :clearable="item.clearable !== false"
-                            :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                            @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <el-input
+                    v-if="item.inputType == 'input'"
+                    v-model="scope.row[item.field]"
+                    size="small"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    @change="
+                      value => tableRowChange(scope.$index, item.field, value)
+                    "
+                  />
                   <!-- 开关 -->
-                  <el-switch v-else-if="item.inputType == 'switch'"
-                             v-model="scope.row[item.field]"
-                             :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                             :active-value="item.switchOption.disableValue"
-                             :inactive-value="item.switchOption.enableValue"
-                             @change="(value) => tableRowChange(scope.$index, item.field, value)"
-                             active-color="#5887fb"
-                             inactive-color="#ccc">
+                  <el-switch
+                    v-else-if="item.inputType == 'switch'"
+                    v-model="scope.row[item.field]"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    :active-value="item.switchOption.disableValue"
+                    :inactive-value="item.switchOption.enableValue"
+                    @change="
+                      value => tableRowChange(scope.$index, item.field, value)
+                    "
+                    active-color="#5887fb"
+                    inactive-color="#ccc"
+                  >
                   </el-switch>
-                  <el-input v-else-if="item.inputType == 'input-number'"
-                            v-model="scope.row[item.field]"
-                            size="small"
-                            :min="item.inputNumberOption.min"
-                            :max="item.inputNumberOption.max"
-                            :placeholder="item.placeholder || '请输入'"
-                            :clearable="item.clearable !== false"
-                            :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                            @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <el-input
+                    v-else-if="item.inputType == 'input-number'"
+                    v-model="scope.row[item.field]"
+                    size="small"
+                    :min="item.inputNumberOption.min"
+                    :max="item.inputNumberOption.max"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    @change="
+                      value => tableRowChange(scope.$index, item.field, value)
+                    "
+                  />
 
                   <!-- 自定义input -->
-                  <anji-input v-else-if="item.inputType == 'anji-input'"
-                              v-model.trim="scope.row[item.field]"
-                              :default-value="item.defaultValue"
-                              :unit="item.anjiInput.unit"
-                              :conversion="item.anjiInput.conversion"
-                              :placeholder="item.placeholder || '请输入'"
-                              :clearable="item.clearable !== false"
-                              :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                              @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <anji-input
+                    v-else-if="item.inputType == 'anji-input'"
+                    v-model.trim="scope.row[item.field]"
+                    :default-value="item.defaultValue"
+                    :unit="item.anjiInput.unit"
+                    :conversion="item.anjiInput.conversion"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_input')
+                    "
+                    :clearable="item.clearable !== false"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    @change="
+                      value => tableRowChange(scope.$index, item.field, value)
+                    "
+                  />
 
                   <!-- 下拉框 -->
-                  <anji-select v-else-if="item.inputType == 'anji-select'"
-                               v-model.trim="scope.row[item.field]"
-                               :multiple="item.anjiSelectOption.multiple"
-                               :default-value="item.defaultValue"
-                               :dict-code="item.anjiSelectOption.dictCode"
-                               :url="item.anjiSelectOption.url"
-                               :method="item.anjiSelectOption.method"
-                               :query-param="item.anjiSelectOption.queryParam"
-                               :option="item.anjiSelectOption.option"
-                               :label="item.anjiSelectOption.label"
-                               :disabled-options="item.anjiSelectOption.disabledOptions"
-                               :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                               @change="(value, option) => tableRowChange(scope.$index, item.field, value, option)" />
+                  <anji-select
+                    v-else-if="item.inputType == 'anji-select'"
+                    v-model.trim="scope.row[item.field]"
+                    :multiple="item.anjiSelectOption.multiple"
+                    :default-value="item.defaultValue"
+                    :dict-code="item.anjiSelectOption.dictCode"
+                    :url="item.anjiSelectOption.url"
+                    :method="item.anjiSelectOption.method"
+                    :query-param="item.anjiSelectOption.queryParam"
+                    :option="item.anjiSelectOption.option"
+                    :label="item.anjiSelectOption.label"
+                    :disabled-options="item.anjiSelectOption.disabledOptions"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    @change="
+                      (value, option) =>
+                        tableRowChange(scope.$index, item.field, value, option)
+                    "
+                  />
                   <!-- 日期时间框  -->
-                  <el-date-picker v-else-if="item.inputType.indexOf('date') >= 0"
-                                  v-model="scope.row[item.field]"
-                                  style="width: 100%"
-                                  :placeholder="item.placeholder || '请选择'"
-                                  :type="item.inputType"
-                                  :clearable="item.clearable !== false"
-                                  :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
-                                  @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <el-date-picker
+                    v-else-if="item.inputType.indexOf('date') >= 0"
+                    v-model="scope.row[item.field]"
+                    style="width: 100%"
+                    :placeholder="
+                      item.placeholder || $lang('placeholder_select')
+                    "
+                    :type="item.inputType"
+                    :clearable="item.clearable !== false"
+                    :disabled="
+                      saveButtonStatus[scope.$index] == 'inShow' ||
+                        item.disabled
+                    "
+                    @change="
+                      value => tableRowChange(scope.$index, item.field, value)
+                    "
+                  />
                   <!-- 待扩展的表单类型,请自行扩展 -->
-                  <el-input v-else
-                            placeholder="组件不支持此类型表单请至组件内部自行扩展"
-                            disabled />
+                  <el-input
+                    v-else
+                    placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                    disabled
+                  />
                 </el-form-item>
               </template>
             </el-table-column>
           </template>
-          <el-table-column fixed="right"
-                           label="操作"
-                           width="100">
+          <el-table-column
+            fixed="right"
+            :label="$lang('GLOBAL.table_operation')"
+            width="100"
+          >
             <template slot-scope="scope">
-              <el-button type="text"
-                         size="small"
-                         @click="handleAddOrUpdate(scope.row, scope.$index)">{{ getRowEditButton(scope.$index) }}</el-button>
-              <el-button type="text"
-                         size="small"
-                         @click="handleDelete(scope.row, scope.$index)">删除</el-button>
+              <el-button
+                type="text"
+                size="small"
+                @click="handleAddOrUpdate(scope.row, scope.$index)"
+                >{{ getRowEditButton(scope.$index) }}</el-button
+              >
+              <el-button
+                type="text"
+                size="small"
+                @click="handleDelete(scope.row, scope.$index)"
+                >{{ $lang("btn_delete") }}</el-button
+              >
             </template>
           </el-table-column>
         </el-table>
         <!-- 表格结束 -->
       </el-form>
-      <button v-if="modelType != 'view'"
-              class="table-add-row-button"
-              @click="handleAdd">
+      <button
+        v-if="modelType != 'view'"
+        class="table-add-row-button"
+        @click="handleAdd"
+      >
         <i class="el-icon-plus" />
-        <span>新增</span>
+        <span>{{ $lang("btn_add") }}</span>
       </button>
     </div>
   </div>
 </template>
 
 <script>
-const ROW_DELETE_FLAG = 'deletedFlag'
+const ROW_DELETE_FLAG = "deletedFlag";
 export default {
   components: {},
   props: {
@@ -142,242 +211,268 @@ export default {
       type: [Object],
       default: () => {
         return {
-          title: '', // 页面标题
-          labelWidth: '',
+          title: "", // 页面标题
+          labelWidth: "",
           queryFormFields: [], // 查询表单条件
           buttons: {
             // 按钮
             query: {},
             edit: {},
             delete: {},
-            add: {},
+            add: {}
           },
-          columns: [], // 表格列
-        }
-      },
+          columns: [] // 表格列
+        };
+      }
     },
     relateData: {
       // 关联的主记录
       type: [Object],
       default: () => {
-        return {}
-      },
+        return {};
+      }
     },
     value: {
       type: [Array],
       default: () => {
-        return []
-      },
+        return [];
+      }
     },
     valueNew: {
       type: [Array],
       default: () => {
-        return []
-      },
+        return [];
+      }
     }
   },
-  data () {
+  data() {
     return {
       checkRecords: [], // 表格中当前选中的记录
 
       form: {
         records: [], // 接口返回的记录列表
-        total: 0, // 接口返回的总条数
+        total: 0 // 接口返回的总条数
       },
 
       saveButtonStatus: [], // 维护表格中每行编辑按钮的状态 inShow inEditing inAdding
 
       rowIdList: []
-    }
+    };
   },
   computed: {
     // 主键的列名
-    primaryKeyFieldName () {
-      var primaryKey = this.option.columns.find((item) => item['primaryKey'] == true)
+    primaryKeyFieldName() {
+      let primaryKey = this.option.columns.find(
+        item => item["primaryKey"] == true
+      );
       if (primaryKey != null) {
-        return primaryKey['field']
+        return primaryKey["field"];
       } else {
-        return null
-        console.warn('在columns中查找primaryKey=true失败,会导致查询详情和删除失败')
+        return null;
+        console.warn(
+          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
+        );
       }
     },
     // 指定当前实体关联主表的关联字段,孙子关联表,没有该属性
-    joinColumn () {
-      var columnName = this.option.joinColumn
+    joinColumn() {
+      let columnName = this.option.joinColumn;
       if (this.isBlank(columnName)) {
-        console.warn('在columns中查找关联字段失败,会导致查询详情和删除失败,孙子关联表忽略该错误')
-        columnName = ''
+        console.warn(
+          "在columns中查找关联字段失败,会导致查询详情和删除失败,孙子关联表忽略该错误"
+        );
+        columnName = "";
       }
-      return columnName
+      return columnName;
     },
     // 未删除的记录
-    formRecordsUndelete () {
+    formRecordsUndelete() {
       if (this.form.records == null) {
-        return []
+        return [];
       }
-      return this.form.records.filter((item) => item[ROW_DELETE_FLAG] == null || item[ROW_DELETE_FLAG] == false)
-    },
+      return this.form.records.filter(
+        item => item[ROW_DELETE_FLAG] == null || item[ROW_DELETE_FLAG] == false
+      );
+    }
   },
   watch: {},
-  created () {
+  created() {
     // 主表 relateData 的关联字段 joinColumn 变动时,触发查询子表的查询,孙子关联儿子的无效
     if (this.isNotBlank(this.joinColumn)) {
       this.$watch(
-        function () {
-          return this.relateData[this.joinColumn]
+        function() {
+          return this.relateData[this.joinColumn];
         },
-        function (newVal, oldVal) {
+        function(newVal, oldVal) {
           // 如果是关联字段发生更新,触发查询
           if (this.isNotBlank(newVal)) {
-            this.handleQueryPageList(newVal)
+            this.handleQueryPageList(newVal);
           } else {
             // 如果关联字段为空,清空本表格的数据,如果是父组件(弹出框)关闭时,设置this.relateData = {默认值}时触发
-            this.checkRecords = []
-            this.form.records = []
-            this.form.total = 0
-            this.saveButtonStatus = []
+            this.checkRecords = [];
+            this.form.records = [];
+            this.form.total = 0;
+            this.saveButtonStatus = [];
           }
         }
-      )
+      );
     }
   },
-  mounted () {
+  mounted() {
     // 首次打开时,根据主表关联字段查询子表,加载表格数据
-    if (this.isNotBlank(this.relateData) && this.isNotBlank(this.relateData[this.joinColumn])) {
-      this.handleQueryPageList()
+    if (
+      this.isNotBlank(this.relateData) &&
+      this.isNotBlank(this.relateData[this.joinColumn])
+    ) {
+      this.handleQueryPageList();
     }
   },
   methods: {
     // 该行是否显示 true/false/ 'hideOnAdd hideOnView hideOnEdit'
-    fieldIsHide (tableHide) {
-      if (typeof tableHide == 'boolean') {
-        return tableHide
+    fieldIsHide(tableHide) {
+      if (typeof tableHide == "boolean") {
+        return tableHide;
       }
-      if (typeof tableHide == 'string') {
-        if (this.modelType == 'add') {
-          return tableHide.indexOf('hideOnAdd') >= 0
+      if (typeof tableHide == "string") {
+        if (this.modelType == "add") {
+          return tableHide.indexOf("hideOnAdd") >= 0;
         }
-        if (this.modelType == 'view') {
-          return tableHide.indexOf('hideOnView') >= 0
+        if (this.modelType == "view") {
+          return tableHide.indexOf("hideOnView") >= 0;
         }
-        if (this.modelType == 'edit') {
-          return tableHide.indexOf('hideOnEdit') >= 0
+        if (this.modelType == "edit") {
+          return tableHide.indexOf("hideOnEdit") >= 0;
         }
       }
-      return false
+      return false;
     },
     // 获取行的提交按钮文字
-    getRowEditButton (index) {
-      if (this.saveButtonStatus[index] == 'inEditing') {
-        return 'btn_savetemp'
-      } else if (this.saveButtonStatus[index] == 'inAdding') {
-        return 'btn_savetemp'
-      } else if (this.saveButtonStatus[index] == 'inShow') {
-        return 'btn_edit'
+    getRowEditButton(index) {
+      if (this.saveButtonStatus[index] == "inEditing") {
+        return this.$lang("btn_savetemp");
+      } else if (this.saveButtonStatus[index] == "inAdding") {
+        return this.$lang("btn_savetemp");
+      } else if (this.saveButtonStatus[index] == "inShow") {
+        return this.$lang("btn_edit");
       } else {
-        return 'not_permission'
+        return "not_permission";
       }
     },
     // 表格行渲染前前置处理
-    tableRowClassAdapter ({ row, rowIndex }) {
-      row.index = rowIndex
+    tableRowClassAdapter({ row, rowIndex }) {
+      row.index = rowIndex;
     },
     // 查询
-    async handleQueryPageList (joinColumnValue) {
+    async handleQueryPageList(joinColumnValue) {
       if (this.isBlank(joinColumnValue)) {
-        joinColumnValue = this.relateData[this.joinColumn]
+        joinColumnValue = this.relateData[this.joinColumn];
       }
-      var params = {}
-      params[this.joinColumn] = joinColumnValue
-      this.queryPageList(params)
+      let params = {};
+      params[this.joinColumn] = joinColumnValue;
+      this.queryPageList(params);
     },
     // 暴露给外部直接调用带参数
-    async queryPageList (params) {
+    async queryPageList(params) {
       // 默认的排序
       if (this.isNotBlank(this.option.buttons.query.order)) {
-        params['sort'] = this.option.buttons.query.sort
-        params['order'] = this.option.buttons.query.order
+        params["sort"] = this.option.buttons.query.sort;
+        params["order"] = this.option.buttons.query.order;
       }
-      const { data, code } = await this.option.buttons.query.api(params)
-      if (code != '200') return
-      this.form.records = data.records
-      this.form.total = data.total
-      this.$emit('input', this.form.records)
-      for (var i = 0; i < this.form.total; i++) {
-        this.saveButtonStatus.push('inShow')
+      const { data, code } = await this.option.buttons.query.api(params);
+      if (code != "200") return;
+      this.form.records = data.records;
+      this.form.total = data.total;
+      this.$emit("input", this.form.records);
+      for (let i = 0; i < this.form.total; i++) {
+        this.saveButtonStatus.push("inShow");
       }
     },
     // 选择项改变时
-    handleSelectionChange (val) {
-      this.checkRecords = val
+    handleSelectionChange(val) {
+      this.checkRecords = val;
     },
     // 表格选中某一行时
-    handleTableRowClick (row, column, event) {
+    handleTableRowClick(row, column, event) {
       // console.log(row)
       // console.log(column)
       // 行点击后,回调option中的tableRowClick事件
-      if (typeof this.option.tableRowClick == 'function') {
-        this.option.tableRowClick(this.form.records, row, row.index, this.relateData)
+      if (typeof this.option.tableRowClick == "function") {
+        this.option.tableRowClick(
+          this.form.records,
+          row,
+          row.index,
+          this.relateData
+        );
       }
     },
     // 行数据更新时回调
-    tableRowChange (rowIndex, fieldName, fieldVal, fieldExtend) {
+    tableRowChange(rowIndex, fieldName, fieldVal, fieldExtend) {
       // 通知外面的组件
-      this.$emit('input', this.form.records)
+      this.$emit("input", this.form.records);
 
       // 表单变动后,回调option中的tableRowChange事件
-      if (typeof this.option.tableChange == 'function') {
-        this.option.tableChange(this.form.records, rowIndex, fieldName, fieldVal, fieldExtend, this.relateData)
+      if (typeof this.option.tableChange == "function") {
+        this.option.tableChange(
+          this.form.records,
+          rowIndex,
+          fieldName,
+          fieldVal,
+          fieldExtend,
+          this.relateData
+        );
       }
     },
     // 新增
-    handleAdd () {
-      this.saveButtonStatus.push('inAdding')
-      this.form.records.push({})
+    handleAdd() {
+      this.saveButtonStatus.push("inAdding");
+      this.form.records.push({});
     },
     // 父节点Change,子节点表格更新
-    handleUpdata () {
-      this.saveButtonStatus = []
-      this.form.records = []
+    handleUpdata() {
+      this.saveButtonStatus = [];
+      this.form.records = [];
     },
     // 提交和修改
-    handleAddOrUpdate (row, index) {
+    handleAddOrUpdate(row, index) {
       // 编辑状态下点击保存提交
-      if (this.saveButtonStatus[index] == 'inEditing' || this.saveButtonStatus[index] == 'inAdding') {
-        this.handleSaveTemp(row, index)
+      if (
+        this.saveButtonStatus[index] == "inEditing" ||
+        this.saveButtonStatus[index] == "inAdding"
+      ) {
+        this.handleSaveTemp(row, index);
       } else {
-        this.$set(this.saveButtonStatus, index, 'inEditing')
+        this.$set(this.saveButtonStatus, index, "inEditing");
       }
     },
     // 校验表单
-    validate (callback) {
-      this.$refs['form'].validate(async (valid, obj) => {
+    validate(callback) {
+      this.$refs["form"].validate(async (valid, obj) => {
         if (callback != null) {
-          callback(valid)
+          callback(valid);
         }
-      })
+      });
     },
     // 暂存
-    async handleSaveTemp (row, index) {
-      this.$refs['form'].validate((valid) => {
+    async handleSaveTemp(row, index) {
+      this.$refs["form"].validate(valid => {
         if (valid) {
           if (this.isBlank(row[this.primaryKeyFieldName])) {
             // 补全关联属性
-            if (typeof this.option.beforeInsert == 'function') {
-              this.option.beforeInsert(this.relateData, row)
+            if (typeof this.option.beforeInsert == "function") {
+              this.option.beforeInsert(this.relateData, row);
             }
           } else {
             // 补全关联属性
-            if (typeof this.option.beforeUpdate == 'function') {
-              this.option.beforeUpdate(this.relateData, row)
+            if (typeof this.option.beforeUpdate == "function") {
+              this.option.beforeUpdate(this.relateData, row);
             }
           }
           // 将行按钮的文字改成编辑
-          this.$set(this.saveButtonStatus, index, 'inShow')
+          this.$set(this.saveButtonStatus, index, "inShow");
           // 通知外面的组件
-          this.$emit('input', this.form.records)
+          this.$emit("input", this.form.records);
         }
-      })
+      });
       /*
       this.$refs['form'].validate((valid) => {
         if (valid) {
@@ -417,32 +512,34 @@ export default {
       */
     },
     // 删除
-    handleDelete (row, index) {
-      this.saveButtonStatus.splice(index, 1) // 清空状态
+    handleDelete(row, index) {
+      this.saveButtonStatus.splice(index, 1); // 清空状态
       // 界面上临时新增出来的一行,还没有提交到数据库,可以直接删除
-      if (this.saveButtonStatus[index] == 'inAdding') {
-        this.form.records.splice(index, 1)
-        this.saveButtonStatus.splice(index, 1)
-        this.$emit('input', this.form.records)
-        return
+      if (this.saveButtonStatus[index] == "inAdding") {
+        this.form.records.splice(index, 1);
+        this.saveButtonStatus.splice(index, 1);
+        this.$emit("input", this.form.records);
+        return;
       }
       // if (this.isBlank(row) || this.isBlank(row[this.primaryKeyFieldName])) {
       //   return
       // }
       // 将对应的行标识成删除
       // 找出该行在原始记录中的index
-      // var realIndex = this.form.records.findIndex((item) => item[this.primaryKeyFieldName] == row[this.primaryKeyFieldName])
-      // row[ROW_DELETE_FLAG] = true
-      // this.$set(this.form.records, realIndex, row)
+      let realIndex = this.form.records.findIndex(
+        item => item[this.primaryKeyFieldName] == row[this.primaryKeyFieldName]
+      );
+      row[ROW_DELETE_FLAG] = true;
+      this.$set(this.form.records, realIndex, row);
+      this.$emit("input", this.form.records);
+      // this.form.records.splice(index, 1)
+      // this.rowIdList.push(row.id)
       // this.$emit('input', this.form.records)
-      this.form.records.splice(index, 1)
-      this.rowIdList.push(row.id)
-      this.$emit('input', this.form.records)
-      this.$emit('update:valueNew', this.rowIdList)
+      // this.$emit('update:valueNew', this.rowIdList)
       /*
       之前是直接调用接口删除,后面统一改成在主表接口中增加、更新、删除
       // 已经保存在数据库的的行,要调用删除按钮
-      var primaryKey = row[this.primaryKeyFieldName]
+      let primaryKey = row[this.primaryKeyFieldName]
       this.$confirm(this.$lang('promptMessage_deleteTip'), this.$lang('promptMessage_deleteTipTitle'), {
         type: 'warning',
         confirmButtonClass: 'delete_sure',
@@ -459,9 +556,9 @@ export default {
           e
         })
         */
-    },
-  },
-}
+    }
+  }
+};
 </script>
 
 <style scoped lang="scss">
diff --git a/report-ui/src/components/AnjiPlus/anji-crud/edit.vue b/report-ui/src/components/AnjiPlus/anji-crud/edit.vue
index 76bb6d6b..b13411a2 100644
--- a/report-ui/src/components/AnjiPlus/anji-crud/edit.vue
+++ b/report-ui/src/components/AnjiPlus/anji-crud/edit.vue
@@ -1,13 +1,7 @@
-<!--
- * @Author: lide1202@hotmail.com
- * @Date: 2021-3-13 11:04:24
- * @Last Modified by:   lide1202@hotmail.com
- * @Last Modified time: 2021-3-13 11:04:24
- !-->
 <template>
   <el-dialog
     :width="dialogWidth"
-    :class="dialogFullScreen?'full-screen':'notfull-screen'"
+    :class="dialogFullScreen ? 'full-screen' : 'notfull-screen'"
     :close-on-click-modal="false"
     center
     :visible.sync="showDialog"
@@ -15,7 +9,7 @@
     @close="handleCloseDialog('close')"
   >
     <template v-slot:title>
-      {{ option.title + '--' + modelType }}
+      {{ option.title + "--" + $lang(`btn_${modelType}`) }}
       <button
         type="button"
         aria-label="Close"
@@ -67,49 +61,55 @@
     <!--自定义的卡片插槽-->
     <slot name="customCard" />
 
-    <div
-      slot="footer"
-      style="text-align: center"
-    >
-      <slot
-        v-if="modelType =='edit'"
-        name="editBtn"
-        :rowData="rowData"
-      />
-      <el-button
-        type="danger"
-        plain
-        @click="handleCloseDialog('close')"
-      >关闭</el-button>
+    <div slot="footer" style="text-align: center">
+      <slot v-if="modelType == 'edit'" name="editBtn" :rowData="rowData" />
+      <el-button type="danger" plain @click="handleCloseDialog('close')">{{
+        $lang("btn_close")
+      }}</el-button>
       <el-button
-        v-if="modelType!='view'"
+        v-if="modelType != 'view'"
         type="primary"
         plain
         @click="handleValidateAndSave"
-      >保存</el-button>
+        >{{ $lang("btn_save") }}</el-button
+      >
     </div>
   </el-dialog>
 </template>
 
 <script>
-import EditForm from './edit-form'
-import EditTable from './edit-table'
+import EditForm from "./edit-form";
+import EditTable from "./edit-table";
 export default {
-  name: 'EditDialog',
+  name: "EditDialog",
   components: { EditForm, EditTable },
   props: {
     visible: {
       type: [Boolean],
       default: () => {
-        return false
-      },
+        return false;
+      }
     },
     rowData: {
       // 查询参数,对应表格那行数据
       type: [Object],
       default: () => {
-        return {}
-      },
+        return {};
+      }
+    },
+    // 预处理详情接口数据
+    handleDetailData: {
+      type: Function,
+      default: data => {
+        return data;
+      }
+    },
+    // 预处理提交数据
+    submitDetailData: {
+      type: Function,
+      default: (data, tpe) => {
+        return data;
+      }
     },
     modelType: String, // add view edit
     option: {
@@ -117,20 +117,20 @@ export default {
       type: Object,
       default: () => {
         return {
-          title: '', // 页面标题
-          labelWidth: '', // 表单输入框label宽度
+          title: "", // 页面标题
+          labelWidth: "", // 表单输入框label宽度
           queryFormFields: [], // 查询表单条件
           buttons: {
             // 按钮
             query: {},
             edit: {},
             delete: {},
-            add: {},
+            add: {}
           },
-          columns: [], // 表格列
-        }
-      },
-    },
+          columns: [] // 表格列
+        };
+      }
+    }
   },
   data() {
     return {
@@ -141,191 +141,230 @@ export default {
       // 提交表单的数据
       saveForm: {},
       // 已成功校验的关联表单个数
-      countForValidJoinForm: 0,
-    }
+      countForValidJoinForm: 0
+    };
   },
   computed: {
     // 弹出框的宽度,根据一行显示几列动态调整
     dialogWidth() {
       if (this.cardRowColNum == 2) {
-        return '60%'
+        return "60%";
       }
       if (this.cardRowColNum == 3) {
-        return '70%'
+        return "70%";
       }
       if (this.cardRowColNum == 4) {
-        return '80%'
+        return "80%";
       }
-      return '60%'
+      return "60%";
     },
     // 关联属性表
     joinEntitys() {
       if (this.isBlank(this.option.joinEntitys)) {
-        return []
+        return [];
       } else {
-        return this.option.joinEntitys
+        return this.option.joinEntitys;
         // 找出所有hide != true的关联表
-        // var entitys = this.option.joinEntitys.filter((item) => item['hide'] == null || item['hide'] == false)
+        // let entitys = this.option.joinEntitys.filter((item) => item['hide'] == null || item['hide'] == false)
         // return entitys
       }
     },
     // 一对一关联表的个数
     countJoinEntityOneToOne() {
-      var entitys = this.joinEntitys.filter((item) => item['joinType'] == 'OneToOne')
+      let entitys = this.joinEntitys.filter(
+        item => item["joinType"] == "OneToOne"
+      );
       if (entitys == null) {
-        return 0
+        return 0;
       }
-      return entitys.length
-    },
+      return entitys.length;
+    }
   },
   watch: {
     // 监控dialog的显示隐藏变量
     visible(newValue, oldValue) {
-      this.showDialog = newValue
+      this.showDialog = newValue;
       // 为主表的编辑表单,渲染上默认值
-      this.initDefaultSaveForm()
+      this.initDefaultSaveForm();
     },
     rowData(newValue, oldValue) {
       if (newValue != null) {
-        this.queryByPrimarykey(newValue)
+        this.queryByPrimarykey(newValue);
       }
-    },
+    }
   },
   mounted() {
     // 为主表的编辑表单,渲染上默认值
-    this.initDefaultSaveForm()
+    this.initDefaultSaveForm();
   },
   methods: {
     // 暴露给外部crud页面,回传saveForm的值
     getSaveForm() {
-      return this.saveForm
+      return this.saveForm;
     },
     setSaveForm(saveForm) {
-      this.saveForm = saveForm
+      this.saveForm = saveForm;
     },
     initDefaultSaveForm() {
       // saveForm的默认值
-      var defaultSaveForm = {}
-      this.option.columns.forEach((item) => {
-        var key = item.editField
+      let defaultSaveForm = {};
+      this.option.columns.forEach(item => {
+        let key = item.editField;
         if (this.isBlank(key)) {
-          key = item.field
+          key = item.field;
         }
-        var val = item.defaultValue
+        let val = item.defaultValue;
         if (this.isNotBlank(val)) {
-          defaultSaveForm[key] = val
+          defaultSaveForm[key] = val;
         }
-      })
+      });
       // 为主表的编辑表单,渲染上默认值
-      this.saveForm = this.deepClone(defaultSaveForm)
-      console.log('编辑框默认值:' + JSON.stringify(this.saveForm))
+      this.saveForm = this.deepClone(defaultSaveForm);
+      console.log("编辑框默认值:" + JSON.stringify(this.saveForm));
     },
     handleCloseDialog(val) {
       // 为主表的编辑表单,渲染上默认值
-      this.initDefaultSaveForm()
-      this.showDialog = false, // 编辑详情弹框是否显示
-      this.dialogFullScreen = false, // 弹出框全屏
-      this.cardRowColNum = 2, // 主信息一行显示几列
-      this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
-      this.$emit('closeEvent', val)
+      this.initDefaultSaveForm();
+      this.showDialog = false; // 编辑详情弹框是否显示
+      this.dialogFullScreen = false; // 弹出框全屏
+      this.cardRowColNum = 2; // 主信息一行显示几列
+      this.countForValidJoinForm = 0; // 已成功校验的关联表单个数
+      this.$emit("closeEvent", val);
+    },
+    handleTopCloseDialog() {
+      // 为主表的编辑表单,渲染上默认值
+      this.initDefaultSaveForm();
+      this.showDialog = false; // 编辑详情弹框是否显示
+      this.dialogFullScreen = false; // 弹出框全屏
+      this.cardRowColNum = 2; // 主信息一行显示几列
+      this.countForValidJoinForm = 0; // 已成功校验的关联表单个数
     },
     // 设置一行显示几列
     handleSetRowColNum(num) {
-      this.cardRowColNum = num
+      this.cardRowColNum = num;
     },
     // 根据关联类型计算组件
     getComponentByJoinType(type, item) {
-      if (type == 'OneToOne') {
-        return 'EditForm'
-      } else if (type == 'OneToMany') {
-        return 'EditTable'
+      if (type == "OneToOne") {
+        return "EditForm";
+      } else if (type == "OneToMany") {
+        return "EditTable";
       } else {
-        return ''
+        return "";
       }
     },
     async queryByPrimarykey(rowData) {
-      const { data, code } = await this.option.buttons.queryByPrimarykey.api(rowData)
-      if (code != '200') return
-      this.showDialog = true
-      this.saveForm = data
+      const { data, code } = await this.option.buttons.queryByPrimarykey.api(
+        rowData
+      );
+      if (code != "200") return;
+      this.showDialog = true;
+      this.saveForm = this.handleDetailData(data);
     },
     // 保存前,先调用校验
     handleValidateAndSave() {
-      this.countForValidJoinForm = 0
+      this.countForValidJoinForm = 0;
       // 主表单校验
-      this.$refs.mainForm.validate((mainValid) => {
+      this.$refs.mainForm.validate(mainValid => {
         if (mainValid == false) {
-          console.warn('主表单校验失败')
-          return
+          console.warn("主表单校验失败");
+          return;
         }
-        console.log('主表单校验完成')
+        console.log("主表单校验完成");
         if (this.joinEntitys == null || this.joinEntitys.length == 0) {
           // 如果子表没有信息,直接提交
-          this.handleSave()
-          return
+          this.handleSave();
+          return;
         }
-        for (var i = 0; i < this.joinEntitys.length; i++) {
-          console.log(`开始校验子表单-${i} 校验`)
-          var item = this.joinEntitys[i]
-          if (this.$refs['joinForm' + i] == null || item.hide == true || this.saveForm[item.fieldNameInMainEntityOnSave] == null || this.saveForm[item.fieldNameInMainEntityOnSave].length == 0) {
-            console.log('子表单没有数据,直接跳过')
-            this.countForValidJoinForm++
-            console.log('已经校验的子表单:' + this.countForValidJoinForm + ' 共:' + this.joinEntitys.length)
+        for (let i = 0; i < this.joinEntitys.length; i++) {
+          console.log(`开始校验子表单-${i} 校验`);
+          let item = this.joinEntitys[i];
+          console.log(item);
+          if (
+            this.$refs["joinForm" + i] == null ||
+            item.hide == true ||
+            this.saveForm[item.fieldNameInMainEntityOnSave] == null ||
+            this.saveForm[item.fieldNameInMainEntityOnSave].length == 0
+          ) {
+            console.warn("子表单校验失败");
+          } else {
+            const childrenChecked = this.checkedChildrenValidate(
+              this.saveForm[item.fieldNameInMainEntityOnSave],
+              item.columns
+            );
+            if (!childrenChecked) {
+              return;
+            }
+
+            //  console.log('子表单没有数据,直接跳过')
+            this.countForValidJoinForm++;
+            console.log(
+              "已经校验的子表单:" +
+                this.countForValidJoinForm +
+                " 共:" +
+                this.joinEntitys.length
+            );
             // 所有关联表单校验通过
             if (this.countForValidJoinForm == this.joinEntitys.length) {
-              console.log('子表单校验完成,提交主表单')
-              this.handleSave()
+              console.log("子表单校验完成,提交主表单");
+              this.handleSave();
             }
-            continue
+            continue;
           }
-          var joinForm = this.$refs['joinForm' + i]
-          if (toString.call(joinForm) == '[object Array]') {
-            joinForm = joinForm[0]
+          let joinForm = this.$refs["joinForm" + i];
+          if (toString.call(joinForm) == "[object Array]") {
+            joinForm = joinForm[0];
           }
-          joinForm.validate((joinValid) => {
+          joinForm.validate(joinValid => {
             if (joinValid) {
-              this.countForValidJoinForm++
-              console.log('已经校验的子表单:' + this.countForValidJoinForm + ' 共:' + this.joinEntitys.length)
+              this.countForValidJoinForm++;
+              console.log(
+                "已经校验的子表单:" +
+                  this.countForValidJoinForm +
+                  " 共:" +
+                  this.joinEntitys.length
+              );
               // 所有关联表单校验通过
               if (this.countForValidJoinForm == this.joinEntitys.length) {
-                console.log('子表单校验完成,提交主表单')
-                this.handleSave()
+                console.log("子表单校验完成,提交主表单");
+                this.handleSave();
               }
             } else {
-              console.warn(`子表单${i}校验失败:`)
+              console.warn(`子表单${i}校验失败:`);
             }
-          })
+          });
         }
-      })
+      });
     },
     async handleSave() {
+      const params = this.submitDetailData(this.saveForm, this.modelType);
       // 新增
-      if (this.modelType == 'add') {
-        const { code, message } = await this.option.buttons.add.api(this.saveForm)
-        if (code == '200') {
+      if (this.modelType == "add") {
+        const { code, message } = await this.option.buttons.add.api(params);
+        if (code == "200") {
           // 保存结束,关闭对话框
-          this.handleCloseDialog()
+          this.handleCloseDialog();
           // 向外层发关闭事件
-          this.$emit('closeEvent')
-          return
+          // this.$emit('closeEvent')
+          return;
         } else {
-          this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
-          console.log(`提交表单调用新增接口失败:${message}`)
+          // ;(this.countForValidJoinForm = 0), // 已成功校验的关联表单个数
+          console.log(`提交表单调用新增接口失败:${message}`);
         }
       }
       // 修改
-      if (this.modelType == 'edit') {
+      if (this.modelType == "edit") {
         // console.log(this.$slots.customCard[0].context.$refs.cardInEditData.updateData.settingValue)
-        const { code, message } = await this.option.buttons.edit.api(this.saveForm)
-        if (code == '200') {
+        const { code, message } = await this.option.buttons.edit.api(params);
+        if (code == "200") {
           // 保存结束,关闭对话框
-          this.handleCloseDialog()
+          this.handleCloseDialog();
           // 向外层发关闭事件
-          this.$emit('closeEvent')
-          return
+          // this.$emit('closeEvent')
+          return;
         } else {
-          this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
-          console.log(`提交表单调用更新接口失败:${message}`)
+          // ;(this.countForValidJoinForm = 0), // 已成功校验的关联表单个数
+          console.log(`提交表单调用更新接口失败:${message}`);
         }
       }
       /* 分步提交
@@ -340,7 +379,7 @@ export default {
           return
         }
         // 主表保存成功后,保存子表
-        for (var i = 0; i < this.joinEntitys.length; i++) {
+        for (let i = 0; i < this.joinEntitys.length; i++) {
           if (this.joinEntitys[i].joinType == 'OneToOne') {
             this.$refs['joinForm' + i].handleSave(() => {
               this.countForSavedOneToOneJoinEntity++
@@ -355,8 +394,24 @@ export default {
         }
       })*/
     },
-  },
-}
+    // 子表单数据校验
+    checkedChildrenValidate(list, confingList) {
+      const configFileds = confingList.map(item => item.field);
+      for (let i = 0; i < list.length; i++) {
+        const item = list[i];
+        for (let key = 0; key < configFileds.length; key++) {
+          if (
+            item.hasOwnProperty(configFileds[key]) &&
+            !item[configFileds[key]]
+          ) {
+            return false;
+          }
+        }
+      }
+      return true;
+    }
+  }
+};
 </script>
 
 <style scoped lang="scss">
diff --git a/report-ui/src/components/AnjiPlus/anji-daterange.vue b/report-ui/src/components/AnjiPlus/anji-daterange.vue
new file mode 100644
index 00000000..3cebf091
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-daterange.vue
@@ -0,0 +1,151 @@
+<!--
+:v-model=""
+:format="'yyyy-MM-dd HH:mm:ss'"  绑定值的格式,一般是参数传到后台的数据格式
+:defaultValue="['00:00:00','23:59:59']"  选中日期后的默认具体时刻 数据格式为数组 ['00:00:00', '23:59:59']
+@change=""
+-->
+<template>
+  <div>
+    <el-date-picker
+      v-model="selectedRangeValue"
+      style="width: 100%"
+      type="datetimerange"
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :value-format="format"
+      unlink-panels
+      :picker-options="pickerOptions"
+      :default-time="defaultValue"
+      @change="valueChanged"
+    />
+  </div>
+</template>
+<script>
+import miment from "miment";
+export default {
+  props: {
+    format: {
+      type: String,
+      default: "yyyy-MM-dd HH:mm:ss"
+    },
+    defaultValue: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    value: {
+      type: String,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      selectedRangeValue: [],
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "今天",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date(
+                new Date(new Date().getTime()).setHours(0, 0, 0, 0)
+              );
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "昨天",
+            onClick(picker) {
+              const start = new Date(
+                new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(
+                  0,
+                  0,
+                  0,
+                  0
+                )
+              );
+              const end = new Date(
+                new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(
+                  23,
+                  59,
+                  59,
+                  999
+                )
+              );
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(
+                miment()
+                  .add(-1, "ww")
+                  .stamp()
+              );
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(
+                miment()
+                  .add(-1, "MM")
+                  .stamp()
+              );
+              picker.$emit("pick", [start, end]);
+            }
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(
+                miment()
+                  .add(-3, "MM")
+                  .stamp()
+              );
+              picker.$emit("pick", [start, end]);
+            }
+          }
+        ],
+        onPick: ({ maxDate, minDate }) => {
+          this.minDate = minDate;
+          this.maxDate = maxDate;
+        }
+      }
+    };
+  },
+  watch: {
+    value(val) {
+      this.parseDefaultVal(val);
+    }
+  },
+  created() {},
+  methods: {
+    // el-date-range的值默认是数组,gaea需要的是'start,end'字符串,这里做下转换
+    parseDefaultVal(val) {
+      if (val != null) {
+        this.selectedRangeValue = val.split(",");
+      } else {
+        this.selectedRangeValue = [];
+      }
+    },
+    valueChanged(val) {
+      if (val != null) {
+        let rangeVal = val.toString();
+        this.$emit("change", rangeVal);
+        this.$emit("input", rangeVal);
+      }
+    }
+  }
+};
+</script>
diff --git a/report-ui/src/components/AnjiPlus/anji-dialog.vue b/report-ui/src/components/AnjiPlus/anji-dialog.vue
new file mode 100644
index 00000000..03ce18ed
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-dialog.vue
@@ -0,0 +1,209 @@
+<template>
+  <el-dialog
+    :width="dialogWidth"
+    :class="dialogFullScreen ? 'full-screen' : 'notfull-screen'"
+    center
+    :fullscreen="dialogFullScreen"
+    :visible.sync="dialogConfig.dialogVisible"
+    :before-close="handleDialogClose"
+    append-to-body
+    modal-append-to-body
+  >
+    <template v-slot:title>
+      {{ getDialogTitle(dialogConfig.dialogType) }}
+      <button
+        v-if="dialogConfig.isFullScreen"
+        type="button"
+        aria-label="Close"
+        class="el-dialog__headerbtn"
+        style="right: 50px"
+        @click="dialogFullScreen = !dialogFullScreen"
+      >
+        <i class="el-dialog__close el-icon el-icon-full-screen" />
+      </button>
+    </template>
+    <div class="card-body">
+      <div class="anji-card">
+        <div v-if="dialogConfig.isSetColRow" class="card-head">
+          <div class="main-card-header-button">
+            <el-button type="text" @click="handleSetRowColNum(4)"
+              >||||</el-button
+            >
+            <el-button type="text" @click="handleSetRowColNum(3)"
+              >|||</el-button
+            >
+            <el-button type="text" @click="handleSetRowColNum(2)">||</el-button>
+          </div>
+        </div>
+        <slot name="dialogCont" />
+        <slot />
+      </div>
+    </div>
+    <div slot="footer" class="anji-button">
+      <el-button
+        v-if="isBtnClose"
+        type="danger"
+        plain
+        @click="handleDialogClose"
+        >{{ dialogConfig.isBtnClose.text || $lang("btn_close") }}</el-button
+      >
+      <el-button
+        v-if="isBtnSave"
+        type="primary"
+        plain
+        @click="handleDialogSave"
+        >{{ dialogConfig.isBtnSave.text || $lang("btn_save") }}</el-button
+      >
+      <slot name="dialogBtn" />
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  props: {
+    // 弹框配置文件
+    dialogConfig: {
+      required: true,
+      type: Object,
+      default: () => {
+        return {
+          dialogVisible: false,
+          dialogType: "add",
+          dialogWidth: "60%",
+          isFullScreen: true,
+          isSetColRow: true,
+          isBtnClose: {
+            value: true,
+            text: this.$lang("btn_close")
+          },
+          isBtnSave: {
+            value: true,
+            text: this.$lang("btn_save")
+          },
+          column: 2,
+          setColumnFn: () => {}
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      dialogFullScreen: false // 弹出框全屏
+    };
+  },
+  computed: {
+    dialogWidth() {
+      if (this.dialogConfig.dialogWidth) {
+        return this.dialogConfig.dialogWidth;
+      } else {
+        if (this.dialogConfig.column == 2) {
+          return "60%";
+        }
+        if (this.dialogConfig.column == 3) {
+          return "70%";
+        }
+        if (this.dialogConfig.column == 4) {
+          return "80%";
+        }
+      }
+      return "60%";
+    },
+    isBtnClose() {
+      return this.dialogConfig.isBtnClose || this.dialogConfig.isBtnClose.value;
+    },
+    isBtnSave() {
+      return this.dialogConfig.isBtnSave || this.dialogConfig.isBtnSave.value;
+    }
+  },
+  methods: {
+    getDialogTitle(type) {
+      let title = this.$lang("btn_add");
+      switch (type) {
+        case "add":
+          title = this.$lang("btn_add");
+          break;
+        case "edit":
+          title = this.$lang("btn_edit");
+          break;
+        case "view":
+          title = this.$lang("btn_view");
+          break;
+        default:
+          title = type;
+          break;
+      }
+      return title;
+    },
+    handleSetRowColNum(val) {
+      const cardRowColSpan = 24 / val;
+      this.dialogConfig.setColumnFn(cardRowColSpan);
+    },
+    handleDialogClose() {
+      this.$emit("handleClose");
+    },
+    handleDialogSave() {
+      this.$emit("handleDialogSave");
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.notfull-screen {
+  /deep/.el-dialog__body {
+    background-color: rgb(240, 242, 245);
+    padding: 5px;
+    max-height: 60vh;
+    overflow: auto;
+  }
+}
+.full-screen {
+  /deep/.el-dialog__body {
+    background-color: rgb(240, 242, 245);
+    padding: 5px;
+    height: calc(100vh - 110px);
+    overflow: auto;
+  }
+}
+.anji-card {
+  margin-bottom: 5px;
+  box-sizing: border-box;
+  padding: 0 20px;
+  color: rgba(0, 0, 0, 0.65);
+  font-size: 14px;
+  font-letiant: tabular-nums;
+  line-height: 1.5;
+  list-style: none;
+  font-feature-settings: "tnum";
+  position: relative;
+  background: #fff;
+  border-radius: 2px;
+  transition: all 0.3s;
+}
+.drawerContainer {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  .drawerMain {
+    flex: 1;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding: 0 20px;
+  }
+  .footer {
+    border-top: 1px solid #eff0f3;
+    height: 66px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+.addForm {
+  text-align: center;
+}
+.activeColor /deep/.el-form-item__label {
+  color: #5887fb;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus/anji-fourLevel.vue b/report-ui/src/components/AnjiPlus/anji-fourLevel.vue
new file mode 100644
index 00000000..92a53d8b
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-fourLevel.vue
@@ -0,0 +1,72 @@
+<template>
+  <el-cascader ref="country" v-model="countryValue" size="mini" :props="props" :options="countryOptions" style="width: 100%" clearable @change="handleChange" />
+</template>
+<script>
+import { postRequest } from '@/api/common.js'
+export default {
+  props: {
+    value: null,
+  },
+  data() {
+    return {
+      countryValue: [],
+      props: {
+        value: 'value',
+        label: 'label',
+        children: 'children',
+      },
+      countryOptions: [],
+    }
+  },
+  watch: {
+    value(val) {
+      this.handleCountryEcho(val)
+    },
+  },
+  created() {
+    this.provinceFn()
+    this.handleCountryEcho(this.value)
+  },
+  methods: {
+    // 获取省市区
+    async provinceFn() {
+      const { code, data } = await postRequest()
+      if (code != 200) return
+      this.countryOptions = data
+    },
+    handleChange(value) {
+      if (value.length > 0) {
+        const countryNameArr = this.$refs.country.getCheckedNodes()[0].pathLabels
+        const countryObj = {
+          countryName: countryNameArr[0],
+          provinceName: countryNameArr[1],
+          cityName: countryNameArr[2],
+          areaName: countryNameArr[3],
+          countryCode: value[0],
+          provinceCode: value[1],
+          cityCode: value[2],
+          areaCode: value[3],
+        }
+
+        this.$emit('input', countryObj)
+        this.$emit('change', countryObj)
+      } else {
+        this.$emit('input', {})
+        this.$emit('change', {})
+      }
+    },
+    handleCountryEcho(val) {
+      if (val) {
+        this.countryValue = [val.countryCode, val.provinceCode, val.cityCode, val.areaCode || val.regionCode]
+      } else {
+        this.countryValue = []
+      }
+    },
+  },
+}
+</script>
+<style lang="scss" scoped>
+.con {
+  display: inline-block;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus/anji-input.vue b/report-ui/src/components/AnjiPlus/anji-input.vue
new file mode 100644
index 00000000..5399ad6c
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus/anji-input.vue
@@ -0,0 +1,180 @@
+<template>
+  <div class="anji-input">
+    <el-input v-model="inputValue" type="number" :placeholder="placeholder" :disabled="disabled" clearable @change="change">
+      <template slot="append">
+        <el-dropdown @command="handleClick">
+          <el-button type="primary"> {{ systemUnit }}</el-button>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item v-for="item in SystemTimeConversionRadioGroup" :key="item.label" :command="item">{{ item.label }}</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </template>
+    </el-input>
+  </div>
+</template>
+<script>
+export default {
+  props: {
+    value: null,
+    placeholder: null,
+    unit: String,
+    defaultUnit: String,
+    disabled: Boolean,
+  },
+  data() {
+    return {
+      inputValue: '',
+      input: '',
+      unitObj: {},
+      systemUnit: '', // 单位
+      systemConversion: '', // 转换单位
+      systemKeepPoint: '', // 保留小数位
+      systemRounding: '', // 取整方案
+      SystemTimeConversionRadioGroup: [], // 单位选择
+    }
+  },
+  watch: {
+    value: function (val, oldVal) {
+      this.echoInputValue(val)
+    },
+  },
+  mounted() {
+    this.getLocalStorage()
+    this.getSystemData()
+    this.echoInputValue(this.value)
+  },
+  methods: {
+    getSystemData() {
+      const defaultUnit = this.defaultUnit
+      this.systemKeepPoint = this.getSystemKeepPoint()
+      this.systemRounding = this.getSystemRounding()
+      this.SystemTimeConversionRadioGroup = this.getSystemTimeConversionRadioGroup()
+      if (defaultUnit == undefined) {
+        this.systemUnit = this.getSystemUnit()
+        this.systemConversion = this.getSystemConversion()
+      } else {
+        this.systemUnit = defaultUnit
+        this.systemConversion = this.SystemTimeConversionRadioGroup.find((item) => item.label == this.systemUnit)['value']
+      }
+    },
+    // 获取单位
+    getSystemUnit() {
+      let unit = ''
+      for (const key in this.unitObj) {
+        if (key.toLowerCase().indexOf('conversiontext') != -1) {
+          unit = this.unitObj[key]
+        }
+      }
+      return unit
+    },
+    // 获取转换关系
+    getSystemConversion() {
+      let conversion = ''
+      for (const key in this.unitObj) {
+        if (key.toLowerCase().indexOf('conversion') != -1 && key.toLowerCase().indexOf('conversiontext') == -1 && key.toLowerCase().indexOf('conversionradiogroup') == -1) {
+          conversion = this.unitObj[key]
+        }
+      }
+      return conversion
+    },
+    // 获取保留几位小数
+    getSystemKeepPoint() {
+      let keepPoint = ''
+      for (const key in this.unitObj) {
+        if (key.toLowerCase().indexOf('keeppoint') != -1) {
+          keepPoint = this.unitObj[key]
+        }
+      }
+      return keepPoint
+    },
+    // 获取怎么取整
+    getSystemRounding() {
+      let rounding = ''
+      for (const key in this.unitObj) {
+        if (key.toLowerCase().indexOf('pointrounding') != -1) {
+          rounding = this.unitObj[key]
+        }
+      }
+      return rounding
+    },
+    // 获取各类型的全部单位
+    getSystemTimeConversionRadioGroup() {
+      let radionGroup = ''
+      for (const key in this.unitObj) {
+        if (key.toLowerCase().indexOf('radiogroup') != -1) {
+          radionGroup = this.unitObj[key]
+        }
+      }
+      return radionGroup
+    },
+    handleClick(command) {
+      this.systemUnit = command.label
+      this.systemConversion = command.value
+      this.echoInputValue(this.value)
+    },
+    // 从本地获取数据
+    getLocalStorage() {
+      const localstorageData = this.getSettingByName('unit_conversion')
+      const unitObj = {}
+      for (const key in localstorageData) {
+        if (key.toLowerCase().indexOf(this.unit.toLowerCase()) != -1) {
+          unitObj[key] = localstorageData[key]
+        }
+      }
+      this.unitObj = unitObj
+    },
+    change(val) {
+      if (val === null || val === '') {
+        this.$emit('input', val)
+        this.$emit('change', val)
+        return
+      }
+      this.inputValue = val
+      if (this.systemRounding != undefined) {
+        let newVal
+        if (this.systemRounding == 'up') {
+          newVal = this.upFixed(val, this.systemKeepPoint)
+        }
+        if (this.systemRounding == 'down') {
+          newVal = this.downFixed(val, this.systemKeepPoint)
+        }
+        this.inputValue = newVal
+        const value = (newVal || val) * this.systemConversion
+        this.$emit('input', value)
+        this.$emit('change', value)
+      } else {
+        const value = val * this.systemConversion
+        this.inputValue = val
+        this.$emit('input', value)
+        this.$emit('change', value)
+      }
+    },
+    echoInputValue(val) {
+      if (val === undefined || val === '') {
+        this.inputValue = ''
+      } else {
+        this.inputValue = val / this.systemConversion
+      }
+    },
+    // 向上取整
+    upFixed(num, fix) {
+      return Math.ceil(num * Math.pow(10, fix)) / Math.pow(10, fix).toFixed(fix)
+    },
+    // 向下取整
+    downFixed(num, fix) {
+      return Math.floor(num * Math.pow(10, fix)) / Math.pow(10, fix).toFixed(fix)
+    },
+  },
+}
+</script>
+<style lang="less" scoped>
+// /deep/.el-input-group__append {
+//   padding: 0 5px;
+// }
+/deep/.el-input__suffix {
+  padding: 0 6px;
+}
+.anji-input /deep/ .el-input__inner {
+  padding-right: 0 !important;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus/anji-select.vue b/report-ui/src/components/AnjiPlus/anji-select.vue
index 7e952ffd..e2830c43 100644
--- a/report-ui/src/components/AnjiPlus/anji-select.vue
+++ b/report-ui/src/components/AnjiPlus/anji-select.vue
@@ -25,6 +25,8 @@
     <el-select
       v-model="selectValue"
       :clearable="clearable"
+      :allow-create="!!allowCreate"
+      :default-first-option="!!allowCreate"
       :collapse-tags="collapseTags"
       filterable
       class="filter-item"
@@ -39,29 +41,39 @@
         v-for="(item, index) in options"
         :key="index"
         :label="getItemLabel(item, label)"
-        :value="item[option]"
+        :value="item[option] + ''"
         :disabled="isDisabledOption(item)"
       >
         <template v-if="mergeLabel">
           <span style="float: left">{{ getItemLabel(item, label) }}</span>
-          <span style="float: right; color: #8492a6; font-size: 13px">{{
-            item[option]
-          }}</span>
+          <span
+            v-if="!dictCode && !localOptions"
+            style="float: right; color: #8492a6; font-size: 13px"
+            >{{ item[option] }}</span
+          >
         </template>
       </el-option>
-      <el-option v-if="totalPage > 1" label="搜索更多" value="" disabled />
+      <el-option v-if="totalPage >= 1" value="" disabled>{{
+        $lang("GLOBAL.placeholder_select_remote_search")
+      }}</el-option>
     </el-select>
   </div>
 </template>
 
 <script>
 import request from "@/utils/request";
-import { getStorageItem } from "@/utils/storage";
 export default {
   props: {
     dictCode: null, // 当传入dictCode时,可以不用传递url
     url: null,
-    method: null,
+    allowCreate: {
+      type: Boolean,
+      default: false
+    },
+    method: {
+      type: String,
+      default: "get"
+    },
     queryParam: {
       type: Object,
       default: () => {
@@ -91,7 +103,7 @@ export default {
     },
     mergeLabel: {
       type: Boolean,
-      default: false
+      default: true
     },
     // 禁用的下拉选项
     disabledOptions: {
@@ -117,10 +129,13 @@ export default {
   computed: {
     // 根据dictCode和url拼出最终的请求url
     requestUrl() {
+      if (this.localOptions) {
+        return null;
+      }
       if (this.url != null && this.url.trim() != "") {
         if (this.url.indexOf("?") > 0) {
           if (this.option == null) {
-            console.log("url-" + this.url.substring(this.url.indexOf("?")));
+            // console.log('url-' + this.url.substring(this.url.indexOf('?')))
           }
           if (this.label == null) {
           }
@@ -134,20 +149,39 @@ export default {
     }
   },
   watch: {
-    value: function(val, oldVal) {
-      if (this.multiple != null) {
-        if (!this.value) {
-          this.selectValue = [];
-        } else {
-          this.selectValue = this.value;
+    dictCode(val) {
+      if (val) {
+        this.queryData();
+      }
+    },
+    // 监听接口地址变化时,触发刷新请求
+    localOptions(val) {
+      this.options = val;
+    },
+    value: {
+      handler(val) {
+        if (
+          typeof val == "string" &&
+          this.url != null &&
+          this.url.trim() != ""
+        ) {
+          this.remoteQuery(val);
         }
-      } else {
-        if (this.value != null && this.value != undefined) {
-          this.selectValue = this.value;
+        if (this.multiple != null) {
+          if (!this.value) {
+            this.selectValue = [];
+          } else {
+            this.selectValue = this.value;
+          }
         } else {
-          this.selectValue = "";
+          if (this.value != null && this.value != undefined) {
+            this.selectValue = this.value + "";
+          } else {
+            this.selectValue = "";
+          }
         }
-      }
+      },
+      immediate: true
     },
     url() {
       setTimeout(() => {
@@ -160,7 +194,7 @@ export default {
       this.selectValue = this.value;
     } else {
       if (this.value != null) {
-        this.selectValue = this.value;
+        this.selectValue = this.value + "";
       }
     }
   },
@@ -181,17 +215,17 @@ export default {
       ) {
         return false;
       }
-      var currentOptionVal = option[this.option];
+      let currentOptionVal = option[this.option];
       return this.disabledOptions.indexOf(currentOptionVal) >= 0;
     },
     change(value) {
       if (value === "") {
-        value = null;
+        value = "";
       }
       this.$emit("input", value);
 
       // 根据当前值,找出对应的选项
-      var optionItem = this.options.find(item => item[this.option] == value);
+      let optionItem = this.options.find(item => item[this.option] == value);
       this.$emit("change", value, optionItem);
     },
     // 根据用户配置的label,生成对应的标签
@@ -199,34 +233,38 @@ export default {
       if (label.indexOf("${") < 0 && label.indexOf("}" < 0)) {
         return item[label];
       }
-      var reg = /\$\{[a-zA-Z0-9]*\}/g;
-      var list = label.match(reg);
+      let reg = /\$\{[a-zA-Z0-9]*\}/g;
+      let list = label.match(reg);
       // ["${id}", "${text}"]
-      var result = label;
-      for (var i = 0; i < list.length; i++) {
-        var sub = list[i];
-        var key = sub.replace("${", "").replace("}", "");
+      let result = label;
+      for (let i = 0; i < list.length; i++) {
+        let sub = list[i];
+        let key = sub.replace("${", "").replace("}", "");
         result = result.replace(sub, item[key]);
       }
       return result;
     },
     // 从本地localStorage取 gaeaDict
     getOptionsFromLocalStorage() {
-      var dicts = getStorageItem("gaeaDict");
-      var options = [];
+      let dicts = JSON.parse(localStorage.getItem("gaeaDict"));
+      let options = [];
       if (!dicts.hasOwnProperty(this.dictCode)) {
         return [];
       }
-      var dictItems = dicts[this.dictCode];
-      for (var i = 0; i < dictItems.length; i++) {
-        var dictItem = dictItems[i];
-        options.push({ id: dictItem.id, text: dictItem.text });
+      let dictItems = dicts[this.dictCode];
+      for (let i = 0; i < dictItems.length; i++) {
+        let dictItem = dictItems[i];
+        options.push({
+          id: dictItem.id.toString(),
+          text: dictItem.text,
+          extend: dictItem.extend
+        });
       }
       return options;
     },
     queryData() {
       // 所有从本地localStorage取,因为在App.vue中已经请求远程保存到本地了
-      var options = this.getOptionsFromLocalStorage();
+      let options = this.getOptionsFromLocalStorage();
       if (this.isNotBlank(options)) {
         this.options = options;
         return;
@@ -245,7 +283,7 @@ export default {
       }
     },
     queryDataByGet(keyword) {
-      var param = this.deepClone(this.queryParam);
+      let param = this.deepClone(this.queryParam);
       if (this.isNotBlank(keyword)) {
         param["keyword"] = keyword;
       }
@@ -255,11 +293,12 @@ export default {
         headers: { noPrompt: true },
         params: param
       }).then(response => {
+        // console.log(response)
         this.setOptions(response.data);
       });
     },
     queryDataByPost(keyword) {
-      var param = this.deepClone(this.queryParam);
+      let param = this.deepClone(this.queryParam);
       if (this.isNotBlank(keyword)) {
         param["keyword"] = keyword;
       }
@@ -299,9 +338,9 @@ export default {
       this.options = resData.records;
     },
     remoteQuery(keyword) {
-      if (this.isBlank(keyword)) {
-        return;
-      }
+      // if (this.isBlank(keyword)) {
+      //   return
+      // }
       setTimeout(() => {
         if (
           this.method != null &&
@@ -316,8 +355,11 @@ export default {
   }
 };
 </script>
-<style scoped>
-.filter-item {
-  width: 100%;
+<style lang="scss" scoped>
+.el-select-dropdown__item.selected {
+  text-align: center;
+}
+.el-select-dropdown__item.is-disabled {
+  text-align: center;
 }
 </style>
diff --git a/report-ui/src/components/AnjiPlus/anji-tree.vue b/report-ui/src/components/AnjiPlus/anji-tree.vue
index dd767518..107878ac 100644
--- a/report-ui/src/components/AnjiPlus/anji-tree.vue
+++ b/report-ui/src/components/AnjiPlus/anji-tree.vue
@@ -6,90 +6,107 @@
  !-->
 <template>
   <div>
-    <el-input class="filterInput" placeholder="搜索" v-model="filterText" v-if="enableFilter" />
+    <el-input
+      class="filterInput"
+      placeholder="搜索"
+      v-model="filterText"
+      v-if="enableFilter"
+    />
     <div class="title">{{ labelName }}</div>
-    <el-tree ref="table_tree" :data="treeData" node-key="id" :default-expand-all="isOpen" :expand-on-click-node="false" :filter-node-method="filterNode" @node-click="nodeClick" @check="checkedEvent" />
+    <el-tree
+      ref="table_tree"
+      :data="treeData"
+      node-key="id"
+      :default-expand-all="isOpen"
+      :expand-on-click-node="false"
+      :filter-node-method="filterNode"
+      @node-click="nodeClick"
+      @check="checkedEvent"
+    />
   </div>
 </template>
 
 <script>
-import request from '@/utils/request'
+import request from "@/utils/request";
 export default {
   components: {},
   props: {
     url: {
       type: [String],
       default: () => {
-        return ''
-      },
+        return "";
+      }
     },
     id: {
       type: [String],
       default: () => {
-        return 'id'
-      },
+        return "id";
+      }
     },
     label: {
       type: [String],
       default: () => {
-        return ''
-      },
+        return "";
+      }
     },
     value: {
       type: [String],
       default: () => {
-        return ''
-      },
+        return "";
+      }
     },
     labelName: String,
     enableFilter: Boolean,
-    isOpen: Boolean,
+    isOpen: Boolean
   },
   data() {
     return {
-      filterText: '',
-      treeData: [],
-    }
+      filterText: "",
+      treeData: []
+    };
   },
   computed: {},
   watch: {
     filterText(val) {
-      this.$refs.table_tree.filter(val)
-    },
+      this.$refs.table_tree.filter(val);
+    }
   },
   mounted() {
-    this.queryData()
+    this.queryData();
   },
   methods: {
     filterNode(val, data) {
-      if (!val) return true
-      return data.label.indexOf(val) !== -1
+      if (!val) return true;
+      return data.label.indexOf(val) !== -1;
     },
     queryData() {
       if (this.isBlank(this.url)) {
-        return
+        return;
       }
       request({
         url: this.url,
-        method: 'GET',
-      }).then((response) => {
-        if (response.code != '200') {
-          return
+        method: "GET"
+      }).then(response => {
+        if (response.code != "200") {
+          return;
         }
-        this.treeData = Object.prototype.toString.call(response.data) == '[object Array]' ? response.data : response.data.tree|| response.data.menuTree
-      })
+        this.treeData =
+          Object.prototype.toString.call(response.data) == "[object Array]"
+            ? response.data
+            : response.data.tree || response.data.menuTree;
+      });
     },
     // 点击tree节点时 将tree的id作为上级机构代码 查询列表
     nodeClick(node) {
-      this.$emit('input', node['id'])
-      this.$emit('node-click', node['id'])
+      this.$emit("input", node["id"]);
+      this.$emit("node-click", node["id"]);
     },
     checkedEvent(item, evt) {
-      var ids = evt.checkedKeys.toString()
-      this.$emit('input', ids)
-    },
-  },
-}
+      let ids = evt.checkedKeys.toString();
+      this.$emit("input", ids);
+    }
+  }
+};
 </script>
 
 <style scoped lang="scss">
diff --git a/report-ui/src/components/AnjiPlus/anji-upload.vue b/report-ui/src/components/AnjiPlus/anji-upload.vue
index 7b16bffe..565d17df 100644
--- a/report-ui/src/components/AnjiPlus/anji-upload.vue
+++ b/report-ui/src/components/AnjiPlus/anji-upload.vue
@@ -13,7 +13,43 @@
       :before-upload="handleBeforeUpload"
       :class="fileList && fileList.length >= limit ? 'hide_box' : ''"
     >
-      <i class="el-icon-plus" />
+      <i slot="default" class="el-icon-plus" />
+      <div slot="file" slot-scope="{ file }" class="imgBox">
+        <img
+          v-if="typeImgShow(file)"
+          class="el-upload-list__item-thumbnail"
+          :src="file.url"
+          alt=""
+        />
+        <svg-icon
+          v-else-if="typePdfShow(file)"
+          icon-class="PDF"
+          class="iconFont"
+        />
+        <svg-icon
+          v-else-if="typeExcelShow(file)"
+          icon-class="Excel"
+          class="iconFont"
+        />
+        <span class="el-upload-list__item-actions">
+          <span
+            v-if="typeImgShow(file)"
+            class="el-upload-list__item-preview"
+            @click="handlePictureCardPreview(file)"
+          >
+            <i class="el-icon-zoom-in" />
+          </span>
+          <span
+            class="el-upload-list__item-delete"
+            @click="handleDownload(file)"
+          >
+            <i class="el-icon-download" />
+          </span>
+          <span class="el-upload-list__item-delete" @click="handleRemove(file)">
+            <i class="el-icon-delete" />
+          </span>
+        </span>
+      </div>
     </el-upload>
     <el-dialog :visible.sync="dialogVisibleImageUpload" :modal="false">
       <img width="100%" :src="imageUploadUrl" alt="" />
@@ -33,19 +69,25 @@ export default {
     viewUrl: {
       type: String,
       default: () => {
-        return process.env.BASE_API + "/file/download/";
+        return "";
       }
     },
     limit: {
       type: Number,
       default: () => {
-        return 1;
+        return 3;
       }
     },
     value: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    uploadType: {
       type: String,
       default: () => {
-        return "";
+        return "img";
       }
     }
   },
@@ -53,15 +95,16 @@ export default {
     return {
       imageUploadUrl: "",
       dialogVisibleImageUpload: false,
-      fileList: []
+      fileList: [],
+      modeString: ""
     };
   },
   computed: {
     requestUrl() {
       if (this.upLoadUrl != null && this.upLoadUrl.trim() != "") {
-        return process.env.BASE_API + this.upLoadUrl;
+        return process.env.VUE_APP_BASE_API + this.upLoadUrl;
       } else {
-        return process.env.BASE_API + "/file/upload";
+        return process.env.VUE_APP_BASE_API + "/meta/file/upload";
       }
     },
     headers() {
@@ -73,37 +116,80 @@ export default {
   watch: {
     value: {
       handler(val) {
-        this.echoUpload(this.value);
+        this.echoUpload(val);
       },
       immediate: true
     }
   },
-  mounted() {
-    this.echoUpload(this.value);
-  },
+  mounted() {},
   methods: {
+    // 图片
+    typeImgShow(file) {
+      if (!file.fileType) return;
+      const fileType = file.fileType.toLowerCase();
+      if (
+        fileType == "jpg" ||
+        fileType == "png" ||
+        fileType == "gif" ||
+        fileType == "icon"
+      ) {
+        return true;
+      }
+      return false;
+    },
+    // pdf
+    typePdfShow(file) {
+      if (!file.fileType) return;
+      const fileType = file.fileType.toLowerCase();
+      if (fileType == "pdf") {
+        return true;
+      }
+      return false;
+    },
+    // excel
+    typeExcelShow(file) {
+      if (!file.fileType) return;
+      const fileType = file.fileType.toLowerCase();
+      if (fileType == "xlsx" || fileType == "xls" || fileType == "csv") {
+        return true;
+      }
+      return false;
+    },
     handleRemove(file) {
-      this.fileList = [];
+      const fileList = [];
+      this.fileList.forEach(el => {
+        if (el.fileId != file.fileId) {
+          fileList.push(el);
+        }
+      });
+      this.fileList = fileList;
       this.change();
     },
     handlePictureCardPreview(file) {
       this.imageUploadUrl = file.url;
       this.dialogVisibleImageUpload = true;
     },
+    // 下载
+    handleDownload(file) {
+      window.open(file.url);
+    },
     // 上传成功的回调
     handleSuccess(response, file, fileList) {
+      console.log(fileList);
       if (response.code != 200) {
         this.$message.error("上传失败");
         return;
       }
       this.fileList.push({
-        url: file.response.data.urlPath
+        url: file.response.data.urlPath,
+        fileId: file.response.data.fileId,
+        fileType: file.response.data.fileType
       });
       this.change();
     },
     // 回传出去
     change() {
-      const fileList = (this.fileList.length > 0 && this.fileList[0].url) || "";
+      const fileList = this.fileList;
       this.$emit("input", fileList);
       this.$emit("change", fileList);
     },
@@ -113,7 +199,18 @@ export default {
         .split(".")
         [file.name.split(".").length - 1].toLowerCase();
       // .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
-      const extensionList = ["png", "jpg", "gif", "icon"];
+      const extensionList = [
+        "png",
+        "jpg",
+        "gif",
+        "icon",
+        "pdf",
+        "xlsx",
+        "xls",
+        "csv",
+        "mp4",
+        "avi"
+      ];
       if (extensionList.indexOf(extension) < 0) {
         this.$message.warning("请上传正确的格式文件");
         return false;
@@ -122,11 +219,20 @@ export default {
     },
     // 回显
     echoUpload(val) {
-      if (!val) {
-        this.fileList = [];
+      if (val && val.length > 0) {
+        const fileList = [];
+        for (let i = 0; i < val.length; i++) {
+          const obj = {};
+          obj.url = val[i].urlPath || val[i].url;
+          obj.fileType = val[i].fileType;
+          obj.fileId = val[i].fileId;
+          fileList.push(obj);
+        }
+        fileList.forEach((el, index) => {
+          this.$set(this.fileList, index, el);
+        });
       } else {
-        const list = [{ url: val }];
-        this.fileList = list;
+        this.fileList = [];
       }
     }
   }
@@ -142,7 +248,7 @@ export default {
   width: 60px;
   height: 60px;
 }
-.hide_box /deep/ .el-upload--picture-card {
+.hide_box .el-upload--picture-card {
   display: none;
 }
 .el-upload-list__item {
diff --git a/report-ui/src/components/AnjiPlus备份/anji-crud/anji-crud.vue b/report-ui/src/components/AnjiPlus备份/anji-crud/anji-crud.vue
new file mode 100644
index 00000000..bcc89183
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-crud/anji-crud.vue
@@ -0,0 +1,808 @@
+<template>
+  <div :class="[hasTreeFieldInQueryForm ? 'page-container' : 'app-container']">
+    <div v-if="hasTreeFieldInQueryForm" class="left-container">
+      <AnjiTree
+        ref="queryFormTree"
+        v-model.trim="queryParams[queryFormTreeField.field]"
+        :is-open="queryFormTreeField.anjiTreeOption.isOpen"
+        :enable-filter="queryFormTreeField.anjiTreeOption.enableFilter"
+        :label-name="queryFormTreeField.label"
+        :url="queryFormTreeField.anjiTreeOption.url"
+        @node-click="handleTreeNodeCheck"
+      />
+    </div>
+    <div class="right-container">
+      <!-- 查询表单开始 -->
+      <el-form
+        ref="formSearch"
+        :model="queryParams"
+        label-width="100px"
+        v-permission="option.buttons.query.permission"
+      >
+        <el-row>
+          <el-col
+            v-for="(item, index) in queryFormFieldExcludeTree"
+            :key="item.field"
+            :span="queryFormFieldSpan(item)"
+          >
+            <el-form-item
+              v-if="index <= 2 || (index > 2 && queryParams.showMoreSearch)"
+              :label="item.label"
+              :rules="item.rules"
+              :prop="item.field"
+            >
+              <!-- 输入框 -->
+              <el-input
+                v-if="
+                  item.inputType == 'input' || item.inputType == 'input-number'
+                "
+                v-model.trim="queryParams[item.field]"
+                :placeholder="item.placeholder || '请输入'"
+                :clearable="item.clearable !== false"
+                :disabled="item.disabled"
+                @change="value => queryFormChange(item.field, value)"
+              />
+              <!-- 开关 -->
+              <el-switch
+                v-else-if="item.inputType == 'switch'"
+                v-model.trim="queryParams[item.field]"
+                :disabled="item.disabled"
+                :active-value="item.switchOption.disableValue"
+                :inactive-value="item.switchOption.enableValue"
+                active-color="#5887fb"
+                inactive-color="#ccc"
+                @change="value => queryFormChange(item.field, value)"
+              />
+              <!-- 下拉框 -->
+              <anji-select
+                v-else-if="item.inputType == 'anji-select'"
+                v-model.trim="queryParams[item.field]"
+                :multiple="item.anjiSelectOption.multiple"
+                :dict-code="item.anjiSelectOption.dictCode"
+                :url="item.anjiSelectOption.url"
+                :method="item.anjiSelectOption.method"
+                :query-param="item.anjiSelectOption.queryParam"
+                :option="item.anjiSelectOption.option"
+                :label="item.anjiSelectOption.label"
+                :disabled-options="item.anjiSelectOption.disabledOptions"
+                :disabled="item.disabled"
+                :merge-label="item.anjiSelectOption.mergeLabel"
+                @change="value => queryFormChange(item.field, value)"
+              />
+              <!-- 日期时间框  -->
+              <el-date-picker
+                v-else-if="item.inputType.indexOf('date') >= 0"
+                v-model="queryParams[item.field]"
+                style="width: 100%"
+                :placeholder="item.placeholder || '请选择'"
+                :type="item.inputType"
+                :clearable="item.clearable !== false"
+                @change="value => queryFormChange(item.field, value)"
+              />
+              <!-- 待扩展的表单类型,请自行扩展 -->
+              <el-input
+                v-else
+                placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                disabled
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6" style="text-align: center">
+            <el-button type="primary" @click="handleQueryForm('query')"
+              >查询</el-button
+            >
+            <el-button type="danger" @click="handleResetForm()">重置</el-button>
+            <a
+              v-if="queryFormFieldExcludeTree.length > 3"
+              style="margin-left: 8px"
+              @click="handleToggleMoreSearch"
+            >
+              {{ queryParams.showMoreSearch == true ? "收起" : "展开" }}
+              <i
+                :class="
+                  queryParams.showMoreSearch
+                    ? 'el-icon-arrow-up'
+                    : 'el-icon-arrow-down'
+                "
+              />
+            </a>
+          </el-col>
+        </el-row>
+      </el-form>
+      <!-- 查询表单结束 -->
+
+      <!-- 批量操作 -->
+      <slot name="buttonLeftOnTable" />
+      <el-button
+        v-if="
+          option.buttons.add.isShow == undefined
+            ? true
+            : option.buttons.add.isShow
+        "
+        v-permission="option.buttons.add.permission"
+        type="primary"
+        icon="el-icon-plus"
+        @click="handleOpenEditView('add')"
+        >新增</el-button
+      >
+      <el-button
+        v-if="
+          option.buttons.delete.isShow == undefined
+            ? true
+            : option.buttons.delete.isShow
+        "
+        v-permission="option.buttons.delete.permission"
+        :disabled="disableBatchDelete"
+        type="danger"
+        icon="el-icon-delete"
+        @click="handleDeleteBatch()"
+        >删除</el-button
+      >
+
+      <!-- 表格开始 -->
+      <el-table
+        class="anji_curd_table"
+        :data="records"
+        border
+        @selection-change="handleSelectionChange"
+        @sort-change="handleSortChange"
+      >
+        <!--多选-->
+        <el-table-column fixed type="selection" width="50" align="center" />
+        <!--隐藏列-->
+        <el-table-column v-if="tableExpandColumns.length > 0" type="expand">
+          <template slot-scope="scope">
+            <p
+              v-for="item in tableExpandColumns"
+              :key="item.field"
+              class="table-expand-item"
+            >
+              <span class="titel"> {{ item.label }}: </span>
+              <span>{{ scope.row[item.field] }}</span>
+            </p>
+          </template>
+        </el-table-column>
+        <!--序号-->
+        <el-table-column label="序号" min-width="50" align="center">
+          <template slot-scope="scope">
+            {{
+              queryParams.pageSize * (queryParams.pageNumber - 1) +
+                scope.$index +
+                1
+            }}
+          </template>
+        </el-table-column>
+
+        <template v-for="item in option.columns">
+          <el-table-column
+            v-if="item.tableHide != true && item.columnType != 'expand'"
+            :key="item.field"
+            :prop="item.field"
+            :label="fieldLabel(item)"
+            :min-width="item.minWidth || 110"
+            :sortable="item.sortable"
+            :show-overflow-tooltip="true"
+            align="center"
+          >
+            <template slot-scope="scope">
+              <div v-if="item.columnType == 'imgPreview'">
+                <!-- 图片缩略图-->
+                <el-image
+                  style="width: 25%; height: 50%"
+                  fit="contain"
+                  :src="scope.row[item.field]"
+                  :preview-src-list="[scope.row[item.field]]"
+                />
+              </div>
+              <div v-else>
+                <span v-if="item.inputType == 'switch' && !item.colorStyle">
+                  <el-switch
+                    v-model.trim="scope.row[item.field]"
+                    :active-value="1"
+                    :inactive-value="0"
+                    active-color="#5887fb"
+                    inactive-color="#ccc"
+                    @change="switchChange(scope.row, item.switchOption)"
+                  />
+                </span>
+                <!-- 带单位 -->
+                <span v-else-if="item.inputType == 'anji-input'">{{
+                  fieldValueByAnjiInput(scope.row[item.field], item)
+                }}</span>
+                <!--表格 a 合并 b上-->
+                <span v-else-if="item.mergeColumn"
+                  >{{ scope.row[item.field] }}({{
+                    scope.row[item.mergeColumn]
+                  }})</span
+                >
+                <!-- 没有单位 -->
+                <span
+                  v-else-if="item.colorStyle"
+                  :class="item.colorStyle[scope.row[item.editField]]"
+                  >{{ fieldValueByRowRenderer(scope.row, item) }}</span
+                >
+                <span v-else>{{
+                  fieldValueByRowRenderer(scope.row, item)
+                }}</span>
+                <!-- 正常展示模式
+                <div v-if="!item.custom">
+                  是第一列数据 && 需要高亮字段不为false 高亮并且可以点击
+                  <span v-if="!index && item.operate !== false" class="view" @click="handleOpenEditView('view', scope.row)">{{ scope.row[item.field] }}</span>
+                  <span v-else>{{ scope.row[item.field] }}</span>
+                </div>
+                -->
+                <!-- 自定义展示数据
+                <div v-else v-html="item.renderer(scope.row)" />-->
+              </div>
+            </template>
+          </el-table-column>
+        </template>
+        <!--操作栏-->
+        <el-table-column
+          align="center"
+          fixed="right"
+          label="操作"
+          :width="
+            option.buttons.customButton &&
+            option.buttons.customButton.operationWidth
+              ? option.buttons.customButton.operationWidth
+              : 100
+          "
+        >
+          <template slot-scope="scope">
+            <slot name="edit" :msg="scope.row" />
+            <el-button
+              v-if="
+                (option.buttons.query.isShow == undefined
+                  ? true
+                  : option.buttons.query.isShow) &&
+                  hasPermission(option.buttons.edit.permission) == false
+              "
+              type="text"
+              size="small"
+              @click="handleOpenEditView('view', scope.row)"
+              v-permission="option.buttons.query.permission"
+              >查看</el-button
+            >
+            <el-button
+              v-if="
+                option.buttons.edit.isShow == undefined
+                  ? true
+                  : option.buttons.edit.isShow
+              "
+              type="text"
+              size="small"
+              @click="handleOpenEditView('edit', scope.row)"
+              v-permission="option.buttons.edit.permission"
+              >编辑</el-button
+            >
+            <el-button
+              v-if="
+                hasRowCustomButton == false &&
+                option.buttons.delete.isShow == undefined
+                  ? true
+                  : option.buttons.edit.isShow
+              "
+              type="text"
+              size="small"
+              @click="handleDeleteBatch(scope.row)"
+              v-permission="option.buttons.delete.permission"
+              >删除</el-button
+            >
+            <el-dropdown v-if="hasRowCustomButton" trigger="click">
+              <span class="el-dropdown-link">
+                更多<i class="el-icon-caret-bottom el-icon--right" />
+              </span>
+              <el-dropdown-menu slot="dropdown">
+                <el-dropdown-item class="clearfix">
+                  <slot name="rowButton" :msg="scope.row" />
+                  <el-button
+                    v-if="
+                      option.buttons.delete.isShow == undefined
+                        ? true
+                        : option.buttons.edit.isShow
+                    "
+                    type="text"
+                    size="small"
+                    @click="handleDeleteBatch(scope.row)"
+                    v-permission="option.buttons.delete.permission"
+                    >删除</el-button
+                  >
+                </el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="pagination">
+        <el-pagination
+          v-show="total > 0"
+          background
+          :current-page.sync="queryParams.pageNumber"
+          :page-sizes="$pageSizeAll"
+          :page-size="queryParams.pageSize"
+          layout="total, prev, pager, next, jumper, sizes"
+          :total="total"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+        />
+        <div>
+          <slot name="tableSelectionBtn" :selection="checkRecords" />
+        </div>
+      </div>
+
+      <!-- 表格结束 -->
+
+      <EditDialog
+        ref="edit"
+        :option="option"
+        :model-type="editDialogModelType"
+        :visible="editDialogOpen"
+        :row-data="editDialogRowData"
+        @closeEvent="editDialogClosedEvent"
+      >
+        <template v-slot:customCard>
+          <slot name="cardInEditPage" />
+        </template>
+        <template slot="editBtn" slot-scope="scope">
+          <slot name="editBtnPage" :rowData="scope" />
+        </template>
+      </EditDialog>
+    </div>
+    <slot name="pageSection" />
+  </div>
+</template>
+<script>
+import AnjiTree from "@/components/AnjiPlus/anji-tree.vue";
+import EditDialog from "./edit";
+import request from "@/utils/request";
+export default {
+  components: {
+    EditDialog,
+    AnjiTree
+  },
+  props: {
+    option: {
+      require: true,
+      type: Object,
+      default: () => {
+        return {
+          // 查询表单条件
+          queryFormFields: [],
+          // 按钮
+          buttons: {
+            query: {},
+            edit: {},
+            delete: {},
+            add: {}
+          },
+          // 表格列
+          columns: [],
+          queryFormChange: (fileName, val) => {}
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      // 查询表单提交的值
+      queryParams: {
+        showMoreSearch: false, // 是否展开更多搜索条件
+        pageNumber: 1,
+        pageSize: 10,
+        order: "",
+        sort: ""
+      },
+
+      checkRecords: [], // 表格中当前选中的记录
+      records: [], // 接口返回的记录列表
+      total: 0, // 接口返回的总条数
+
+      // 编辑详情弹框
+      editDialogOpen: false, // 新建时主动打开编辑弹框
+      editDialogRowData: {}, // 编辑时的主键
+      editDialogModelType: "view", // 编辑 查看
+
+      hasRowCustomButton: false // 除了编辑删除外,还有自定义的行按钮
+    };
+  },
+  computed: {
+    // 左侧树形查询条件
+    queryFormTreeField() {
+      var treeField = this.option.queryFormFields.find(
+        item => item["inputType"] == "anji-tree"
+      );
+      return treeField;
+    },
+    // 查询条件里是否有树形控件
+    hasTreeFieldInQueryForm() {
+      return this.isNotBlank(this.queryFormTreeField);
+    },
+    // 不包含树形控件的查询条件
+    queryFormFieldExcludeTree() {
+      var treeFields = this.option.queryFormFields.filter(
+        item => item["inputType"] != "anji-tree"
+      );
+      return treeFields;
+    },
+    // 主键的列名
+    primaryKeyFieldName() {
+      var primaryKey = this.option.columns.find(
+        item => item["primaryKey"] == true
+      );
+      if (primaryKey != null) {
+        return primaryKey["field"];
+      } else {
+        return null;
+        console.warn(
+          "在columns中查找primaryKey=true失败,会导致查询详情和删除失败"
+        );
+      }
+    },
+
+    // 表格中可展开的列
+    tableExpandColumns() {
+      var expandColumns = this.option.columns.filter(
+        item => item["columnType"] == "expand"
+      );
+      return expandColumns;
+    },
+
+    // 是否可以批量删除
+    disableBatchDelete() {
+      return this.checkRecords.length <= 0;
+    }
+  },
+  created() {
+    // 为查询框中所有input加上默认值
+    this.option.queryFormFields.forEach(item => {
+      // 动态添加属性
+      this.$set(this.queryParams, item.field, item.defaultValue || null);
+    });
+    // 查询列表
+    this.handleQueryForm("query");
+    this.queryFormChange();
+  },
+  mounted() {
+    if (this.$scopedSlots["rowButton"] != null) {
+      this.hasRowCustomButton = true;
+    } else {
+      this.hasRowCustomButton = false;
+    }
+  },
+  methods: {
+    queryFormFieldSpan(item) {
+      // console.log(item)
+
+      if (item.span != null) {
+        return item.span;
+      } else {
+        return 6;
+      }
+      // let rowLength = this.option.queryFormFields.length;
+      // console.log(rowLength, "ss")
+      // console.log(rowLength % 3)
+      // if (rowLength <= 3) {
+      //   return 6
+      // }
+      // else if (rowLength % 3 == 0) {
+      //   return 8
+      // } else if (rowLength > 6) {
+      //   return 8
+      // }
+    },
+    // 切换更多搜索条件
+    handleToggleMoreSearch() {
+      this.queryParams.showMoreSearch = !this.queryParams.showMoreSearch;
+    },
+    // 列上排序切换
+    handleSortChange(column) {
+      // {column: {…}, prop: "orgCode", order: "ascending"}
+      if (column == null || column.prop == null) {
+        console.warn("排序字段名prop为空,无法排序");
+        return;
+      }
+      var sort = column.prop; // 列表查询默认排序列
+      var order = column.order == "ascending" ? "ASC" : "DESC";
+      this.queryParams["sort"] = sort;
+      this.queryParams["order"] = order;
+      this.handleQueryForm("query");
+    },
+    // 查询按钮
+    handleQueryForm(from) {
+      // 如果是点查询按钮,把树的查询属性去掉
+      if (from == "query") {
+        if (this.hasTreeFieldInQueryForm) {
+          delete this.queryParams[this.queryFormTreeField.field];
+        }
+      }
+      // 如果是点树查询,把查询区里的属性去掉
+      if (from == "tree") {
+        if (this.hasTreeFieldInQueryForm) {
+          var treeVal = this.queryParams[this.queryFormTreeField.field];
+          this.queryParams = {
+            pageNumber: 1,
+            pageSize: 10
+          };
+          this.queryParams[this.queryFormTreeField.field] = treeVal;
+        }
+      }
+      // 默认的排序
+      if (
+        this.isBlank(this.queryParams["order"]) &&
+        this.isNotBlank(this.option.buttons.query.order)
+      ) {
+        this.queryParams["sort"] = this.option.buttons.query.sort;
+        this.queryParams["order"] = this.option.buttons.query.order;
+      }
+      this.queryParams.pageNumber = 1;
+      this.handleQueryPageList();
+    },
+    // 列表查询
+    async handleQueryPageList() {
+      var params = this.queryParams;
+      // 将特殊参数值urlcode处理 var params = this.urlEncodeObject(this.queryParams, 'order,sort')
+      const { data, code } = await this.option.buttons.query.api(params);
+      if (code != "200") return;
+      this.records = data.records;
+      this.total = data.total;
+    },
+    // 重置
+    handleResetForm() {
+      this.queryParams = {
+        pageNumber: 1,
+        pageSize: 10
+      };
+      // this.$refs['queryForm'].resetFields()
+      // this.records = []
+      // this.total = 0
+    },
+    // 树形查询条件点击回调
+    handleTreeNodeCheck() {
+      this.handleQueryForm("tree");
+      // 为新建页面的对应属性值,绑定上对应的默认值
+      var treeFieldName = this.queryFormTreeField["field"];
+      for (var i = 0; i < this.option.columns.length; i++) {
+        var item = this.option.columns[i];
+        if (
+          item["editField"] == treeFieldName ||
+          item["field"] == treeFieldName
+        ) {
+          this.$set(
+            this.option.columns[i],
+            "defaultValue",
+            this.queryParams[treeFieldName]
+          );
+          break;
+        }
+      }
+    },
+    // 编辑和查看操作
+    handleOpenEditView(modelType, row) {
+      if (modelType == "view" || modelType == "edit") {
+        this.editDialogRowData = row;
+      }
+      this.editDialogModelType = modelType;
+      if (modelType == "add") {
+        // 新增模式,不需要查询数据详情,直接打开
+        this.editDialogOpen = true;
+      }
+      const obj = {
+        type: modelType,
+        value: row
+      };
+      this.$emit("handleCustomValue", obj);
+    },
+    // 弹框被关闭时的回调事件
+    editDialogClosedEvent(value) {
+      // 把列表页中弹框打开标记改成已关闭
+      this.editDialogOpen = false;
+      // 关闭弹出框时,如果有树,刷新下
+      if (
+        this.hasTreeFieldInQueryForm &&
+        this.$refs.queryFormTree != null &&
+        !value
+      ) {
+        this.$refs.queryFormTree.queryData();
+      }
+      this.handleQueryPageList();
+      // 关闭时 清空表单的验证规则
+      this.$refs.edit.$refs.mainForm.$refs.editForm.resetFields();
+    },
+    // 批量删除
+    handleDeleteBatch(row) {
+      var ids = [];
+      if (row != null) {
+        ids.push(row[this.primaryKeyFieldName]); // 删除指定的行
+      } else {
+        // 批量删除选中的行
+        ids = this.checkRecords.map(item => item[this.primaryKeyFieldName]);
+      }
+      this.$confirm("删除确认", "确认要删除吗?", {
+        type: "warning",
+        confirmButtonClass: "delete_sure",
+        cancelButtonClass: "el-button--danger is-plain"
+      })
+        .then(() => {
+          this.option.buttons.delete.api(ids).then(res => {
+            // {code: "200", message: "操作成功", data: true}
+            this.checkRecords = [];
+            // 关闭弹出框时,如果有树,刷新下
+            if (
+              this.hasTreeFieldInQueryForm &&
+              this.$refs.queryFormTree != null
+            ) {
+              this.$refs.queryFormTree.queryData();
+            }
+            this.handleQueryPageList();
+          });
+        })
+        .catch(e => {
+          e;
+        });
+    },
+
+    // 选择项改变时
+    handleSelectionChange(val) {
+      this.checkRecords = val;
+    },
+    // 页码改变
+    handleCurrentChange(pageNumber) {
+      this.queryParams.pageNumber = pageNumber;
+      this.handleQueryPageList();
+    },
+    // 每页size改变时
+    handleSizeChange(val) {
+      this.queryParams.pageNumber = 1;
+      this.queryParams.pageSize = val;
+      this.handleQueryPageList();
+    },
+    // table列文件缩略图
+    thumbnailUrl(row, field) {
+      // return 'http://10.108.3.123:9090/tms/file/download/79ee7e8b-2a9a-4142-b06d-706ac8089205'
+      // if (row.filePath) {
+      //   if (row.filePath.endsWith('xlsx') || row.filePath.endsWith('xls')) {
+      //     return fileExcel;
+      //   } else if (row.filePath.endsWith('pdf')) {
+      //     return filePdf;
+      //   }
+      //   return process.env.VUE_APP_BASE_API + '/tms/file/download/' + row.fileId;
+      // } else {
+      //   return logo;
+      // }
+    },
+    // 带单位的列,需要转换
+    fieldLabel(columnConfig) {
+      if (columnConfig == null) {
+        return "";
+      }
+      if (
+        columnConfig.inputType == "anji-input" &&
+        columnConfig.anjiInput != null
+      ) {
+        return `${columnConfig.label}(${columnConfig.anjiInput.unit})`;
+      } else {
+        return columnConfig.label;
+      }
+    },
+    // 带单位的输入框
+    fieldValueByAnjiInput(value, columnConfig) {
+      if (columnConfig == null) {
+        return value;
+      }
+      if (
+        columnConfig.inputType == "anji-input" &&
+        columnConfig.anjiInput != null
+      ) {
+        return value / columnConfig.anjiInput.conversion;
+      } else {
+        return value;
+      }
+    },
+    // 带表格列格式化的值
+    fieldValueByRowRenderer(row, columnConfig) {
+      if (
+        columnConfig == null ||
+        typeof columnConfig.fieldTableRowRenderer != "function"
+      ) {
+        return row[columnConfig.field];
+      } else {
+        return columnConfig.fieldTableRowRenderer(row);
+      }
+    },
+    // 暴露给外部crud页面,回传saveForm的值
+    getMainEntity() {
+      return this.$refs.edit.getSaveForm();
+    },
+    setMainEntity(object) {
+      this.$refs.edit.setSaveForm(object);
+    },
+    async switchChange(val, api) {
+      request({
+        url: api.url,
+        method: "put",
+        headers: { noPrompt: false },
+        data: [val.id]
+      }).then(response => {
+        this.handleQueryPageList();
+      });
+    },
+    queryFormChange(fileName, fieldVal) {
+      if (typeof this.option.queryFormChange == "function") {
+        this.option.queryFormChange(this.queryParams, fileName, fieldVal);
+      }
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.style-btn {
+  pointer-events: none;
+}
+.page-container {
+  height: 100%;
+  position: relative;
+  .left-container {
+    width: 20%;
+    position: absolute;
+    top: 0;
+    left: 0;
+    background: #fff;
+    border-radius: 4px 0px 0px 4px;
+    padding: 20px 20px 0;
+    overflow: hidden;
+    overflow-y: auto;
+    height: 100%;
+  }
+  .right-container {
+    width: calc(80% - 5px);
+    position: absolute;
+    top: 0;
+    right: 0;
+    background: #fff;
+    border-radius: 0px 4px 4px 0px;
+    padding: 20px 20px 0;
+    height: 100%;
+  }
+}
+.el-table .cell,
+.el-table td div {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.el-dropdown {
+  font-size: 12px;
+  display: inline;
+  color: #5887fb;
+  cursor: pointer;
+}
+.el-dropdown-menu--mini .el-dropdown-menu__item {
+  min-width: 80px;
+  max-width: 110px;
+  float: right;
+  .el-button--text {
+    float: right;
+  }
+  &:hover {
+    background: none !important;
+  }
+  .el-button--mini {
+    float: right;
+  }
+  .el-button + .el-button {
+    margin-left: 0 !important;
+    float: right;
+  }
+}
+.pagination {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  margin-top: 20px;
+}
+.anji_curd_table {
+  margin-top: 20px;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-crud/edit-form.vue b/report-ui/src/components/AnjiPlus备份/anji-crud/edit-form.vue
new file mode 100644
index 00000000..d186a84e
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-crud/edit-form.vue
@@ -0,0 +1,362 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-5-4 11:04:24
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-5-6 11:04:24
+ !-->
+<template>
+  <div v-if="option['hide'] == null || option['hide'] == false" class="anji-card">
+    <div class="card-head">
+      {{ option.title }}
+      <div class="main-card-header-button">
+        <el-button type="text" @click="handleSetRowColNum(4)">||||</el-button>
+        <el-button type="text" @click="handleSetRowColNum(3)">|||</el-button>
+        <el-button type="text" @click="handleSetRowColNum(2)">||</el-button>
+      </div>
+    </div>
+    <div class="card-body">
+      <el-form ref="editForm" :model="editForm" :label-width="option.labelWidth || '100px'">
+        <!--:disabled="modelType == 'view'"-->
+        <template v-for="group in columnGroups">
+          <el-divider v-if="groupModel" :key="group" content-position="left">{{ group }}</el-divider>
+          <el-row :key="group" class="form_table">
+            <template v-for="item in groupFormFields[group]">
+              <el-col v-if="fieldIsHide(item.editHide) == false" :key="item.editField" :span="item.rowColSpan == null ? cardRowColSpan : item.rowColSpan">
+                <el-form-item :label="item.label" :rules="item.rules" :prop="item.editField" :disabled="item.disabled">
+                  <!-- 输入框 -->
+                  <span v-if="item.tips != '' && item.tips != null" :style="{ 'margin-left': '-13px' }" class="input_tips">
+                    <el-tooltip class="item" effect="dark" :content="item.tips" placement="top-start">
+                      <svg-icon icon-class="tishi-yiwen" />
+                    </el-tooltip>
+                  </span>
+                  <el-input v-if="item.inputType == 'input'" v-model.trim="editForm[item.editField]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- 开关 -->
+                  <el-switch v-else-if="item.inputType == 'switch'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :active-value="1" :inactive-value="0" inactive-color="#ccc" active-color="#5887fb" @change="(value) => formChange(item.editField, value, null)" />
+                  <el-input-number v-else-if="item.inputType == 'input-number'" v-model.trim="editForm[item.editField]" :min="item.inputNumberOption.min" :max="item.inputNumberOption.max" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- 自定义input -->
+                  <anji-input v-else-if="item.inputType == 'anji-input'" v-model.trim="editForm[item.editField]" :unit="item.anjiInput.unit" :conversion="item.anjiInput.conversion" :keep-point="item.anjiInput.keepPoint" :rounding="item.anjiInput.rounding" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- 下拉框 -->
+                  <anji-select
+                    v-else-if="item.inputType == 'anji-select'"
+                    v-model.trim="editForm[item.editField]"
+                    :multiple="item.anjiSelectOption.multiple"
+                    :disabled="fieldIsDisable(item.disabled)"
+                    :dict-code="item.anjiSelectOption.dictCode"
+                    :placeholder="item.placeholder"
+                    :url="item.anjiSelectOption.url"
+                    :method="item.anjiSelectOption.method"
+                    :query-param="item.anjiSelectOption.queryParam"
+                    :merge-label="item.anjiSelectOption.mergeLabel"
+                    :option="item.anjiSelectOption.option"
+                    :label="item.anjiSelectOption.label"
+                    :remote-filter="item.anjiSelectOption.remoteFilter"
+                    :disabled-options="item.anjiSelectOption.disabledOptions"
+                    @change="(value, option) => formChange(item.editField, value, option)"
+                  />
+                  <!-- 日期时间框  -->
+                  <el-date-picker v-else-if="item.inputType.indexOf('date') >= 0" v-model="editForm[item.editField]" style="width: 100%" :placeholder="item.placeholder || '请选择'" :type="item.inputType" :format="item.format" :value-format="item.valueFormat" :disabled="fieldIsDisable(item.disabled)" :clearable="item.clearable !== false" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- checkbox -->
+                  <anji-checkbox v-else-if="item.inputType == 'checkbox'" v-model.trim="editForm[item.editField]" :dict-code="item.anjiCheckbox.dictCode" :label="item.anjiCheckbox.label" :disabled="fieldIsDisable(item.disabled)" @change="(value, options) => formChange(item.editField, value, options)" />
+                  <!-- 城市三级联动 -->
+                  <anji-cascader v-else-if="item.inputType == 'anji-cascader'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :url="item.anjiCascader.url" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- 上传组件 -->
+                  <anji-upload v-else-if="item.inputType == 'anji-upload'" v-model.trim="editForm[item.editField]" :up-load-url="item.anjiUpload.upLoadUrl" :view-url="item.anjiUpload.viewUrl" :upload-type="item.anjiUpload.uploadType" :limit="item.anjiUpload.limit" @change="(value) => formChange(item.editField, value, null)" />
+                  <!-- input自带输入建议 -->
+                  <anji-autocomplete v-else-if="item.inputType == 'anji-autocomplete'" v-model.trim="editForm[item.editField]" :disabled="fieldIsDisable(item.disabled)" :label="item.anjiAutocomplete.label" :option="item.anjiAutocomplete.option" :appoint-value="item.anjiAutocomplete.appointValue" :url="item.anjiAutocomplete.url" @change="(value, option) => formChange(item.editField, value, option)" />
+                  <!-- textarea -->
+                  <el-input v-else-if="item.inputType == 'textarea'" v-model.trim="editForm[item.editField]" :placeholder="item.placeholder || '请输入'" :clearable="item.clearable !== false" :disabled="fieldIsDisable(item.disabled)" type="textarea" :rows="2" @change="(value) => formChange(item.editField, value, null)" />
+                  <el-input v-else placeholder="组件不支持此类型表单请至组件内部自行扩展" disabled />
+                </el-form-item>
+              </el-col>
+            </template>
+          </el-row>
+        </template>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  props: {
+    modelType: String, // add view edit
+    showDialog: Boolean,
+    option: {
+      // 界面渲染相关配置json
+      type: [Object],
+      default: () => {
+        return {
+          title: '', // 页面标题
+          labelWidth: '', // 表单输入框label宽度
+          queryFormFields: [], // 查询表单条件
+          buttons: {
+            // 按钮
+            query: {},
+            edit: {},
+            delete: {},
+            add: {},
+          },
+          columns: [], // 表格列
+          formChange: (formData, fieldName, fieldVal, fieldExtend) => {}, // 弹出框表单修改回调
+        }
+      },
+    },
+    // 当relateData不为空时,该组件是渲染与relateData一对一关联的子表信息
+    relateData: {
+      type: [Object],
+      default: () => {
+        return {}
+      },
+    },
+    value: {
+      type: [Object],
+      default: () => {
+        return {}
+      },
+    },
+  },
+  data() {
+    return {
+      cardRowColNum: 2, // 主信息一行显示几列
+      editForm: {}, // 提交表单的数据
+    }
+  },
+  computed: {
+    // 主键的列名
+    primaryKeyFieldName() {
+      var primaryKey = this.option.columns.find((item) => item['primaryKey'] == true)
+      if (primaryKey != null) {
+        return primaryKey['field']
+      } else {
+        return null
+        console.warn('在columns中查找primaryKey=true失败,会导致查询详情和删除失败')
+      }
+    },
+    // 指定当前实体关联主表的
+    joinColumn() {
+      var columnName = this.option.joinColumn
+      if (this.isBlank(columnName)) {
+        console.warn('在joinEntity中查找joinColumn属性失败,会导致查询详情和删除失败')
+        columnName = ''
+      }
+      return columnName
+    },
+    // 提交表单中所有的列,因部分翻译字段,表格中和表单中的key不一样,field editField不全,这里补全下,
+    formFields() {
+      if (this.option.columns == null) {
+        return []
+      }
+      var fields = this.deepClone(this.option.columns)
+      fields = fields.map((item) => {
+        if (this.isBlank(item['editField'])) {
+          item['editField'] = item['field']
+        }
+        // 没有设定分组的,全部补全成 其它信息
+        if (this.isBlank(item['group'])) {
+          item['group'] = '其它'
+        }
+        return item
+      })
+      return fields
+    },
+    // 卡片设定中一行显示几列,每列的col数
+    cardRowColSpan() {
+      return 24 / this.cardRowColNum
+    },
+    // 如果表单内容较多,启用了分组,这里先算出所有的分组
+    columnGroups() {
+      if (this.isBlank(this.formFields)) {
+        return []
+      } else {
+        // 找出所有hide != true的关联表
+        var groups = this.formFields
+          .map((item) => {
+            return item['group']
+          })
+          .filter((currentValue, index, arr) => {
+            return arr.indexOf(currentValue) == index
+          })
+        return groups
+      }
+    },
+    /* {
+      '分组1': [{column1.1}],
+      '分组2': [{column2.1}],
+    }*/
+    groupFormFields() {
+      if (this.showDialog) {
+        // 将每个分组初始化
+        var groupFormFields = {}
+        this.columnGroups.forEach((value, index, array) => {
+          groupFormFields[value] = []
+        })
+        // 将所有编辑列,按分组存放
+        this.formFields.forEach((item, index, array) => {
+          groupFormFields[item['group']].push(item)
+        })
+        return groupFormFields
+      }
+      return []
+    },
+    groupModel() {
+      return this.columnGroups.length > 1
+    },
+  },
+  created() {
+    // 如果表单,是做为主表的编辑页面,如果是通过v-model传递进来的值,通过监听value,更新this.editForm
+    this.$watch(
+      function () {
+        return this.value
+      },
+      function (newVal, oldVal) {
+        this.editForm = newVal
+        // 通过v-model传递值进来时,说明当前form是主表信息
+        this.formChange()
+      }
+    )
+    // 如果表单,是做为子表的编辑页面,当relateData中的关联字段发生更新时,触发查询,比如在goods详情中,goodsCode
+    this.$watch(
+      function () {
+        return this.relateData[this.joinColumn]
+      },
+      function (newVal, oldVal) {
+        // 如果是父组件(弹出框)关闭时,设置this.relateData = {}时触发,清空本组件的数据
+        if (this.isBlank(this.relateData)) {
+          this.cardRowColNum = 2
+          this.editForm = {}
+          return
+        }
+        // 如果是关联字段发生更新,触发查询
+        if (this.isNotBlank(newVal)) {
+          this.queryDetail()
+        }
+      }
+    )
+  },
+  mounted() {
+    // 如果表单,是做为主表的编辑页面,如果是通过v-model传递进来的值
+    if (this.isNotBlank(this.value)) {
+      this.editForm = this.value
+      this.formChange()
+    }
+    // 如果表单,是做为子表的编辑页面,首次打开时,根据关联属性,加载详情数据
+    if (this.isNotBlank(this.relateData) && this.isNotBlank(this.relateData[this.joinColumn])) {
+      this.queryDetail()
+    }
+  },
+  methods: {
+    // 该行是否显示 true/false/ 'hideOnAdd hideOnView hideOnEdit'
+    fieldIsHide(editHide) {
+      if (typeof editHide == 'boolean') {
+        return editHide
+      }
+      if (typeof editHide == 'string') {
+        if (this.modelType == 'add') {
+          return editHide.indexOf('hideOnAdd') >= 0
+        }
+        if (this.modelType == 'view') {
+          return editHide.indexOf('hideOnView') >= 0
+        }
+        if (this.modelType == 'edit') {
+          return editHide.indexOf('hideOnEdit') >= 0
+        }
+      }
+      return false
+    },
+    // 该行是否禁用 true/false/ 'disableOnAdd disableOnView disableOnEdit'
+    fieldIsDisable(disable) {
+      if (typeof disable == 'boolean') {
+        return disable
+      }
+      if (typeof disable == 'string') {
+        if (this.modelType == 'add') {
+          return disable.indexOf('disableOnAdd') >= 0
+        }
+        if (this.modelType == 'view') {
+          return disable.indexOf('disableOnView') >= 0
+        }
+        if (this.modelType == 'edit') {
+          return disable.indexOf('disableOnEdit') >= 0
+        }
+      }
+      return false
+    },
+    // 设置一行显示几列
+    handleSetRowColNum(num) {
+      this.cardRowColNum = num
+      this.$emit('changeRowColNum', num)
+    },
+
+    async queryDetail() {
+      var queryParams = this.relateData
+      const { data, code } = await this.option.buttons.queryByPrimarykey.api(queryParams)
+      if (code != '200') return
+      this.editForm = data
+      this.formChange()
+    },
+    // 校验表单
+    validate(callback) {
+      this.$refs.editForm.validate(async (valid, obj) => {
+        if (callback != null) {
+          callback(valid)
+        }
+      })
+    },
+    handleSave(callback) {
+      this.$refs.editForm.validate(async (valid, obj) => {
+        if (valid) {
+          if (this.modelType == 'add') {
+            // 当edit-from是作为关联子表的界面,补全关联属性
+            if (typeof this.option.beforeInsert == 'function') {
+              this.option.beforeInsert(this.relateData, this.editForm)
+            }
+            const { code, message } = await this.option.buttons.add.api(this.editForm)
+            if (code == '200') {
+              if (callback != null) {
+                callback()
+              }
+            } else {
+              console.log(`提交表单调用新增接口失败:${message}`)
+            }
+          } else {
+            // 当edit-from是作为关联子表的界面,补全关联属性
+            if (typeof this.option.beforeUpdate == 'function') {
+              this.option.beforeUpdate(this.relateData, this.editForm)
+            }
+            const { code, message } = await this.option.buttons.edit.api(this.editForm)
+            if (code == '200') {
+              if (callback != null) {
+                callback()
+              }
+            } else {
+              console.log(`提交表单调用更新接口失败:${message}`)
+            }
+          }
+        } else {
+          console.log('表单校验失败')
+        }
+      })
+    },
+    // 表单任何一个变动时,通知外部v-model
+    formChange(fieldName, fieldVal, fieldExtend) {
+      this.$emit('input', this.editForm)
+      // 表单变动后,回调option中的formChange事件
+      if (typeof this.option.formChange == 'function') {
+        this.option.formChange(this.editForm, fieldName, fieldVal, fieldExtend)
+      }
+    },
+  },
+}
+</script>
+
+<style scoped lang="scss">
+.input_tips {
+  position: absolute;
+  margin-top: -8px;
+  .svg-icon {
+    font-size: 20px;
+    color: rgb(71, 8, 8);
+  }
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-crud/edit-table.vue b/report-ui/src/components/AnjiPlus备份/anji-crud/edit-table.vue
new file mode 100644
index 00000000..df3b030b
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-crud/edit-table.vue
@@ -0,0 +1,498 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-5-4 11:04:24
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-5-6 11:04:24
+ !-->
+<template>
+  <div v-if="option['hide'] == null || option['hide'] == false"
+       class="anji-card">
+    <div class="card-head">{{ option.title }}</div>
+    <div class="card-body">
+      <el-form ref="form"
+               :model="form">
+        <!-- 表格开始 -->
+        <el-table :data="formRecordsUndelete"
+                  border
+                  :row-class-name="tableRowClassAdapter"
+                  @selection-change="handleSelectionChange"
+                  @row-click="handleTableRowClick">
+          <!-- <el-table-column fixed type="selection" width="50" align="center" /> -->
+
+          <el-table-column label="序号"
+                           min-width="50"
+                           align="center">
+            <template slot-scope="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+
+          <template v-for="item in option.columns">
+            <el-table-column v-if="fieldIsHide(item.tableHide) != true && item.columnType != 'expand'"
+                             :key="item.field"
+                             :label="item.label"
+                             :min-width="item.minWidth || 110"
+                             align="center">
+              <template slot-scope="scope">
+                <el-form-item :prop="'records.' + scope.$index + '.' + item.field"
+                              :rules="item.rules">
+                  <!-- 输入框 -->
+                  <el-input v-if="item.inputType == 'input'"
+                            v-model="scope.row[item.field]"
+                            size="small"
+                            :placeholder="item.placeholder || '请输入'"
+                            :clearable="item.clearable !== false"
+                            :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                            @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <!-- 开关 -->
+                  <el-switch v-else-if="item.inputType == 'switch'"
+                             v-model="scope.row[item.field]"
+                             :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                             :active-value="item.switchOption.disableValue"
+                             :inactive-value="item.switchOption.enableValue"
+                             @change="(value) => tableRowChange(scope.$index, item.field, value)"
+                             active-color="#5887fb"
+                             inactive-color="#ccc">
+                  </el-switch>
+                  <el-input v-else-if="item.inputType == 'input-number'"
+                            v-model="scope.row[item.field]"
+                            size="small"
+                            :min="item.inputNumberOption.min"
+                            :max="item.inputNumberOption.max"
+                            :placeholder="item.placeholder || '请输入'"
+                            :clearable="item.clearable !== false"
+                            :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                            @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+
+                  <!-- 自定义input -->
+                  <anji-input v-else-if="item.inputType == 'anji-input'"
+                              v-model.trim="scope.row[item.field]"
+                              :default-value="item.defaultValue"
+                              :unit="item.anjiInput.unit"
+                              :conversion="item.anjiInput.conversion"
+                              :placeholder="item.placeholder || '请输入'"
+                              :clearable="item.clearable !== false"
+                              :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                              @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+
+                  <!-- 下拉框 -->
+                  <anji-select v-else-if="item.inputType == 'anji-select'"
+                               v-model.trim="scope.row[item.field]"
+                               :multiple="item.anjiSelectOption.multiple"
+                               :default-value="item.defaultValue"
+                               :dict-code="item.anjiSelectOption.dictCode"
+                               :url="item.anjiSelectOption.url"
+                               :method="item.anjiSelectOption.method"
+                               :query-param="item.anjiSelectOption.queryParam"
+                               :option="item.anjiSelectOption.option"
+                               :label="item.anjiSelectOption.label"
+                               :disabled-options="item.anjiSelectOption.disabledOptions"
+                               :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                               @change="(value, option) => tableRowChange(scope.$index, item.field, value, option)" />
+                  <!-- 日期时间框  -->
+                  <el-date-picker v-else-if="item.inputType.indexOf('date') >= 0"
+                                  v-model="scope.row[item.field]"
+                                  style="width: 100%"
+                                  :placeholder="item.placeholder || '请选择'"
+                                  :type="item.inputType"
+                                  :clearable="item.clearable !== false"
+                                  :disabled="saveButtonStatus[scope.$index] == 'inShow' || item.disabled"
+                                  @change="(value) => tableRowChange(scope.$index, item.field, value)" />
+                  <!-- 待扩展的表单类型,请自行扩展 -->
+                  <el-input v-else
+                            placeholder="组件不支持此类型表单请至组件内部自行扩展"
+                            disabled />
+                </el-form-item>
+              </template>
+            </el-table-column>
+          </template>
+          <el-table-column fixed="right"
+                           label="操作"
+                           width="100">
+            <template slot-scope="scope">
+              <el-button type="text"
+                         size="small"
+                         @click="handleAddOrUpdate(scope.row, scope.$index)">{{ getRowEditButton(scope.$index) }}</el-button>
+              <el-button type="text"
+                         size="small"
+                         @click="handleDelete(scope.row, scope.$index)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 表格结束 -->
+      </el-form>
+      <button v-if="modelType != 'view'"
+              class="table-add-row-button"
+              @click="handleAdd">
+        <i class="el-icon-plus" />
+        <span>新增</span>
+      </button>
+    </div>
+  </div>
+</template>
+
+<script>
+const ROW_DELETE_FLAG = 'deletedFlag'
+export default {
+  components: {},
+  props: {
+    modelType: String, // add view edit
+    option: {
+      // 界面渲染相关配置json
+      type: [Object],
+      default: () => {
+        return {
+          title: '', // 页面标题
+          labelWidth: '',
+          queryFormFields: [], // 查询表单条件
+          buttons: {
+            // 按钮
+            query: {},
+            edit: {},
+            delete: {},
+            add: {},
+          },
+          columns: [], // 表格列
+        }
+      },
+    },
+    relateData: {
+      // 关联的主记录
+      type: [Object],
+      default: () => {
+        return {}
+      },
+    },
+    value: {
+      type: [Array],
+      default: () => {
+        return []
+      },
+    },
+    valueNew: {
+      type: [Array],
+      default: () => {
+        return []
+      },
+    }
+  },
+  data () {
+    return {
+      checkRecords: [], // 表格中当前选中的记录
+
+      form: {
+        records: [], // 接口返回的记录列表
+        total: 0, // 接口返回的总条数
+      },
+
+      saveButtonStatus: [], // 维护表格中每行编辑按钮的状态 inShow inEditing inAdding
+
+      rowIdList: []
+    }
+  },
+  computed: {
+    // 主键的列名
+    primaryKeyFieldName () {
+      var primaryKey = this.option.columns.find((item) => item['primaryKey'] == true)
+      if (primaryKey != null) {
+        return primaryKey['field']
+      } else {
+        return null
+        console.warn('在columns中查找primaryKey=true失败,会导致查询详情和删除失败')
+      }
+    },
+    // 指定当前实体关联主表的关联字段,孙子关联表,没有该属性
+    joinColumn () {
+      var columnName = this.option.joinColumn
+      if (this.isBlank(columnName)) {
+        console.warn('在columns中查找关联字段失败,会导致查询详情和删除失败,孙子关联表忽略该错误')
+        columnName = ''
+      }
+      return columnName
+    },
+    // 未删除的记录
+    formRecordsUndelete () {
+      if (this.form.records == null) {
+        return []
+      }
+      return this.form.records.filter((item) => item[ROW_DELETE_FLAG] == null || item[ROW_DELETE_FLAG] == false)
+    },
+  },
+  watch: {},
+  created () {
+    // 主表 relateData 的关联字段 joinColumn 变动时,触发查询子表的查询,孙子关联儿子的无效
+    if (this.isNotBlank(this.joinColumn)) {
+      this.$watch(
+        function () {
+          return this.relateData[this.joinColumn]
+        },
+        function (newVal, oldVal) {
+          // 如果是关联字段发生更新,触发查询
+          if (this.isNotBlank(newVal)) {
+            this.handleQueryPageList(newVal)
+          } else {
+            // 如果关联字段为空,清空本表格的数据,如果是父组件(弹出框)关闭时,设置this.relateData = {默认值}时触发
+            this.checkRecords = []
+            this.form.records = []
+            this.form.total = 0
+            this.saveButtonStatus = []
+          }
+        }
+      )
+    }
+  },
+  mounted () {
+    // 首次打开时,根据主表关联字段查询子表,加载表格数据
+    if (this.isNotBlank(this.relateData) && this.isNotBlank(this.relateData[this.joinColumn])) {
+      this.handleQueryPageList()
+    }
+  },
+  methods: {
+    // 该行是否显示 true/false/ 'hideOnAdd hideOnView hideOnEdit'
+    fieldIsHide (tableHide) {
+      if (typeof tableHide == 'boolean') {
+        return tableHide
+      }
+      if (typeof tableHide == 'string') {
+        if (this.modelType == 'add') {
+          return tableHide.indexOf('hideOnAdd') >= 0
+        }
+        if (this.modelType == 'view') {
+          return tableHide.indexOf('hideOnView') >= 0
+        }
+        if (this.modelType == 'edit') {
+          return tableHide.indexOf('hideOnEdit') >= 0
+        }
+      }
+      return false
+    },
+    // 获取行的提交按钮文字
+    getRowEditButton (index) {
+      if (this.saveButtonStatus[index] == 'inEditing') {
+        return 'btn_savetemp'
+      } else if (this.saveButtonStatus[index] == 'inAdding') {
+        return 'btn_savetemp'
+      } else if (this.saveButtonStatus[index] == 'inShow') {
+        return 'btn_edit'
+      } else {
+        return 'not_permission'
+      }
+    },
+    // 表格行渲染前前置处理
+    tableRowClassAdapter ({ row, rowIndex }) {
+      row.index = rowIndex
+    },
+    // 查询
+    async handleQueryPageList (joinColumnValue) {
+      if (this.isBlank(joinColumnValue)) {
+        joinColumnValue = this.relateData[this.joinColumn]
+      }
+      var params = {}
+      params[this.joinColumn] = joinColumnValue
+      this.queryPageList(params)
+    },
+    // 暴露给外部直接调用带参数
+    async queryPageList (params) {
+      // 默认的排序
+      if (this.isNotBlank(this.option.buttons.query.order)) {
+        params['sort'] = this.option.buttons.query.sort
+        params['order'] = this.option.buttons.query.order
+      }
+      const { data, code } = await this.option.buttons.query.api(params)
+      if (code != '200') return
+      this.form.records = data.records
+      this.form.total = data.total
+      this.$emit('input', this.form.records)
+      for (var i = 0; i < this.form.total; i++) {
+        this.saveButtonStatus.push('inShow')
+      }
+    },
+    // 选择项改变时
+    handleSelectionChange (val) {
+      this.checkRecords = val
+    },
+    // 表格选中某一行时
+    handleTableRowClick (row, column, event) {
+      // console.log(row)
+      // console.log(column)
+      // 行点击后,回调option中的tableRowClick事件
+      if (typeof this.option.tableRowClick == 'function') {
+        this.option.tableRowClick(this.form.records, row, row.index, this.relateData)
+      }
+    },
+    // 行数据更新时回调
+    tableRowChange (rowIndex, fieldName, fieldVal, fieldExtend) {
+      // 通知外面的组件
+      this.$emit('input', this.form.records)
+
+      // 表单变动后,回调option中的tableRowChange事件
+      if (typeof this.option.tableChange == 'function') {
+        this.option.tableChange(this.form.records, rowIndex, fieldName, fieldVal, fieldExtend, this.relateData)
+      }
+    },
+    // 新增
+    handleAdd () {
+      this.saveButtonStatus.push('inAdding')
+      this.form.records.push({})
+    },
+    // 父节点Change,子节点表格更新
+    handleUpdata () {
+      this.saveButtonStatus = []
+      this.form.records = []
+    },
+    // 提交和修改
+    handleAddOrUpdate (row, index) {
+      // 编辑状态下点击保存提交
+      if (this.saveButtonStatus[index] == 'inEditing' || this.saveButtonStatus[index] == 'inAdding') {
+        this.handleSaveTemp(row, index)
+      } else {
+        this.$set(this.saveButtonStatus, index, 'inEditing')
+      }
+    },
+    // 校验表单
+    validate (callback) {
+      this.$refs['form'].validate(async (valid, obj) => {
+        if (callback != null) {
+          callback(valid)
+        }
+      })
+    },
+    // 暂存
+    async handleSaveTemp (row, index) {
+      this.$refs['form'].validate((valid) => {
+        if (valid) {
+          if (this.isBlank(row[this.primaryKeyFieldName])) {
+            // 补全关联属性
+            if (typeof this.option.beforeInsert == 'function') {
+              this.option.beforeInsert(this.relateData, row)
+            }
+          } else {
+            // 补全关联属性
+            if (typeof this.option.beforeUpdate == 'function') {
+              this.option.beforeUpdate(this.relateData, row)
+            }
+          }
+          // 将行按钮的文字改成编辑
+          this.$set(this.saveButtonStatus, index, 'inShow')
+          // 通知外面的组件
+          this.$emit('input', this.form.records)
+        }
+      })
+      /*
+      this.$refs['form'].validate((valid) => {
+        if (valid) {
+          // 验证通过
+          if (this.isBlank(row[this.primaryKeyFieldName])) {
+            // 补全关联属性
+            if (typeof this.option.beforeInsert == 'function') {
+              this.option.beforeInsert(this.relateData, row)
+            }
+            // 主键为空,新增
+            this.option.buttons.add.api(row).then((response) => {
+              if (response.code == 200) {
+                this.$set(this.saveButtonStatus, index, 'inShow')
+                // 保存完成后刷新列表
+                this.handleQueryPageList()
+              }
+            })
+          } else {
+            // 补全关联属性
+            if (typeof this.option.beforeUpdate == 'function') {
+              this.option.beforeUpdate(this.relateData, row)
+            }
+            // 修改
+            this.option.buttons.edit.api(row).then((response) => {
+              if (response.code == 200) {
+                this.$set(this.saveButtonStatus, index, 'inShow')
+                // 保存完成后刷新列表
+                this.handleQueryPageList()
+              }
+            })
+          }
+        } else {
+          console.log('valid fail')
+          return false
+        }
+      })
+      */
+    },
+    // 删除
+    handleDelete (row, index) {
+      this.saveButtonStatus.splice(index, 1) // 清空状态
+      // 界面上临时新增出来的一行,还没有提交到数据库,可以直接删除
+      if (this.saveButtonStatus[index] == 'inAdding') {
+        this.form.records.splice(index, 1)
+        this.saveButtonStatus.splice(index, 1)
+        this.$emit('input', this.form.records)
+        return
+      }
+      // if (this.isBlank(row) || this.isBlank(row[this.primaryKeyFieldName])) {
+      //   return
+      // }
+      // 将对应的行标识成删除
+      // 找出该行在原始记录中的index
+      // var realIndex = this.form.records.findIndex((item) => item[this.primaryKeyFieldName] == row[this.primaryKeyFieldName])
+      // row[ROW_DELETE_FLAG] = true
+      // this.$set(this.form.records, realIndex, row)
+      // this.$emit('input', this.form.records)
+      this.form.records.splice(index, 1)
+      this.rowIdList.push(row.id)
+      this.$emit('input', this.form.records)
+      this.$emit('update:valueNew', this.rowIdList)
+      /*
+      之前是直接调用接口删除,后面统一改成在主表接口中增加、更新、删除
+      // 已经保存在数据库的的行,要调用删除按钮
+      var primaryKey = row[this.primaryKeyFieldName]
+      this.$confirm(this.$lang('promptMessage_deleteTip'), this.$lang('promptMessage_deleteTipTitle'), {
+        type: 'warning',
+        confirmButtonClass: 'delete_sure',
+        cancelButtonClass: 'el-button--danger is-plain',
+      })
+        .then(() => {
+          this.option.buttons.delete.api(primaryKey).then((res) => {
+            // {code: "200", message: "操作成功", data: true}
+            this.checkRecords = []
+            this.handleQueryPageList()
+          })
+        })
+        .catch((e) => {
+          e
+        })
+        */
+    },
+  },
+}
+</script>
+
+<style scoped lang="scss">
+.table-add-row-button {
+  width: 100%;
+  margin-top: 0px;
+  margin-bottom: 0px;
+  border-color: #d9d9d9;
+  border-style: dashed;
+  line-height: 1.499;
+  position: relative;
+  display: inline-block;
+  font-weight: 400;
+  white-space: nowrap;
+  text-align: center;
+  background-image: none;
+  border: 1px solid transparent;
+  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
+  cursor: pointer;
+  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  -webkit-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -ms-touch-action: manipulation;
+  touch-action: manipulation;
+  height: 32px;
+  padding: 0 15px;
+  font-size: 14px;
+  border-radius: 4px;
+  color: rgba(0, 0, 0, 0.65);
+  background-color: #fff;
+  border-color: #d9d9d9;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-crud/edit.vue b/report-ui/src/components/AnjiPlus备份/anji-crud/edit.vue
new file mode 100644
index 00000000..76bb6d6b
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-crud/edit.vue
@@ -0,0 +1,386 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-3-13 11:04:24
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-3-13 11:04:24
+ !-->
+<template>
+  <el-dialog
+    :width="dialogWidth"
+    :class="dialogFullScreen?'full-screen':'notfull-screen'"
+    :close-on-click-modal="false"
+    center
+    :visible.sync="showDialog"
+    :fullscreen="dialogFullScreen"
+    @close="handleCloseDialog('close')"
+  >
+    <template v-slot:title>
+      {{ option.title + '--' + modelType }}
+      <button
+        type="button"
+        aria-label="Close"
+        class="el-dialog__headerbtn"
+        style="right: 50px"
+        @click="dialogFullScreen = !dialogFullScreen"
+      >
+        <i class="el-dialog__close el-icon el-icon-full-screen" />
+      </button>
+    </template>
+
+    <!--主表详情信息-->
+    <component
+      :is="'EditForm'"
+      ref="mainForm"
+      v-model="saveForm"
+      :option="option"
+      :model-type="modelType"
+      :show-dialog="showDialog"
+      @changeRowColNum="handleSetRowColNum"
+    />
+
+    <!--关联表相关-->
+    <template v-for="(item, index) in joinEntitys">
+      <component
+        :is="getComponentByJoinType(item.joinType, item)"
+        :ref="'joinForm' + index"
+        :key="index"
+        v-model="saveForm[item.fieldNameInMainEntityOnSave]"
+        :value-new.sync="saveForm[item.fieldNameInMainEntityOnId]"
+        :option="item"
+        :model-type="modelType"
+        :relate-data="saveForm"
+      />
+
+      <!--孙子的关联表-->
+      <template v-for="(grandsonItem, grandsonIndex) in item.joinEntitys">
+        <component
+          :is="getComponentByJoinType(grandsonItem.joinType, grandsonItem)"
+          ref="grandsonForm"
+          :key="index + '.' + grandsonIndex"
+          :option="grandsonItem"
+          :model-type="modelType"
+          :relate-data="saveForm"
+        />
+      </template>
+    </template>
+
+    <!--自定义的卡片插槽-->
+    <slot name="customCard" />
+
+    <div
+      slot="footer"
+      style="text-align: center"
+    >
+      <slot
+        v-if="modelType =='edit'"
+        name="editBtn"
+        :rowData="rowData"
+      />
+      <el-button
+        type="danger"
+        plain
+        @click="handleCloseDialog('close')"
+      >关闭</el-button>
+      <el-button
+        v-if="modelType!='view'"
+        type="primary"
+        plain
+        @click="handleValidateAndSave"
+      >保存</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import EditForm from './edit-form'
+import EditTable from './edit-table'
+export default {
+  name: 'EditDialog',
+  components: { EditForm, EditTable },
+  props: {
+    visible: {
+      type: [Boolean],
+      default: () => {
+        return false
+      },
+    },
+    rowData: {
+      // 查询参数,对应表格那行数据
+      type: [Object],
+      default: () => {
+        return {}
+      },
+    },
+    modelType: String, // add view edit
+    option: {
+      require: true,
+      type: Object,
+      default: () => {
+        return {
+          title: '', // 页面标题
+          labelWidth: '', // 表单输入框label宽度
+          queryFormFields: [], // 查询表单条件
+          buttons: {
+            // 按钮
+            query: {},
+            edit: {},
+            delete: {},
+            add: {},
+          },
+          columns: [], // 表格列
+        }
+      },
+    },
+  },
+  data() {
+    return {
+      showDialog: false, // 编辑详情弹框是否显示
+      dialogFullScreen: false, // 弹出框全屏
+      cardRowColNum: 2, // 主信息一行显示几列
+
+      // 提交表单的数据
+      saveForm: {},
+      // 已成功校验的关联表单个数
+      countForValidJoinForm: 0,
+    }
+  },
+  computed: {
+    // 弹出框的宽度,根据一行显示几列动态调整
+    dialogWidth() {
+      if (this.cardRowColNum == 2) {
+        return '60%'
+      }
+      if (this.cardRowColNum == 3) {
+        return '70%'
+      }
+      if (this.cardRowColNum == 4) {
+        return '80%'
+      }
+      return '60%'
+    },
+    // 关联属性表
+    joinEntitys() {
+      if (this.isBlank(this.option.joinEntitys)) {
+        return []
+      } else {
+        return this.option.joinEntitys
+        // 找出所有hide != true的关联表
+        // var entitys = this.option.joinEntitys.filter((item) => item['hide'] == null || item['hide'] == false)
+        // return entitys
+      }
+    },
+    // 一对一关联表的个数
+    countJoinEntityOneToOne() {
+      var entitys = this.joinEntitys.filter((item) => item['joinType'] == 'OneToOne')
+      if (entitys == null) {
+        return 0
+      }
+      return entitys.length
+    },
+  },
+  watch: {
+    // 监控dialog的显示隐藏变量
+    visible(newValue, oldValue) {
+      this.showDialog = newValue
+      // 为主表的编辑表单,渲染上默认值
+      this.initDefaultSaveForm()
+    },
+    rowData(newValue, oldValue) {
+      if (newValue != null) {
+        this.queryByPrimarykey(newValue)
+      }
+    },
+  },
+  mounted() {
+    // 为主表的编辑表单,渲染上默认值
+    this.initDefaultSaveForm()
+  },
+  methods: {
+    // 暴露给外部crud页面,回传saveForm的值
+    getSaveForm() {
+      return this.saveForm
+    },
+    setSaveForm(saveForm) {
+      this.saveForm = saveForm
+    },
+    initDefaultSaveForm() {
+      // saveForm的默认值
+      var defaultSaveForm = {}
+      this.option.columns.forEach((item) => {
+        var key = item.editField
+        if (this.isBlank(key)) {
+          key = item.field
+        }
+        var val = item.defaultValue
+        if (this.isNotBlank(val)) {
+          defaultSaveForm[key] = val
+        }
+      })
+      // 为主表的编辑表单,渲染上默认值
+      this.saveForm = this.deepClone(defaultSaveForm)
+      console.log('编辑框默认值:' + JSON.stringify(this.saveForm))
+    },
+    handleCloseDialog(val) {
+      // 为主表的编辑表单,渲染上默认值
+      this.initDefaultSaveForm()
+      this.showDialog = false, // 编辑详情弹框是否显示
+      this.dialogFullScreen = false, // 弹出框全屏
+      this.cardRowColNum = 2, // 主信息一行显示几列
+      this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
+      this.$emit('closeEvent', val)
+    },
+    // 设置一行显示几列
+    handleSetRowColNum(num) {
+      this.cardRowColNum = num
+    },
+    // 根据关联类型计算组件
+    getComponentByJoinType(type, item) {
+      if (type == 'OneToOne') {
+        return 'EditForm'
+      } else if (type == 'OneToMany') {
+        return 'EditTable'
+      } else {
+        return ''
+      }
+    },
+    async queryByPrimarykey(rowData) {
+      const { data, code } = await this.option.buttons.queryByPrimarykey.api(rowData)
+      if (code != '200') return
+      this.showDialog = true
+      this.saveForm = data
+    },
+    // 保存前,先调用校验
+    handleValidateAndSave() {
+      this.countForValidJoinForm = 0
+      // 主表单校验
+      this.$refs.mainForm.validate((mainValid) => {
+        if (mainValid == false) {
+          console.warn('主表单校验失败')
+          return
+        }
+        console.log('主表单校验完成')
+        if (this.joinEntitys == null || this.joinEntitys.length == 0) {
+          // 如果子表没有信息,直接提交
+          this.handleSave()
+          return
+        }
+        for (var i = 0; i < this.joinEntitys.length; i++) {
+          console.log(`开始校验子表单-${i} 校验`)
+          var item = this.joinEntitys[i]
+          if (this.$refs['joinForm' + i] == null || item.hide == true || this.saveForm[item.fieldNameInMainEntityOnSave] == null || this.saveForm[item.fieldNameInMainEntityOnSave].length == 0) {
+            console.log('子表单没有数据,直接跳过')
+            this.countForValidJoinForm++
+            console.log('已经校验的子表单:' + this.countForValidJoinForm + ' 共:' + this.joinEntitys.length)
+            // 所有关联表单校验通过
+            if (this.countForValidJoinForm == this.joinEntitys.length) {
+              console.log('子表单校验完成,提交主表单')
+              this.handleSave()
+            }
+            continue
+          }
+          var joinForm = this.$refs['joinForm' + i]
+          if (toString.call(joinForm) == '[object Array]') {
+            joinForm = joinForm[0]
+          }
+          joinForm.validate((joinValid) => {
+            if (joinValid) {
+              this.countForValidJoinForm++
+              console.log('已经校验的子表单:' + this.countForValidJoinForm + ' 共:' + this.joinEntitys.length)
+              // 所有关联表单校验通过
+              if (this.countForValidJoinForm == this.joinEntitys.length) {
+                console.log('子表单校验完成,提交主表单')
+                this.handleSave()
+              }
+            } else {
+              console.warn(`子表单${i}校验失败:`)
+            }
+          })
+        }
+      })
+    },
+    async handleSave() {
+      // 新增
+      if (this.modelType == 'add') {
+        const { code, message } = await this.option.buttons.add.api(this.saveForm)
+        if (code == '200') {
+          // 保存结束,关闭对话框
+          this.handleCloseDialog()
+          // 向外层发关闭事件
+          this.$emit('closeEvent')
+          return
+        } else {
+          this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
+          console.log(`提交表单调用新增接口失败:${message}`)
+        }
+      }
+      // 修改
+      if (this.modelType == 'edit') {
+        // console.log(this.$slots.customCard[0].context.$refs.cardInEditData.updateData.settingValue)
+        const { code, message } = await this.option.buttons.edit.api(this.saveForm)
+        if (code == '200') {
+          // 保存结束,关闭对话框
+          this.handleCloseDialog()
+          // 向外层发关闭事件
+          this.$emit('closeEvent')
+          return
+        } else {
+          this.countForValidJoinForm = 0, // 已成功校验的关联表单个数
+          console.log(`提交表单调用更新接口失败:${message}`)
+        }
+      }
+      /* 分步提交
+      // 提交主表
+      this.$refs.mainForm.handleSave(() => {
+        // 如果没有一对一的关联表,直接关闭对话框
+        if (this.countJoinEntityOneToOne == 0) {
+          // 保存结束,关闭对话框
+          this.handleCloseDialog()
+          // 向外层发关闭事件
+          this.$emit('closeEvent')
+          return
+        }
+        // 主表保存成功后,保存子表
+        for (var i = 0; i < this.joinEntitys.length; i++) {
+          if (this.joinEntitys[i].joinType == 'OneToOne') {
+            this.$refs['joinForm' + i].handleSave(() => {
+              this.countForSavedOneToOneJoinEntity++
+              if (this.countForSavedOneToOneJoinEntity == this.countJoinEntityOneToOne) {
+                // 保存结束,关闭对话框
+                this.handleCloseDialog()
+                // 向外层发关闭事件
+                this.$emit('closeEvent')
+              }
+            })
+          }
+        }
+      })*/
+    },
+  },
+}
+</script>
+
+<style scoped lang="scss">
+// /deep/.el-dialog__body {
+//   background-color: rgb(240, 242, 245);
+//   padding: 5px;
+//   max-height: 60vh;
+//   overflow: auto;
+//   height: calc(100vh - 100px);
+// }
+.notfull-screen {
+  /deep/.el-dialog__body {
+    background-color: rgb(240, 242, 245);
+    padding: 5px;
+    max-height: 60vh;
+    overflow: auto;
+  }
+}
+.full-screen {
+  /deep/.el-dialog__body {
+    background-color: rgb(240, 242, 245);
+    padding: 5px;
+    height: calc(100vh - 110px);
+    overflow: auto;
+  }
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-select.vue b/report-ui/src/components/AnjiPlus备份/anji-select.vue
new file mode 100644
index 00000000..7e952ffd
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-select.vue
@@ -0,0 +1,323 @@
+<!--element下拉列表封装
+  使用:
+   <hyb-select ref="hybSelect"
+   url="/v1/orgs"
+   v-model="search.orgId" option="orgId" label="orgName"
+   placeholder multiple localOptions></hyb-select>
+
+    1.url:要显示的下拉列表数据来源
+    规范:返回的数据要符合(JSON中对应的字段必须:label和value,不能其他)
+      [
+        {label:'显示1',value:'值1'},
+        {label:'显示2',value:'值2'},
+        {label:'显示3',value:'值3'},
+        ...
+      ]
+    2.v-model数据的双向绑定,绑定父组件中的字段
+    3.option自定义select的value对应的字段
+    4.label自定义select的label对应的字段
+    5.placeholder下拉列表提示
+    6.multiple是否是多选
+    7.localOptions使用本地的数据,不用请求远程数据,注意:使用该属性时Url属性不能要,不然无效
+-->
+<template>
+  <div>
+    <el-select
+      v-model="selectValue"
+      :clearable="clearable"
+      :collapse-tags="collapseTags"
+      filterable
+      class="filter-item"
+      :placeholder="placeholder"
+      :disabled="disabled"
+      :multiple="multiple == null ? false : true"
+      :remote="remoteFilter"
+      :remote-method="remoteQuery"
+      @change="change"
+    >
+      <el-option
+        v-for="(item, index) in options"
+        :key="index"
+        :label="getItemLabel(item, label)"
+        :value="item[option]"
+        :disabled="isDisabledOption(item)"
+      >
+        <template v-if="mergeLabel">
+          <span style="float: left">{{ getItemLabel(item, label) }}</span>
+          <span style="float: right; color: #8492a6; font-size: 13px">{{
+            item[option]
+          }}</span>
+        </template>
+      </el-option>
+      <el-option v-if="totalPage > 1" label="搜索更多" value="" disabled />
+    </el-select>
+  </div>
+</template>
+
+<script>
+import request from "@/utils/request";
+import { getStorageItem } from "@/utils/storage";
+export default {
+  props: {
+    dictCode: null, // 当传入dictCode时,可以不用传递url
+    url: null,
+    method: null,
+    queryParam: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    value: null,
+    placeholder: null,
+    label: {
+      type: String,
+      default: "text"
+    },
+    option: {
+      type: String,
+      default: "id"
+    },
+    multiple: null,
+    localOptions: null,
+    disabled: null,
+    clearable: {
+      type: Boolean,
+      default: true
+    },
+    collapseTags: {
+      type: Boolean,
+      default: false
+    },
+    mergeLabel: {
+      type: Boolean,
+      default: false
+    },
+    // 禁用的下拉选项
+    disabledOptions: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    // 使用远程搜索
+    remoteFilter: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      options: null,
+      selectValue: null,
+      // 如果是分页的,
+      totalPage: 0
+    };
+  },
+  computed: {
+    // 根据dictCode和url拼出最终的请求url
+    requestUrl() {
+      if (this.url != null && this.url.trim() != "") {
+        if (this.url.indexOf("?") > 0) {
+          if (this.option == null) {
+            console.log("url-" + this.url.substring(this.url.indexOf("?")));
+          }
+          if (this.label == null) {
+          }
+        }
+        return this.url;
+      }
+      if (this.dictCode != null && this.dictCode.trim() != "") {
+        return `/meta/gaeaDict/select/${this.dictCode}`;
+      }
+      return null;
+    }
+  },
+  watch: {
+    value: function(val, oldVal) {
+      if (this.multiple != null) {
+        if (!this.value) {
+          this.selectValue = [];
+        } else {
+          this.selectValue = this.value;
+        }
+      } else {
+        if (this.value != null && this.value != undefined) {
+          this.selectValue = this.value;
+        } else {
+          this.selectValue = "";
+        }
+      }
+    },
+    url() {
+      setTimeout(() => {
+        this.queryData();
+      }, 500);
+    }
+  },
+  created() {
+    if (this.multiple != null) {
+      this.selectValue = this.value;
+    } else {
+      if (this.value != null) {
+        this.selectValue = this.value;
+      }
+    }
+  },
+  mounted() {
+    if (this.requestUrl == null) {
+      this.options = this.localOptions;
+      return;
+    }
+    this.queryData();
+  },
+  methods: {
+    // 判断选择是否已经禁用
+    isDisabledOption(option) {
+      if (
+        option == null ||
+        this.disabledOptions == null ||
+        this.disabledOptions.length == 0
+      ) {
+        return false;
+      }
+      var currentOptionVal = option[this.option];
+      return this.disabledOptions.indexOf(currentOptionVal) >= 0;
+    },
+    change(value) {
+      if (value === "") {
+        value = null;
+      }
+      this.$emit("input", value);
+
+      // 根据当前值,找出对应的选项
+      var optionItem = this.options.find(item => item[this.option] == value);
+      this.$emit("change", value, optionItem);
+    },
+    // 根据用户配置的label,生成对应的标签
+    getItemLabel(item, label) {
+      if (label.indexOf("${") < 0 && label.indexOf("}" < 0)) {
+        return item[label];
+      }
+      var reg = /\$\{[a-zA-Z0-9]*\}/g;
+      var list = label.match(reg);
+      // ["${id}", "${text}"]
+      var result = label;
+      for (var i = 0; i < list.length; i++) {
+        var sub = list[i];
+        var key = sub.replace("${", "").replace("}", "");
+        result = result.replace(sub, item[key]);
+      }
+      return result;
+    },
+    // 从本地localStorage取 gaeaDict
+    getOptionsFromLocalStorage() {
+      var dicts = getStorageItem("gaeaDict");
+      var options = [];
+      if (!dicts.hasOwnProperty(this.dictCode)) {
+        return [];
+      }
+      var dictItems = dicts[this.dictCode];
+      for (var i = 0; i < dictItems.length; i++) {
+        var dictItem = dictItems[i];
+        options.push({ id: dictItem.id, text: dictItem.text });
+      }
+      return options;
+    },
+    queryData() {
+      // 所有从本地localStorage取,因为在App.vue中已经请求远程保存到本地了
+      var options = this.getOptionsFromLocalStorage();
+      if (this.isNotBlank(options)) {
+        this.options = options;
+        return;
+      }
+      // 本地localStorage取不到,再从远程接口取
+      if (this.requestUrl == null) {
+        return;
+      }
+      if (
+        this.method != null &&
+        this.method.toLocaleLowerCase().trim() == "post"
+      ) {
+        this.queryDataByPost();
+      } else {
+        this.queryDataByGet();
+      }
+    },
+    queryDataByGet(keyword) {
+      var param = this.deepClone(this.queryParam);
+      if (this.isNotBlank(keyword)) {
+        param["keyword"] = keyword;
+      }
+      param["multiple"] = this.multiple == null ? null : 1;
+      request({
+        url: this.requestUrl,
+        headers: { noPrompt: true },
+        params: param
+      }).then(response => {
+        this.setOptions(response.data);
+      });
+    },
+    queryDataByPost(keyword) {
+      var param = this.deepClone(this.queryParam);
+      if (this.isNotBlank(keyword)) {
+        param["keyword"] = keyword;
+      }
+      request({
+        url: this.requestUrl,
+        method: "post",
+        headers: { noPrompt: true },
+        data: param
+      }).then(response => {
+        this.setOptions(response.data);
+      });
+    },
+    setOptions(resData) {
+      if (resData == null || resData.length == 0) {
+        this.options = [];
+        this.totalPage = 0;
+        return;
+      }
+      if (this.isArray(resData)) {
+        this.options = resData;
+        this.totalPage = 1;
+        return;
+      }
+      if (
+        resData.records == null ||
+        resData.total == null ||
+        resData.pages == null
+      ) {
+        this.options = [];
+        return;
+      }
+      this.totalPage = resData.pages;
+      // resData.records
+      // resData.total
+      // resData.size
+      // resData.current
+      this.options = resData.records;
+    },
+    remoteQuery(keyword) {
+      if (this.isBlank(keyword)) {
+        return;
+      }
+      setTimeout(() => {
+        if (
+          this.method != null &&
+          this.method.toLocaleLowerCase().trim() == "post"
+        ) {
+          this.queryDataByPost(keyword);
+        } else {
+          this.queryDataByGet(keyword);
+        }
+      }, 200);
+    }
+  }
+};
+</script>
+<style scoped>
+.filter-item {
+  width: 100%;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-tree.vue b/report-ui/src/components/AnjiPlus备份/anji-tree.vue
new file mode 100644
index 00000000..dd767518
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-tree.vue
@@ -0,0 +1,99 @@
+<!--
+ * @Author: lide1202@hotmail.com
+ * @Date: 2021-5-4 11:04:24 
+ * @Last Modified by:   lide1202@hotmail.com
+ * @Last Modified time: 2021-5-5 11:04:24 
+ !-->
+<template>
+  <div>
+    <el-input class="filterInput" placeholder="搜索" v-model="filterText" v-if="enableFilter" />
+    <div class="title">{{ labelName }}</div>
+    <el-tree ref="table_tree" :data="treeData" node-key="id" :default-expand-all="isOpen" :expand-on-click-node="false" :filter-node-method="filterNode" @node-click="nodeClick" @check="checkedEvent" />
+  </div>
+</template>
+
+<script>
+import request from '@/utils/request'
+export default {
+  components: {},
+  props: {
+    url: {
+      type: [String],
+      default: () => {
+        return ''
+      },
+    },
+    id: {
+      type: [String],
+      default: () => {
+        return 'id'
+      },
+    },
+    label: {
+      type: [String],
+      default: () => {
+        return ''
+      },
+    },
+    value: {
+      type: [String],
+      default: () => {
+        return ''
+      },
+    },
+    labelName: String,
+    enableFilter: Boolean,
+    isOpen: Boolean,
+  },
+  data() {
+    return {
+      filterText: '',
+      treeData: [],
+    }
+  },
+  computed: {},
+  watch: {
+    filterText(val) {
+      this.$refs.table_tree.filter(val)
+    },
+  },
+  mounted() {
+    this.queryData()
+  },
+  methods: {
+    filterNode(val, data) {
+      if (!val) return true
+      return data.label.indexOf(val) !== -1
+    },
+    queryData() {
+      if (this.isBlank(this.url)) {
+        return
+      }
+      request({
+        url: this.url,
+        method: 'GET',
+      }).then((response) => {
+        if (response.code != '200') {
+          return
+        }
+        this.treeData = Object.prototype.toString.call(response.data) == '[object Array]' ? response.data : response.data.tree|| response.data.menuTree
+      })
+    },
+    // 点击tree节点时 将tree的id作为上级机构代码 查询列表
+    nodeClick(node) {
+      this.$emit('input', node['id'])
+      this.$emit('node-click', node['id'])
+    },
+    checkedEvent(item, evt) {
+      var ids = evt.checkedKeys.toString()
+      this.$emit('input', ids)
+    },
+  },
+}
+</script>
+
+<style scoped lang="scss">
+.filterInput {
+  margin-bottom: 20px;
+}
+</style>
diff --git a/report-ui/src/components/AnjiPlus备份/anji-upload.vue b/report-ui/src/components/AnjiPlus备份/anji-upload.vue
new file mode 100644
index 00000000..7b16bffe
--- /dev/null
+++ b/report-ui/src/components/AnjiPlus备份/anji-upload.vue
@@ -0,0 +1,161 @@
+<template>
+  <div class="uploadImage">
+    <el-upload
+      :headers="headers"
+      :limit="limit"
+      :action="requestUrl"
+      list-type="picture-card"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :on-remove="handleRemove"
+      :on-success="handleSuccess"
+      :show-file-list="true"
+      :before-upload="handleBeforeUpload"
+      :class="fileList && fileList.length >= limit ? 'hide_box' : ''"
+    >
+      <i class="el-icon-plus" />
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisibleImageUpload" :modal="false">
+      <img width="100%" :src="imageUploadUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { getToken } from "@/utils/auth"; // get token from cookie
+export default {
+  props: {
+    upLoadUrl: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    },
+    viewUrl: {
+      type: String,
+      default: () => {
+        return process.env.BASE_API + "/file/download/";
+      }
+    },
+    limit: {
+      type: Number,
+      default: () => {
+        return 1;
+      }
+    },
+    value: {
+      type: String,
+      default: () => {
+        return "";
+      }
+    }
+  },
+  data() {
+    return {
+      imageUploadUrl: "",
+      dialogVisibleImageUpload: false,
+      fileList: []
+    };
+  },
+  computed: {
+    requestUrl() {
+      if (this.upLoadUrl != null && this.upLoadUrl.trim() != "") {
+        return process.env.BASE_API + this.upLoadUrl;
+      } else {
+        return process.env.BASE_API + "/file/upload";
+      }
+    },
+    headers() {
+      return {
+        Authorization: getToken() // 直接从本地获取token就行
+      };
+    }
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.echoUpload(this.value);
+      },
+      immediate: true
+    }
+  },
+  mounted() {
+    this.echoUpload(this.value);
+  },
+  methods: {
+    handleRemove(file) {
+      this.fileList = [];
+      this.change();
+    },
+    handlePictureCardPreview(file) {
+      this.imageUploadUrl = file.url;
+      this.dialogVisibleImageUpload = true;
+    },
+    // 上传成功的回调
+    handleSuccess(response, file, fileList) {
+      if (response.code != 200) {
+        this.$message.error("上传失败");
+        return;
+      }
+      this.fileList.push({
+        url: file.response.data.urlPath
+      });
+      this.change();
+    },
+    // 回传出去
+    change() {
+      const fileList = (this.fileList.length > 0 && this.fileList[0].url) || "";
+      this.$emit("input", fileList);
+      this.$emit("change", fileList);
+    },
+    // 上传检验
+    handleBeforeUpload(file) {
+      const extension = file.name
+        .split(".")
+        [file.name.split(".").length - 1].toLowerCase();
+      // .png|.jpg|.gif|.icon|.pdf|.xlsx|.xls|.csv|.mp4|.avi
+      const extensionList = ["png", "jpg", "gif", "icon"];
+      if (extensionList.indexOf(extension) < 0) {
+        this.$message.warning("请上传正确的格式文件");
+        return false;
+      }
+      return true;
+    },
+    // 回显
+    echoUpload(val) {
+      if (!val) {
+        this.fileList = [];
+      } else {
+        const list = [{ url: val }];
+        this.fileList = list;
+      }
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.uploadImage .el-upload--picture-card {
+  width: 60px;
+  height: 60px;
+  line-height: 65px;
+}
+.uploadImage .el-upload-list__item {
+  width: 60px;
+  height: 60px;
+}
+.hide_box /deep/ .el-upload--picture-card {
+  display: none;
+}
+.el-upload-list__item {
+  line-height: 1.5;
+}
+.el-upload-list--picture-card .el-upload-list__item-actions {
+  text-align: left;
+  display: flex;
+  flex-wrap: wrap;
+}
+.imgBox,
+.iconFont {
+  width: 100% !important;
+  height: 100% !important;
+}
+</style>
diff --git a/report-ui/src/components/Breadcrumb/index.vue b/report-ui/src/components/Breadcrumb/index.vue
index 90fd9cec..b80dfae2 100644
--- a/report-ui/src/components/Breadcrumb/index.vue
+++ b/report-ui/src/components/Breadcrumb/index.vue
@@ -1,67 +1,81 @@
 <template>
   <el-breadcrumb class="app-breadcrumb" separator="/">
     <transition-group name="breadcrumb">
-      <el-breadcrumb-item v-for="(item,index) in levelListArr" :key="item.path">
-        <span v-if="item.redirect==='noredirect'||index==levelListArr.length-1" class="no-redirect">{{ item.meta.title }}</span>
-        <router-link v-else :to="item.redirect||item.path" class="no-redirect">{{ item.meta.title }}</router-link>
+      <el-breadcrumb-item
+        v-for="(item, index) in levelListArr"
+        :key="item.path"
+      >
+        <span
+          v-if="
+            item.redirect === 'noredirect' || index == levelListArr.length - 1
+          "
+          class="no-redirect"
+          >{{ item.meta.title }}</span
+        >
+        <router-link
+          v-else
+          :to="item.redirect || item.path"
+          class="no-redirect"
+          >{{ item.meta.title }}</router-link
+        >
       </el-breadcrumb-item>
     </transition-group>
   </el-breadcrumb>
 </template>
 
 <script>
-import pathToRegexp from 'path-to-regexp'
+import pathToRegexp from "path-to-regexp";
 
 export default {
   data() {
     return {
       levelList: null
-    }
+    };
   },
   watch: {
     $route() {
-      this.getBreadcrumb()
+      this.getBreadcrumb();
     }
   },
-  computed:{
-    levelListArr(){
-      return this.levelList.filter(item => item.meta && item.meta.title)
+  computed: {
+    levelListArr() {
+      return this.levelList.filter(item => item.meta && item.meta.title);
     }
   },
   created() {
-    this.getBreadcrumb()
+    this.getBreadcrumb();
   },
   methods: {
     getBreadcrumb() {
-      const { params } = this.$route
+      const { params } = this.$route;
       let matched = this.$route.matched.filter(item => {
         if (item.name) {
           // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
-          var toPath = pathToRegexp.compile(item.path)
-          item.path = toPath(params)
-          return true
+          let toPath = pathToRegexp.compile(item.path);
+          item.path = toPath(params);
+          return true;
         }
-      })
+      });
       // const first = matched[0]
       // if (first && first.name !== 'dashboard') {
       //   matched = [{ path: '/dashboard', meta: { title: 'Dashboard' }}].concat(matched)
       // }
-      this.levelList = matched
+      this.levelList = matched;
     }
   }
-}
+};
 </script>
 
 <style rel="stylesheet/scss" lang="scss" scoped>
-  .app-breadcrumb.el-breadcrumb {
-    display: inline-block;
-    font-size: 14px;
-    line-height: 50px;
-    margin-left: 10px;
-    color: #fff;
-    .no-redirect {
-      color: #333;
-      cursor: text;
-    }
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 50px;
+  margin-left: 10px;
+  color: #fff;
+  .no-redirect {
+    color: #333;
+    cursor: text;
   }
+}
 </style>
diff --git a/report-ui/src/components/Permission/permission.js b/report-ui/src/components/Permission/permission.js
index 623f3354..045bb1a2 100644
--- a/report-ui/src/components/Permission/permission.js
+++ b/report-ui/src/components/Permission/permission.js
@@ -2,7 +2,7 @@ import { hasPermission } from '@/utils/auth'
 export default {
   inserted(el, binding, vnode) {
     const { value } = binding // v-permission 绑定的权限值
-    var checkFlag = hasPermission(value)
+    let checkFlag = hasPermission(value)
     if(checkFlag == false && el.parentNode ){
       el.parentNode.removeChild(el)
     }
diff --git a/report-ui/src/components/codeSelect.vue b/report-ui/src/components/codeSelect.vue
index 4c94189c..b489f9ed 100644
--- a/report-ui/src/components/codeSelect.vue
+++ b/report-ui/src/components/codeSelect.vue
@@ -1,154 +1,171 @@
-/*
-* 使用方式 <code-select v-model="params.enableFlag" dictname="ENABLE_FLAG" @changed="handler" placeholder="启用状态" style="width: 120px;"/>
-* 根据/data/basecode.js中字典值,生成下拉列表
-* @property dictname ENABLE_FLAG
-* @property placeholder
-* @property style
-*/
+/* * 使用方式
+<code-select
+  v-model="params.enableFlag"
+  dictname="ENABLE_FLAG"
+  @changed="handler"
+  placeholder="启用状态"
+  style="width: 120px;"
+/>
+* 根据/data/basecode.js中字典值,生成下拉列表 * @property dictname ENABLE_FLAG *
+@property placeholder * @property style */
 
 <template>
-  <el-select :name="inputName" v-model="selectValue" :disabled="disabled" :placeholder="placeholder" :style="mystyle" clearable class="filter-item code-selected" @change="selectChange" @visible-change="drowShow">
-    <el-option v-for="(item,index) in optionList" :key="index" :label="item[label]" :value="item[valWord]" />
+  <el-select
+    :name="inputName"
+    v-model="selectValue"
+    :disabled="disabled"
+    :placeholder="placeholder"
+    :style="mystyle"
+    clearable
+    class="filter-item code-selected"
+    @change="selectChange"
+    @visible-change="drowShow"
+  >
+    <el-option
+      v-for="(item, index) in optionList"
+      :key="index"
+      :label="item[label]"
+      :value="item[valWord]"
+    />
   </el-select>
 </template>
 
 <script>
-  import request from '@/api/axios'
-  import { getStorageItem } from '@/utils/storage'
-  export default {
-    props: {
-      value: {
-        type: [String, Number],
-        default: ''
-      },
-      //用localStrage中的basecode生成下拉
-      dictname: {
-        type: String,
-        default: ''
-      },
-      //远程请求,生成下拉
-      remoteurl: {
-        type: String,
-        default: ''
-      },
-      remoteParams: {
-        type: Object,
-        default: ()=>{}
-      },
-      inputName: {
-        type: String,
-        default: ''
-      },
-      placeholder: {
-        type: String,
-        default: '请选择'
-      },
-      mystyle: {
-        type: String,
-        default: 'width: 100px'
-      },
-      disabled: {
-        type: Boolean,
-        default: false
-      },
-      label:{
-        type: String,
-        default: 'label'
-      },
-      valWord:{
-        type: String,
-        default: 'value'
-      },
+import request from "@/api/axios";
+import { getStorageItem } from "@/utils/storage";
+export default {
+  props: {
+    value: {
+      type: [String, Number],
+      default: ""
     },
-    data () {
-      return {
-        selectValue: '',
-        optionList: [],
-        dictList: [],
-      }
+    //用localStrage中的basecode生成下拉
+    dictname: {
+      type: String,
+      default: ""
     },
-    watch: {
-      value:{
-        handler(newValue, oldValue){
-          if (typeof newValue === 'string') {
-            this.selectValue = newValue
-          } else {
-            this.selectValue = this.parseString(newValue);
-          }
-        },
-        immediate: true
-      },
+    //远程请求,生成下拉
+    remoteurl: {
+      type: String,
+      default: ""
     },
-    computed: {},
-    created () {
-      if (this.dictname !== '') {
-        this.optionList = this.getListFromBaseCode()
-      }
-      if (this.remoteurl !== '') {
-        this.getListFromAjax()
-      }
+    remoteParams: {
+      type: Object,
+      default: () => {}
     },
-    mounted () {},
-    methods: {
-      getListFromBaseCode() {
-        let basecode = getStorageItem('queryForCodeSelect')
-        let list = []
-        if (!basecode.hasOwnProperty(this.dictname)) {
-          return []
-        }
-        this.dictList = basecode[this.dictname]
-        for (let i = 0; i < this.dictList.length; i++) {
-          var codeItem = this.dictList[i]
-          list.push({ 'value': codeItem.value.toString(), 'label': codeItem.label })
+    inputName: {
+      type: String,
+      default: ""
+    },
+    placeholder: {
+      type: String,
+      default: "请选择"
+    },
+    mystyle: {
+      type: String,
+      default: "width: 100px"
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    label: {
+      type: String,
+      default: "label"
+    },
+    valWord: {
+      type: String,
+      default: "value"
+    }
+  },
+  data() {
+    return {
+      selectValue: "",
+      optionList: [],
+      dictList: []
+    };
+  },
+  watch: {
+    value: {
+      handler(newValue, oldValue) {
+        if (typeof newValue === "string") {
+          this.selectValue = newValue;
+        } else {
+          this.selectValue = this.parseString(newValue);
         }
-        return list
-
       },
+      immediate: true
+    }
+  },
+  computed: {},
+  created() {
+    if (this.dictname !== "") {
+      this.optionList = this.getListFromBaseCode();
+    }
+    if (this.remoteurl !== "") {
+      this.getListFromAjax();
+    }
+  },
+  mounted() {},
+  methods: {
+    getListFromBaseCode() {
+      let basecode = getStorageItem("queryForCodeSelect");
+      let list = [];
+      if (!basecode.hasOwnProperty(this.dictname)) {
+        return [];
+      }
+      this.dictList = basecode[this.dictname];
+      for (let i = 0; i < this.dictList.length; i++) {
+        let codeItem = this.dictList[i];
+        list.push({ value: codeItem.value.toString(), label: codeItem.label });
+      }
+      return list;
+    },
 
-      getListFromAjax() {
-        //将url中的参数转换成json,提交
-        var reqData = {};
-        /*var params = this.remoteurl.slice(this.remoteurl.indexOf('?') + 1).split('&');
-        for (var i = 0; i < params.length; i++) {
-            var map = params[i].split('=');
-            var key = map[0];
-            var val = map[1];
+    getListFromAjax() {
+      //将url中的参数转换成json,提交
+      let reqData = {};
+      /*let params = this.remoteurl.slice(this.remoteurl.indexOf('?') + 1).split('&');
+        for (let i = 0; i < params.length; i++) {
+            let map = params[i].split('=');
+            let key = map[0];
+            let val = map[1];
             if(','.indexOf(val)>0 && val.split(',').length>1){
               val = val.split(',');
             }
             reqData[map[0]] = val;
         }*/
-        if(this.remoteParams != null){
-          reqData = this.remoteParams;
+      if (this.remoteParams != null) {
+        reqData = this.remoteParams;
+      }
+      request({
+        url: this.remoteurl,
+        method: "post",
+        data: reqData
+      }).then(response => {
+        if (response.repCode == "0000") {
+          this.optionList = response.repData;
         }
-        request({
-          url: this.remoteurl,
-          method: 'post',
-          data: reqData
-        }).then(response => {
-          if (response.repCode == '0000') {
-            this.optionList = response.repData
-          }
-        })
-      },
+      });
+    },
 
-      selectChange (val) {
-        this.$emit('input', val)
-        var list = this.optionList
-        for (var i in list) {
-          var item = list[i]
-          if (item[this.valWord] === val) {
-            this.$emit('changed', item)
-            break
-          }
+    selectChange(val) {
+      this.$emit("input", val);
+      let list = this.optionList;
+      for (let i in list) {
+        let item = list[i];
+        if (item[this.valWord] === val) {
+          this.$emit("changed", item);
+          break;
         }
-      },
-      drowShow (val) {
-        this.$emit('show', val)
-        this.$emit('click', val)
       }
+    },
+    drowShow(val) {
+      this.$emit("show", val);
+      this.$emit("click", val);
     }
   }
+};
 </script>
 
 <!--<style rel="stylesheet/scss" lang="less" scoped>-->
diff --git a/report-ui/src/components/verifition/utils/ase.js b/report-ui/src/components/verifition/utils/ase.js
index 4c1c5594..16af3ac0 100644
--- a/report-ui/src/components/verifition/utils/ase.js
+++ b/report-ui/src/components/verifition/utils/ase.js
@@ -4,8 +4,8 @@ import CryptoJS from 'crypto-js'
  * @keyWord String  服务器随机返回的关键字
  *  */
 export function aesEncrypt(word,keyWord="XwKsGlMcdPMEhR1B"){
-  var key = CryptoJS.enc.Utf8.parse(keyWord);
-  var srcs = CryptoJS.enc.Utf8.parse(word);
-  var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
+  let key = CryptoJS.enc.Utf8.parse(keyWord);
+  let srcs = CryptoJS.enc.Utf8.parse(word);
+  let encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
   return encrypted.toString();
 }
diff --git a/report-ui/src/components/verifition/utils/util.js b/report-ui/src/components/verifition/utils/util.js
index 55f65d42..7ac5b403 100644
--- a/report-ui/src/components/verifition/utils/util.js
+++ b/report-ui/src/components/verifition/utils/util.js
@@ -1,8 +1,8 @@
 export function resetSize(vm) {
-    var img_width, img_height, bar_width, bar_height;	//图片的宽度、高度,移动条的宽度、高度
+    let img_width, img_height, bar_width, bar_height;	//图片的宽度、高度,移动条的宽度、高度
 
-    var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
-    var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
+    let parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth
+    let parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight
 
     if (vm.imgSize.width.indexOf('%') != -1) {
         img_width = parseInt(this.imgSize.width) / 100 * parentWidth + 'px'
diff --git a/report-ui/src/filter/index.js b/report-ui/src/filter/index.js
index 202ac593..9685a2c9 100644
--- a/report-ui/src/filter/index.js
+++ b/report-ui/src/filter/index.js
@@ -1,3 +1,11 @@
+/*
+ * @Descripttion: 
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-12-11 14:48:27
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2021-12-13 10:15:29
+ */
 
 import { getStorageItem } from '@/utils/storage';
 // 字典
@@ -9,11 +17,11 @@ export function basecode(value, dicName) {
     return value
   }
 
-  var basecode = getStorageItem('queryForCodeSelect')
-  var dictList = basecode[dicName]
-  var dictLabel = value
-  for (var i = 0; i < dictList.length; i++) {
-    var codeItem = dictList[i]
+  let basecode = getStorageItem('queryForCodeSelect')
+  let dictList = basecode[dicName]
+  let dictLabel = value
+  for (let i = 0; i < dictList.length; i++) {
+    let codeItem = dictList[i]
     if(codeItem.value == value || codeItem.labelEng == value){
       dictLabel = codeItem.label
       break
@@ -25,9 +33,9 @@ export function basecode(value, dicName) {
 
 // 根据字典值获取数据
 export function getDataByCode(keyCode) {
-  var dict = basecode[this.keyCode]
+  let dict = basecode[this.keyCode]
   let list = []
-  for (var key in dict) {
+  for (let key in dict) {
       list.push({
           'value': key,
           'text': dict[key]
@@ -38,7 +46,7 @@ export function getDataByCode(keyCode) {
 
 //保留两位小数
 export function fixed(value){
-var intVal = null;
+let intVal = null;
 if(typeof value == 'string' && /^[0-9.]+$/.test(value)){
   intVal = parseInt(value);
 }
@@ -57,16 +65,16 @@ export function formatTimestamp(value) {
   if (value == null) {
     return ''
   }
-  var date = new Date(value)
-  var y = date.getFullYear()
-  var m = date.getMonth() + 1
+  let date = new Date(value)
+  let y = date.getFullYear()
+  let m = date.getMonth() + 1
   m = m < 10 ? ('0' + m) : m
-  var d = date.getDate()
+  let d = date.getDate()
   d = d < 10 ? ('0' + d) : d
-  var h = date.getHours()
+  let h = date.getHours()
   h = h < 10 ? ('0' + h) : h
-  var minute = date.getMinutes()
-  var second = date.getSeconds()
+  let minute = date.getMinutes()
+  let second = date.getSeconds()
   minute = minute < 10 ? ('0' + minute) : minute
   second = second < 10 ? ('0' + second) : second
   return y + '-' + m + '-' + d + ' ' + h + ':' + minute + ':' + second
@@ -78,11 +86,11 @@ export function formatDate(value) {
   if (value == null) {
     return ''
   }
-  var date = new Date(value)
-  var y = date.getFullYear()
-  var m = date.getMonth() + 1
+  let date = new Date(value)
+  let y = date.getFullYear()
+  let m = date.getMonth() + 1
   m = m < 10 ? ('0' + m) : m
-  var d = date.getDate()
+  let d = date.getDate()
   d = d < 10 ? ('0' + d) : d
   return y + '-' + m + '-' + d
 }
\ No newline at end of file
diff --git a/report-ui/src/mixins/access.js b/report-ui/src/mixins/access.js
index 40c76d5d..63e7819d 100644
--- a/report-ui/src/mixins/access.js
+++ b/report-ui/src/mixins/access.js
@@ -15,7 +15,7 @@ export default {
       return getToken()
     },
     getUser: function () {
-      var user = getAccessUser()
+      let user = getAccessUser()
       if (user != null) {
         return user;
       } else {
diff --git a/report-ui/src/mixins/common.js b/report-ui/src/mixins/common.js
index 34411f43..fb71f697 100644
--- a/report-ui/src/mixins/common.js
+++ b/report-ui/src/mixins/common.js
@@ -60,7 +60,7 @@ export default {
     },
     // 封装定制删除数组中的值
     contains (a, obj) {
-      var i = a.length
+      let i = a.length
       while (i--) {
         if (a[i] === obj) {
           return i
@@ -90,8 +90,8 @@ export default {
     },
     sortArray (propertyName) {
       return function (object1, object2) {
-        var value1 = object1[propertyName];
-        var value2 = object2[propertyName];
+        let value1 = object1[propertyName];
+        let value2 = object2[propertyName];
 
         if (value1 < value2) {
           return -1;
@@ -104,8 +104,8 @@ export default {
     },
     // 获取对象类型
     getObjectType (obj) {
-      var toString = Object.prototype.toString
-      var map = {
+      let toString = Object.prototype.toString
+      let map = {
         '[object Boolean]': 'boolean',
         '[object Number]': 'number',
         '[object String]': 'string',
@@ -146,7 +146,7 @@ export default {
         return val.trim() == ''
       }
       if (typeof val === 'object') {
-        for (var key in val) {
+        for (let key in val) {
           return false
         }
         return true
@@ -168,7 +168,7 @@ export default {
         list.forEach((ele) => {
           delete val[ele]
         })
-        for (var o in val) {
+        for (let o in val) {
           return false
         }
         return true
@@ -183,8 +183,8 @@ export default {
 
     // 对象深拷贝
     deepClone (data) {
-      var type = this.getObjectType(data)
-      var obj
+      let type = this.getObjectType(data)
+      let obj
       if (type === 'array') {
         obj = []
       } else if (type === 'object') {
@@ -194,7 +194,7 @@ export default {
         return data
       }
       if (type === 'array') {
-        for (var i = 0, len = data.length; i < len; i++) {
+        for (let i = 0, len = data.length; i < len; i++) {
           data[i] = (() => {
             if (data[i] === 0) {
               return data[i]
@@ -207,7 +207,7 @@ export default {
           obj.push(this.deepClone(data[i]))
         }
       } else if (type === 'object') {
-        for (var key in data) {
+        for (let key in data) {
           if (data) {
             delete data.$parent
           }
@@ -219,12 +219,12 @@ export default {
 
     // 合并json
     mergeObject () {
-      var target = arguments[0] || {}
-      var deep = false
-      var arr = Array.prototype.slice.call(arguments)
-      var i = 1
-      var options, src, key, copy
-      var isArray = false
+      let target = arguments[0] || {}
+      let deep = false
+      let arr = Array.prototype.slice.call(arguments)
+      let i = 1
+      let options, src, key, copy
+      let isArray = false
       if (typeof target === 'boolean') {
         deep = target
         i++
@@ -262,9 +262,9 @@ export default {
 
     // 获取dom在屏幕中的top和left
     getDomTopLeftById (id) {
-      var dom = document.getElementById(id)
-      var top = 0
-      var left = 0
+      let dom = document.getElementById(id)
+      let top = 0
+      let left = 0
       if (dom != null) {
         top = dom.getBoundingClientRect().top
         left = dom.getBoundingClientRect().left
@@ -272,10 +272,10 @@ export default {
       return { top: top, left: left }
     },
     objToOne (obj) {
-      var tmpData = {}
-      for (var index in obj) {
+      let tmpData = {}
+      for (let index in obj) {
         if (typeof obj[index] == 'object') {
-          var resObj = this.objToOne(obj[index])
+          let resObj = this.objToOne(obj[index])
           Object.assign(tmpData, resObj) // 这里使用对象合并
         } else {
           tmpData[index] = obj[index]
@@ -293,8 +293,8 @@ export default {
       if (toString.call(obj) != '[object Object]') {
         return obj
       }
-      var result = {}
-      for (var key in obj) {
+      let result = {}
+      for (let key in obj) {
         if (this.isBlank(obj[key])) {
           continue
         }
@@ -309,13 +309,13 @@ export default {
 
     // 根据数据字典,查询指定字典dict指定值code的,返回整个dictItem{id, text, extend}
     getDictItemByCode (dict, code) {
-      var dicts = getStorageItem('gaeaDict')
+      let dicts = getStorageItem('gaeaDict')
       if (!dicts.hasOwnProperty(dict)) {
         return null
       }
-      var dictItems = dicts[dict]
-      for (var i = 0; i < dictItems.length; i++) {
-        var dictItem = dictItems[i]
+      let dictItems = dicts[dict]
+      for (let i = 0; i < dictItems.length; i++) {
+        let dictItem = dictItems[i]
         if (typeof (code) == 'number') {
           code = code.toString()
         }
@@ -327,7 +327,7 @@ export default {
     },
     // 根据数据字典,查询指定字典dict指定值code的dictItem.text
     getDictLabelByCode (dict, code) {
-      var dictItem = this.getDictItemByCode(dict, code)
+      let dictItem = this.getDictItemByCode(dict, code)
       if (dictItem != null) {
         return dictItem['text']
       } else {
@@ -336,15 +336,24 @@ export default {
     },
     // 根据数据字典,查询指定字典dict指定值code的dictItem.extend
     getDictExtendByCode (dict, code) {
-      var dictItem = this.getDictItemByCode(dict, code)
+      let dictItem = this.getDictItemByCode(dict, code)
       if (dictItem == null) {
         return null
       }
-      var extend = dictItem['extend']
+      let extend = dictItem['extend']
       if (extend == null || extend.trim() == 'null') {
         return null
       }
       return dictItem['extend']
     },
+    getSettingByName(settingName) {
+      let gaeaSetting = JSON.parse(localStorage.getItem('gaeaDict'))
+      if (gaeaSetting[settingName] != null) {
+        return gaeaSetting[settingName]
+      } else {
+        console.error('没有找到系统参数' + settingName + ',请与后端联系')
+        return null
+      }
+    },
   }
 }
diff --git a/report-ui/src/store/modules/user.js b/report-ui/src/store/modules/user.js
index b8f7591d..57488282 100644
--- a/report-ui/src/store/modules/user.js
+++ b/report-ui/src/store/modules/user.js
@@ -24,7 +24,7 @@ const user = {
       const userName = userInfo.userName.trim()
       const password = userInfo.password.trim()
       const captchaVerification = userInfo.captchaVerification.trim()
-      var data ={
+      let data ={
         userName, password,captchaVerification
       }
       return new Promise((resolve, reject) => {
diff --git a/report-ui/src/utils/aes.js b/report-ui/src/utils/aes.js
index 5f264e1a..05c4c384 100644
--- a/report-ui/src/utils/aes.js
+++ b/report-ui/src/utils/aes.js
@@ -1,12 +1,12 @@
 import CryptoJS from 'crypto-js'
 export function aesEncrypt(word){
-  var key = CryptoJS.enc.Utf8.parse("BGxdEUOZkXka4HSj");
-  var srcs = CryptoJS.enc.Utf8.parse(word);
-  var encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
+  let key = CryptoJS.enc.Utf8.parse("BGxdEUOZkXka4HSj");
+  let srcs = CryptoJS.enc.Utf8.parse(word);
+  let encrypted = CryptoJS.AES.encrypt(srcs, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
   return encrypted.toString();
 }
 export function aesDecrypt(word){
-  var key = CryptoJS.enc.Utf8.parse("BGxdEUOZkXka4HSj");
-  var decrypt = CryptoJS.AES.decrypt(word, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
+  let key = CryptoJS.enc.Utf8.parse("BGxdEUOZkXka4HSj");
+  let decrypt = CryptoJS.AES.decrypt(word, key, {mode:CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7});
   return CryptoJS.enc.Utf8.stringify(decrypt).toString();
 }
diff --git a/report-ui/src/utils/auth.js b/report-ui/src/utils/auth.js
index 4e3dae80..9035c0c7 100644
--- a/report-ui/src/utils/auth.js
+++ b/report-ui/src/utils/auth.js
@@ -41,17 +41,17 @@ export function hasPermission(permissionStr) {
   }
 
   // 登录用户的全部权限码
-  var user = getAccessUser()
+  let user = getAccessUser()
   if (user == null || user.authorities == null) {
     return false
   }
-  var opAuthoritiesStr = JSON.stringify(user.authorities)
+  let opAuthoritiesStr = JSON.stringify(user.authorities)
 
   // permissionStr可能是:authorityManage、authorityManage:insert、authorityManage:insert|authorityManage:update
-  var needPermissionArray = permissionStr.split('|')
-  for (var i = 0; i < needPermissionArray.length; i++) {
+  let needPermissionArray = permissionStr.split('|')
+  for (let i = 0; i < needPermissionArray.length; i++) {
     // 只要有其中的一个权限,就返回true
-    var needPermission = needPermissionArray[i] // authorityManage、authorityManage:insert
+    let needPermission = needPermissionArray[i] // authorityManage、authorityManage:insert
     needPermission = needPermission.replace(/\ /g, "") // 去除authorityManage : insert中:前后的空格
 
     if(opAuthoritiesStr.indexOf(needPermission)>=0){
diff --git a/report-ui/src/utils/common.js b/report-ui/src/utils/common.js
index b828c136..c4a54be3 100644
--- a/report-ui/src/utils/common.js
+++ b/report-ui/src/utils/common.js
@@ -2,8 +2,8 @@ export default {
   timestamp2String: function (timestamp, fmt) {
     if (timestamp == null) return;
     if (fmt == null || fmt == '') fmt = 'yyyy-MM-dd hh:mm:ss';
-    var date = new Date(timestamp);
-    var o = {
+    let date = new Date(timestamp);
+    let o = {
       "M+": date.getMonth() + 1, //月份   
       "d+": date.getDate(), //日   
       "h+": date.getHours(), //小时   
@@ -14,7 +14,7 @@ export default {
     };
     if (/(y+)/.test(fmt))
       fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
-    for (var k in o)
+    for (let k in o)
       if (new RegExp("(" + k + ")").test(fmt))
         fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
     return fmt;
@@ -24,12 +24,12 @@ export default {
     if (second == null || second == 0) {
       return "0秒";
     }
-    var day = Math.floor(second / (24 * 3600));
-    var hour = Math.floor((second - day * 24 * 3600) / 3600);
-    var minute = Math.floor((second - day * 24 * 3600 - hour * 3600) / 60);
-    var second = second - day * 24 * 3600 - hour * 3600 - minute * 60;
+    let day = Math.floor(second / (24 * 3600));
+    let hour = Math.floor((second - day * 24 * 3600) / 3600);
+    let minute = Math.floor((second - day * 24 * 3600 - hour * 3600) / 60);
+    let second = second - day * 24 * 3600 - hour * 3600 - minute * 60;
 
-    var result = "";
+    let result = "";
     if (day > 0) {
       result = result + day + "天";
     }
diff --git a/report-ui/src/utils/index.js b/report-ui/src/utils/index.js
index 0cc42693..9fc2f8a7 100644
--- a/report-ui/src/utils/index.js
+++ b/report-ui/src/utils/index.js
@@ -96,12 +96,12 @@ function padLeftZero (str) {
  *  生成uuid
  */
 export function getUUID () {
-  var d = new Date().getTime()
+  let d = new Date().getTime()
   if (window.performance && typeof window.performance.now === 'function') {
     d += performance.now() // use high-precision timer if available
   }
-  var uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
-    var r = (d + Math.random() * 16) % 16 | 0
+  let uuid = 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+    let r = (d + Math.random() * 16) % 16 | 0
     d = Math.floor(d / 16)
     return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16)
   })
diff --git a/report-ui/src/utils/signUtil.js b/report-ui/src/utils/signUtil.js
index 85bbc548..9d5ed1d3 100644
--- a/report-ui/src/utils/signUtil.js
+++ b/report-ui/src/utils/signUtil.js
@@ -4,8 +4,8 @@ function jsonString (obj) {
   if (isObject(obj)) {
     return sortObjByKey(obj);
   } else if (isArray(obj)) {
-    var sortArray = [];
-    for (var i = 0; i < obj.length; i++) {
+    let sortArray = [];
+    for (let i = 0; i < obj.length; i++) {
       if (isObject(obj[i])) {
         sortArray.push(sortObjByKey(obj[i]));
       } else {
@@ -20,10 +20,10 @@ function jsonString (obj) {
 
 // 将通讯录按照 ABCD字母的顺序排序
 function sortObjByKey (obj) {
-  var keys = Object.keys(obj).sort();
-  var newObj = {}
-  for (var i = 0; i < keys.length; i++) {
-    var index = keys[i];
+  let keys = Object.keys(obj).sort();
+  let newObj = {}
+  for (let i = 0; i < keys.length; i++) {
+    let index = keys[i];
     newObj[index] = jsonString(obj[index]);
   }
   return newObj;
@@ -50,13 +50,13 @@ export default {
     if (typeof (data) == "undefined" || data == null) {
       data = {};
     }
-    var gatewayRequest = {};//currentPage pageSize orderBy data  time token sign isFrom
-    var time = new Date().getTime();
+    let gatewayRequest = {};//currentPage pageSize orderBy data  time token sign isFrom
+    let time = new Date().getTime();
     //找出data中值非null的key,同时排除分页参数
     //ignoreKeyList data中哪些key不参与签名
-    var dataKeyArray = [];
-    var excludeKeyArray = ['currentPage', 'pageSize', 'orderBy', 'ignoreKeyList'];
-    for (var key in data) {
+    let dataKeyArray = [];
+    let excludeKeyArray = ['currentPage', 'pageSize', 'orderBy', 'ignoreKeyList'];
+    for (let key in data) {
       if (data[key] == null) {
         continue;
       }
@@ -65,7 +65,7 @@ export default {
         delete data[key];
         if (key === 'ignoreKeyList') {
           //遍历ignoreKeyList,不参与签名校验
-          for (var i = 0; i < data[key].length; i++) {
+          for (let i = 0; i < data[key].length; i++) {
             delete data[data[key][i]];
           }
         }
@@ -77,10 +77,10 @@ export default {
     dataKeyArray.sort();
 
     //生成签名串"time=%s&" + tempSignStr + "&token=%s"
-    var tempSignStr = "time=" + time;
-    for (var i = 0; i < dataKeyArray.length; i++) {
-      var key = dataKeyArray[i];
-      var value = data[key];
+    let tempSignStr = "time=" + time;
+    for (let i = 0; i < dataKeyArray.length; i++) {
+      let key = dataKeyArray[i];
+      let value = data[key];
       tempSignStr += "&";
       tempSignStr += key;
       tempSignStr += "=";
@@ -88,7 +88,7 @@ export default {
     }
     tempSignStr += "&token=";
     tempSignStr += token;
-    var signStr = md5(tempSignStr);
+    let signStr = md5(tempSignStr);
     gatewayRequest['data'] = data;
     gatewayRequest['time'] = time;
     gatewayRequest['token'] = token;
diff --git a/report-ui/src/utils/storage.js b/report-ui/src/utils/storage.js
index 02a2aad1..7c14a4ec 100644
--- a/report-ui/src/utils/storage.js
+++ b/report-ui/src/utils/storage.js
@@ -1,10 +1,18 @@
+/*
+ * @Descripttion: 
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-12-11 14:48:27
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2021-12-13 10:16:49
+ */
 import Cookies from 'js-cookie'
 export function setCookie(k, v) {
   if(typeof(v) == "undefined" || v == null){
     return;
   }
 
-  var val = v;
+  let val = v;
   if(typeof(v) == "object"){
     val = JSON.stringify(v);
   }
@@ -13,7 +21,7 @@ export function setCookie(k, v) {
 }
 
 export function getCookie(k) {
-  var val = Cookies.get(k);
+  let val = Cookies.get(k);
   try{
     //如果是number boolean jsonstring是不会报错的
     return JSON.parse(val);
@@ -32,7 +40,7 @@ export function setStorageItem(k, v) {
     return;
   }
 
-  var val = v;
+  let val = v;
   if(typeof(v) == "object"){
     val = JSON.stringify(v);
   }
@@ -40,7 +48,7 @@ export function setStorageItem(k, v) {
 }
 
 export function getStorageItem(k) {
-  var val = localStorage.getItem(k);
+  let val = localStorage.getItem(k);
   try{
     //如果是number boolean jsonstring是不会报错的
     return JSON.parse(val);
diff --git a/report-ui/src/utils/throttle.js b/report-ui/src/utils/throttle.js
index 68396b40..93b8ad79 100644
--- a/report-ui/src/utils/throttle.js
+++ b/report-ui/src/utils/throttle.js
@@ -1,9 +1,17 @@
+/*
+ * @Descripttion: 
+ * @version: 
+ * @Author: qianlishi
+ * @Date: 2021-12-11 14:48:27
+ * @LastEditors: qianlishi
+ * @LastEditTime: 2021-12-13 10:16:48
+ */
 /**
  * 函数节流
  */
 export function _throttle(fn,delay){
     let timer
-    var delay = delay || 1000;   //一秒内触发一次
+    let delay = delay || 1000;   //一秒内触发一次
     return function(...args){
         const context = this
         let canExecute = !timer