diff --git a/cmdb-ui/src/modules/cmdb/components/batchDownload/batchDownload.vue b/cmdb-ui/src/modules/cmdb/components/batchDownload/batchDownload.vue
index 429d6ce..6cfecf6 100644
--- a/cmdb-ui/src/modules/cmdb/components/batchDownload/batchDownload.vue
+++ b/cmdb-ui/src/modules/cmdb/components/batchDownload/batchDownload.vue
@@ -111,12 +111,14 @@ export default {
   },
   methods: {
     ...mapMutations('cmdbStore', ['SET_IS_TABLE_LOADING']),
-    open({ preferenceAttrList }) {
+    open({ preferenceAttrList, ciTypeName = undefined }) {
       this.preferenceAttrList = preferenceAttrList
       this.visible = true
       this.$nextTick((res) => {
         this.form.setFieldsValue({
-          filename: `cmdb-${moment().format('YYYYMMDDHHmmss')}`,
+          filename: ciTypeName
+            ? `cmdb-${ciTypeName}-${moment().format('YYYYMMDDHHmmss')}`
+            : `cmdb-${moment().format('YYYYMMDDHHmmss')}`,
         })
         if (this.treeType === 'tree') {
           const _check = ['ci_type_alias']
diff --git a/cmdb-ui/src/modules/cmdb/views/batch/index.vue b/cmdb-ui/src/modules/cmdb/views/batch/index.vue
index c6c8420..d9f8a4a 100644
--- a/cmdb-ui/src/modules/cmdb/views/batch/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/batch/index.vue
@@ -106,7 +106,6 @@ export default {
       this.uploadData = _uploadData.slice(1)
       this.hasError = false
       this.isUploading = false
-      this.$refs.uploadResult.visible = false
     },
     handleUpload() {
       if (!this.ciType) {
diff --git a/cmdb-ui/src/modules/cmdb/views/ci/index.vue b/cmdb-ui/src/modules/cmdb/views/ci/index.vue
index 64019af..8a484a5 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci/index.vue
@@ -576,7 +576,10 @@ export default {
     },
 
     async openBatchDownload() {
-      this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList })
+      this.$refs.batchDownload.open({
+        preferenceAttrList: this.preferenceAttrList,
+        ciTypeName: this.$route.meta.title || this.$route.meta.name,
+      })
     },
     batchDownload({ filename, type, checkedKeys }) {
       const jsonAttrList = []
diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue
index 84af7f3..c9abc1f 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue
@@ -276,15 +276,16 @@ export default {
     },
     mergeRowMethod({ row, _rowIndex, column, visibleData }) {
       const fields = ['created_at', 'username']
-      const cellValue = row[column.property]
-      if (cellValue && fields.includes(column.property)) {
+      const cellValue1 = row['created_at']
+      const cellValue2 = row['username']
+      if (cellValue1 && cellValue2 && fields.includes(column.property)) {
         const prevRow = visibleData[_rowIndex - 1]
         let nextRow = visibleData[_rowIndex + 1]
-        if (prevRow && prevRow[column.property] === cellValue) {
+        if (prevRow && prevRow['created_at'] === cellValue1 && prevRow['username'] === cellValue2) {
           return { rowspan: 0, colspan: 0 }
         } else {
           let countRowspan = 1
-          while (nextRow && nextRow[column.property] === cellValue) {
+          while (nextRow && nextRow['created_at'] === cellValue1 && nextRow['username'] === cellValue2) {
             nextRow = visibleData[++countRowspan + _rowIndex]
           }
           if (countRowspan > 1) {
diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/attrAD.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/attrAD.vue
index d04f138..1e705ac 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/attrAD.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/attrAD.vue
@@ -136,8 +136,8 @@ export default {
     async getCITypeDiscovery(currentTab) {
       await getCITypeDiscovery(this.CITypeId).then((res) => {
         this.adCITypeList = res.filter((item) => item.adr_id)
-        if (res && res.length && !this.currentTab) {
-          this.currentTab = res[0].id
+        if (this.adCITypeList && this.adCITypeList.length && !this.currentTab) {
+          this.currentTab = this.adCITypeList[0].id
         }
         if (currentTab) {
           this.currentTab = currentTab
diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue
index 18b7cd1..c54faad 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue
@@ -40,20 +40,18 @@
                   <a-menu-item key="0">
                     <a-upload
                       name="file"
-                      accept="json"
+                      accept=".json"
                       :showUploadList="false"
                       style="display: inline-block"
-                      action="/api/v0.1/ci_types/template/import/file "
+                      action="/api/v0.1/ci_types/template/import/file"
+                      @change="changeUploadFile"
                     >
-                      <a-space
-                      ><a><a-icon type="upload"/></a><a>导入</a></a-space
-                      >
+                      <a><a-icon type="upload"/></a><a> 导入</a>
                     </a-upload>
                   </a-menu-item>
                   <a-menu-item key="1">
                     <a-space>
-                      <a><a-icon type="download"/></a>
-                      <a href="/api/v0.1/ci_types/template/export/file">导出</a>
+                      <a href="/api/v0.1/ci_types/template/export/file"><a-icon type="download" /> 导出</a>
                     </a-space>
                   </a-menu-item>
                 </a-menu>
@@ -134,6 +132,12 @@
                   <a-space class="ci-types-left-detail-action">
                     <a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
                     <a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
+                    <a
+                      v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')"
+                      @click="(e) => handleDownloadCiType(e, ci)"
+                    ><a-icon
+                      type="download"
+                    /></a>
                     <a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
                   </a-space>
                 </div>
@@ -309,6 +313,7 @@ export default {
     OpsMoveIcon,
     AttributeStore,
   },
+  inject: ['reload'],
   data() {
     return {
       emptyImage,
@@ -716,6 +721,21 @@ export default {
         },
       })
     },
+    handleDownloadCiType(e, ci) {
+      e.preventDefault()
+      e.stopPropagation()
+      const x = new XMLHttpRequest()
+      x.open('GET', `/api/v0.1/ci_types/${ci.id}/template/export`, true)
+      x.responseType = 'blob'
+      x.onload = function(e) {
+        const url = window.URL.createObjectURL(x.response)
+        const a = document.createElement('a')
+        a.href = url
+        a.download = `${ci.alias || ci.name}.json`
+        a.click()
+      }
+      x.send()
+    },
     resetRoute() {
       resetRouter()
       const roles = store.getters.roles
@@ -780,6 +800,19 @@ export default {
         }
       })
     },
+    changeUploadFile({ file, fileList, event }) {
+      const key = 'upload'
+      if (file.status === 'uploading') {
+        this.$message.loading({ content: '正在导入中', key, duration: 0 })
+      }
+      if (file.status === 'done') {
+        this.$message.success({ content: '导入成功', key, duration: 2 })
+        this.reload()
+      }
+      if (file.status === 'error') {
+        this.$message.error({ content: '导入失败,请稍后重试', key, duration: 2 })
+      }
+    },
   },
 }
 </script>
diff --git a/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue b/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue
index 70d1c2d..ac0df19 100644
--- a/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue
@@ -58,10 +58,10 @@
             />
             <div class="relation-views-right-bar">
               <a-space>
-                <a-button
-                  v-if="isLeaf"
-                  type="primary"
-                  size="small"
+                <a-button
+                  v-if="isLeaf"
+                  type="primary"
+                  size="small"
                   @click="$refs.create.handleOpen(true, 'create')"
                 >新建</a-button
                 >
@@ -322,6 +322,7 @@
       v-else-if="relationViews.name2id && !relationViews.name2id.length"
     ></a-alert>
     <AddTableModal ref="addTableModal" @reload="reload" />
+    <!-- <GrantDrawer ref="grantDrawer" resourceTypeName="RelationView" app_id="cmdb" /> -->
     <CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" />
 
     <ci-detail ref="detail" :typeId="Number(currentTypeId[0])" />
@@ -378,6 +379,7 @@ export default {
     SearchForm,
     AddTableModal,
     ContextMenu,
+    // GrantDrawer,
     CMDBGrant,
     SplitPane,
     ElTree: Tree,
@@ -651,6 +653,9 @@ export default {
         } else {
           q = `q=_type:${this.currentTypeId[0]},` + q
         }
+        if (Object.values(this.level2constraint).includes('2')) {
+          q = q + `&&has_m2m=1`
+        }
         if (this.currentTypeId[0]) {
           const res = await searchCIRelation(q)
 
@@ -689,6 +694,7 @@ export default {
               root_ids: key.split('%')[0],
               level: this.treeKeys.length - index,
               type_ids: this.showTypes.map((type) => type.id).join(','),
+              has_m2m: Number(Object.values(this.level2constraint).includes('2')),
             }).then((res) => {
               let result
               const getTreeItem = (data, id) => {
@@ -742,7 +748,10 @@ export default {
         _showTypes = JSON.parse(JSON.stringify(this.origShowTypes))
       }
       const promises = _showTypeIds.map((typeId) => {
-        const _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
+        let _q = (`q=_type:${typeId},` + q).replace(/count=\d*/, 'count=1')
+        if (Object.values(this.level2constraint).includes('2')) {
+          _q = _q + `&&has_m2m=1`
+        }
         console.log(_q)
         if (this.treeKeys.length === 0) {
           return searchCI2(_q).then((res) => {
@@ -798,6 +807,7 @@ export default {
             root_ids: ciIds.join(','),
             level: level,
             type_ids: this.showTypes.map((type) => type.id).join(','),
+            has_m2m: Number(Object.values(this.level2constraint).includes('2')),
           }).then((num) => {
             facet.forEach((item, idx) => {
               item[1] += num[ciIds[idx] + '']
@@ -827,6 +837,9 @@ export default {
             .map((item) => item.split('%')[0])
             .join(',')}`
         }
+        if (Object.values(this.level2constraint).includes('2')) {
+          q = q + `&&has_m2m=1`
+        }
         searchCIRelation(q).then(async (res) => {
           const facet = []
           const ciIds = []
@@ -849,6 +862,7 @@ export default {
                 root_ids: ciIds.join(','),
                 level: _level - 1,
                 type_ids: this.showTypes.map((type) => type.id).join(','),
+                has_m2m: Number(Object.values(this.level2constraint).includes('2')),
               }).then((num) => {
                 facet.forEach((item, idx) => {
                   item[1] += num[ciIds[idx] + '']
@@ -1322,7 +1336,10 @@ export default {
       })
     },
     async openBatchDownload() {
-      this.$refs.batchDownload.open({ preferenceAttrList: this.preferenceAttrList })
+      this.$refs.batchDownload.open({
+        preferenceAttrList: this.preferenceAttrList,
+        ciTypeName: this.$route.meta.name,
+      })
     },
     batchDownload({ filename, type, checkedKeys }) {
       const jsonAttrList = []
diff --git a/cmdb-ui/src/modules/cmdb/views/relation_views/modules/AddTableModal.vue b/cmdb-ui/src/modules/cmdb/views/relation_views/modules/AddTableModal.vue
index 10d7229..737e42f 100644
--- a/cmdb-ui/src/modules/cmdb/views/relation_views/modules/AddTableModal.vue
+++ b/cmdb-ui/src/modules/cmdb/views/relation_views/modules/AddTableModal.vue
@@ -130,22 +130,27 @@ export default {
         exp = expression.match(regQ) ? expression.match(regQ)[0] : null
       }
 
-      const res = await searchCI({
+      await searchCI({
         q: `_type:${this.addTypeId}${exp ? `,${exp}` : ''}${fuzzySearch ? `,*${fuzzySearch}*` : ''}`,
         count: 50,
         page: this.currentPage,
         sort,
       })
-      this.tableData = res.result
-      this.totalNumber = res.numfound
-      this.columns = this.getColumns(res.result, this.preferenceAttrList)
-      this.$nextTick(() => {
-        const _table = this.$refs.xTable
-        if (_table) {
-          _table.refreshColumn()
-        }
-        this.loading = false
-      })
+        .then((res) => {
+          this.tableData = res.result
+          this.totalNumber = res.numfound
+          this.columns = this.getColumns(res.result, this.preferenceAttrList)
+          this.$nextTick(() => {
+            const _table = this.$refs.xTable
+            if (_table) {
+              _table.refreshColumn()
+            }
+            this.loading = false
+          })
+        })
+        .catch(() => {
+          this.loading = false
+        })
     },
     getColumns(data, attrList) {
       const modalDom = document.getElementById('add-table-modal')
diff --git a/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue b/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue
index 3b06fd5..d2896be 100644
--- a/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue
@@ -18,10 +18,10 @@
             属性说明
           </span>
         </span>
-        <a-button
-          size="small"
-          icon="plus"
-          type="primary"
+        <a-button
+          size="small"
+          icon="plus"
+          type="primary"
           @click="$refs.create.handleOpen(true, 'create')"
         >新建</a-button
         >
@@ -1054,7 +1054,10 @@ export default {
       this.$refs.jsonEditor.open(column, row)
     },
     async openBatchDownload() {
-      this.$refs.batchDownload.open({ preferenceAttrList: this.currentAttrList })
+      this.$refs.batchDownload.open({
+        preferenceAttrList: this.currentAttrList,
+        ciTypeName: this.$route.meta.title || this.$route.meta.name,
+      })
     },
     batchDownload({ filename, type, checkedKeys }) {
       console.log(filename, type)