diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/attributeCard.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/attributeCard.vue
index 952e4d8..b4ae78e 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/attributeCard.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/attributeCard.vue
@@ -10,7 +10,10 @@
         </div>
         <div class="attribute-card_value-type">{{ valueTypeMap[property.value_type] }}</div>
       </div>
-      <div class="attribute-card-trigger" v-if="property.value_type === '3' || property.value_type === '4'">
+      <div
+        class="attribute-card-trigger"
+        v-if="(property.value_type === '3' || property.value_type === '4') && !isStore"
+      >
         <a @click="openTrigger"><ops-icon type="ops-trigger"/></a>
       </div>
     </div>
@@ -47,7 +50,7 @@
       </a-popover>
 
       <a-space class="attribute-card-operation">
-        <a><a-icon type="edit" @click="handleEdit"/></a>
+        <a v-if="!isStore"><a-icon type="edit" @click="handleEdit"/></a>
         <a style="color:red;"><a-icon type="delete" @click="handleDelete"/></a>
       </a-space>
     </div>
@@ -56,7 +59,7 @@
 </template>
 
 <script>
-import { deleteCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
+import { deleteCITypeAttributesById, deleteAttributesById } from '@/modules/cmdb/api/CITypeAttr'
 import ValueTypeIcon from '@/components/CMDBValueTypeMapIcon'
 import {
   ops_default_show,
@@ -92,6 +95,10 @@ export default {
       type: Number,
       default: null,
     },
+    isStore: {
+      type: Boolean,
+      default: false,
+    },
   },
   data() {
     const propertyList = [
@@ -140,10 +147,17 @@ export default {
         title: '警告',
         content: `确认删除 【${that.property.alias || that.property.name}】?`,
         onOk() {
-          deleteCITypeAttributesById(that.CITypeId, { attr_id: [that.property.id] }).then(() => {
-            that.$message.success('删除成功!')
-            that.$emit('ok')
-          })
+          if (that.isStore) {
+            deleteAttributesById(that.property.id).then(() => {
+              that.$message.success('删除成功!')
+              that.$emit('ok')
+            })
+          } else {
+            deleteCITypeAttributesById(that.CITypeId, { attr_id: [that.property.id] }).then(() => {
+              that.$message.success('删除成功!')
+              that.$emit('ok')
+            })
+          }
         },
         onCancel() {},
       })
diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/attributeStore.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/attributeStore.vue
new file mode 100644
index 0000000..0c28b7b
--- /dev/null
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/attributeStore.vue
@@ -0,0 +1,212 @@
+<template>
+  <a-modal wrapClassName="attrbute-store-wrapper" width="80%" :visible="visible" @cancel="handleCancel">
+    <template slot="title">
+      <div class="attrbute-store-header">
+        <span>属性库</span>
+        <div class="attrbute-store-search">
+          <a-input-group compact>
+            <a-select class="attrbute-store-search-select" v-model="searchKey">
+              <a-select-option value="alias">
+                别名
+              </a-select-option>
+              <a-select-option value="name">
+                名称
+              </a-select-option>
+            </a-select>
+            <a-input
+              ref="input"
+              slot="default"
+              class="attrbute-store-search-input"
+              v-model="searchValue"
+              @pressEnter="pressEnter"
+              allowClear
+              @change="handleInput"
+            >
+              <a-icon slot="suffix" type="search" @click="pressEnter" :style="{ cursor: 'pointer' }" />
+            </a-input>
+          </a-input-group>
+        </div>
+      </div>
+    </template>
+    <a-spin :spinning="loading" :style="{ height: '100%' }">
+      <a-row v-if="attrList.length">
+        <a-col
+          class="attrbute-store-col"
+          :xxl="4"
+          :xl="6"
+          :lg="8"
+          :md="12"
+          :sm="24"
+          v-for="item in attrList"
+          :key="item.id"
+        >
+          <AttributeCard
+            @ok="
+              () => {
+                searchAttributes()
+              }
+            "
+            :isStore="true"
+            :property="item"
+          />
+        </a-col>
+      </a-row>
+      <a-empty v-else>
+        <img slot="image" :src="require('@/assets/data_empty.png')" />
+        <span slot="description"> 暂无数据 </span>
+      </a-empty>
+    </a-spin>
+    <template slot="footer">
+      <a-pagination
+        size="small"
+        show-size-changer
+        show-quick-jumper
+        :current="tablePage.currentPage"
+        :total="tablePage.totalResult"
+        :show-total="(total, range) => `当前展示 ${range[0]}-${range[1]} 条数据, 共 ${total} 条`"
+        :page-size="tablePage.pageSize"
+        :default-current="1"
+        @change="pageOrSizeChange"
+        @showSizeChange="pageOrSizeChange"
+        :pageSizeOptions="['20', '50', '100', '200']"
+      />
+    </template>
+  </a-modal>
+</template>
+
+<script>
+import { searchAttributes } from '../../api/CITypeAttr'
+import AttributeCard from './attributeCard.vue'
+export default {
+  name: 'AttributeStore',
+  components: { AttributeCard },
+  data() {
+    return {
+      visible: false,
+      attrList: [],
+      tablePage: {
+        currentPage: 1,
+        pageSize: 50,
+        totalResult: 0,
+      },
+      loading: false,
+      searchKey: 'alias',
+      searchValue: '',
+    }
+  },
+  methods: {
+    open() {
+      this.visible = true
+      this.searchAttributes()
+    },
+    handleCancel() {
+      this.visible = false
+    },
+    async searchAttributes(currentPage = 1, pageSize = this.tablePage.pageSize) {
+      this.loading = true
+      const params = {
+        page: currentPage,
+        page_size: pageSize,
+      }
+      if (this.searchKey && this.searchValue) {
+        params[this.searchKey] = this.searchValue
+      }
+      searchAttributes(params)
+        .then((res) => {
+          this.attrList = res.attributes
+          this.tablePage = {
+            ...this.tablePage,
+            currentPage: res.page,
+            pageSize: res.page_size,
+            totalResult: res.numfound,
+          }
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    },
+    pageOrSizeChange(currentPage, pageSize) {
+      this.searchAttributes(currentPage, pageSize)
+    },
+    pressEnter() {
+      this.searchAttributes(1)
+    },
+    handleInput(e) {
+      if (!e.target.value) {
+        this.pressEnter()
+      }
+    },
+  },
+}
+</script>
+
+<style lang="less" scoped>
+.attrbute-store-wrapper {
+  .attrbute-store-col {
+    display: flex;
+    justify-content: center;
+  }
+}
+</style>
+
+<style lang="less">
+.attrbute-store-wrapper {
+  .ant-modal-body {
+    height: 70vh;
+    overflow: auto;
+  }
+  .attrbute-store-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+}
+</style>
+
+<style lang="less">
+@import '~@/style/static.less';
+
+.attrbute-store-search {
+  width: 300px;
+  display: inline-block;
+  margin-right: 60px;
+  .ant-input-group.ant-input-group-compact > *:first-child,
+  .ant-input-group.ant-input-group-compact > .ant-select:first-child > .ant-select-selection {
+    border-top-left-radius: 20px !important;
+    border-bottom-left-radius: 20px !important;
+    background-color: #custom_colors[color_1];
+    color: #fff;
+    border: none;
+  }
+  .ant-select-focused .ant-select-selection,
+  .ant-select-selection:focus {
+    box-shadow: none;
+  }
+  .ant-select-selection__rendered {
+    margin-right: 12px;
+  }
+  .ant-select-arrow {
+    color: #fff;
+    font-size: 10px;
+    right: 8px;
+  }
+  .attrbute-store-search-select {
+    width: 65px;
+    .ant-select-selection-selected-value {
+      font-size: 12px;
+    }
+  }
+  .attrbute-store-search-input {
+    display: inline-block;
+    width: calc(100% - 65px);
+    .ant-input {
+      background-color: #f0f5ff;
+      border: none;
+      border-radius: 20px;
+      &:focus {
+        box-shadow: none;
+      }
+    }
+  }
+}
+</style>
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 3275e67..8bb5dfc 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/index.vue
@@ -25,47 +25,71 @@
               class="ops-button-primary"
             >分组</a-button
             >
-            <a-space v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')">
-              <a-upload
-                name="file"
-                accept="json"
-                :showUploadList="false"
-                style="display: inline-block"
-                action="/api/v0.1/ci_types/template/import/file "
+            <a-space>
+              <a
+                @click="
+                  () => {
+                    $refs.attributeStore.open()
+                  }
+                "
+              >属性库</a
               >
-                <a>导入</a>
-              </a-upload>
-              <a href="/api/v0.1/ci_types/template/export/file">导出</a>
+              <a-dropdown v-if="permissions.includes('admin') || permissions.includes('cmdb_admin')">
+                <a><ops-icon type="ops-menu"/></a>
+                <a-menu slot="overlay">
+                  <a-menu-item key="0">
+                    <a-upload
+                      name="file"
+                      accept="json"
+                      :showUploadList="false"
+                      style="display: inline-block"
+                      action="/api/v0.1/ci_types/template/import/file "
+                    >
+                      <a-space
+                      ><a><a-icon type="upload"/></a><a>导入</a></a-space
+                      >
+                    </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-space>
+                  </a-menu-item>
+                </a-menu>
+              </a-dropdown>
             </a-space>
           </div>
           <draggable class="ci-types-left-content" :list="CITypeGroups" @end="handleChangeGroups" filter=".undraggable">
             <div v-for="g in CITypeGroups" :key="g.id || g.name">
               <div
-                :class="`${currentGId === g.id && !currentCId ? 'selected' : ''} ci-types-left-group ${
-                  g.id === -1 ? 'undraggable' : ''
-                }`"
+                :class="
+                  `${currentGId === g.id && !currentCId ? 'selected' : ''} ci-types-left-group ${
+                    g.id === -1 ? 'undraggable' : ''
+                  }`
+                "
                 @click="handleClickGroup(g.id)"
               >
                 <div>
                   <OpsMoveIcon
                     style="width: 17px; height: 17px; display: none; position: absolute; left: -3px; top: 10px"
                   />
-                  <span style="font-weight: 700">{{ g.name || '其他' }}</span>
+                  <span style="font-weight:700">{{ g.name || '其他' }}</span>
                   <span :style="{ color: '#c3cdd7' }">({{ g.ci_types.length }})</span>
                 </div>
                 <a-space>
                   <a-tooltip>
                     <template slot="title">在该组中新增CI模型</template>
-                    <a><a-icon type="plus" @click="handleCreate(g)" /></a>
+                    <a><a-icon type="plus" @click="handleCreate(g)"/></a>
                   </a-tooltip>
                   <template v-if="g.id !== -1">
                     <a-tooltip>
                       <template slot="title">编辑组名称</template>
-                      <a><a-icon type="edit" @click="handleEditGroup(g)" /></a>
+                      <a><a-icon type="edit" @click="handleEditGroup(g)"/></a>
                     </a-tooltip>
                     <a-tooltip>
                       <template slot="title">删除该组</template>
-                      <a style="color: red"><a-icon type="delete" @click="handleDeleteGroup(g)" /></a>
+                      <a style="color: red"><a-icon type="delete" @click="handleDeleteGroup(g)"/></a>
                     </a-tooltip>
                   </template>
                 </a-space>
@@ -102,9 +126,9 @@
                   </div>
                   <span class="ci-types-left-detail-title">{{ ci.alias || ci.name }}</span>
                   <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 style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete" /></a>
+                    <a><a-icon type="user-add" @click="(e) => handlePerm(e, ci)"/></a>
+                    <a><a-icon type="edit" @click="(e) => handleEdit(e, ci)"/></a>
+                    <a style="color: red" @click="(e) => handleDelete(e, ci)"><a-icon type="delete"/></a>
                   </a-space>
                 </div>
               </draggable>
@@ -185,8 +209,12 @@
             <a-divider :style="{ margin: '5px 0' }" />
             <div :style="{ textAlign: 'right' }">
               <a-radio-group v-model="default_order_asc">
-                <a-radio value="1"> 正序 </a-radio>
-                <a-radio value="2"> 倒序 </a-radio>
+                <a-radio value="1">
+                  正序
+                </a-radio>
+                <a-radio value="2">
+                  倒序
+                </a-radio>
               </a-radio-group>
             </div>
           </el-select>
@@ -230,6 +258,7 @@
       </a-form>
     </CustomDrawer>
     <CMDBGrant ref="cmdbGrant" resourceType="CIType" app_id="cmdb" />
+    <AttributeStore ref="attributeStore" />
   </div>
 </template>
 
@@ -257,6 +286,7 @@ import IconArea from './iconArea.vue'
 import SplitPane from '@/components/SplitPane'
 import CMDBGrant from '../../components/cmdbGrant'
 import { ops_move_icon as OpsMoveIcon } from '@/core/icons'
+import AttributeStore from './attributeStore.vue'
 
 export default {
   name: 'CITypes',
@@ -270,6 +300,7 @@ export default {
     IconArea,
     SplitPane,
     OpsMoveIcon,
+    AttributeStore,
   },
   data() {
     return {
@@ -589,7 +620,6 @@ export default {
       }
     },
     async handleChangeCITypes(e, g) {
-      console.log(111, g)
       if (g.id && g.id !== -1) {
         putCITypeGroupByGId(g.id, { name: g.name, type_ids: g.ci_types.map((i) => i.id) })
           .then(() => {
@@ -613,7 +643,6 @@ export default {
       const { type_id } = await createCIType(data).catch(() => {
         this.loading = false
       })
-      console.log(111)
       this.$message.success(`添加成功`)
       if (this.selectGroup && this.selectGroup.id && this.selectGroup.id !== -1) {
         const ids = this.selectGroup.ci_types.map((i) => i.id)
diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/newCiTypeAttrModal.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/newCiTypeAttrModal.vue
index 46dcb1e..33981da 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/newCiTypeAttrModal.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/newCiTypeAttrModal.vue
@@ -91,13 +91,8 @@ export default {
         })
     },
   },
-  beforeMount() {
-    this.loadTotalAttrs()
-  },
   methods: {
     async handleSubmit(isCloseModal = true) {
-      console.log(this.targetKeys)
-
       if (this.activeKey === '2') {
         if (this.targetKeys.length) {
           this.confirmLoading = true
@@ -125,6 +120,7 @@ export default {
       this.visible = true
       this.currentGroup = group
       this.activeKey = '1'
+      this.loadTotalAttrs()
       this.$nextTick(() => {
         this.$refs.createNewAttribute.checkCanDefineComputed()
       })
diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue
index 9125d6d..5c84d13 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue
@@ -90,7 +90,12 @@ export default {
       )
     },
   },
-  inject: ['refresh'],
+  inject: {
+    refresh: {
+      from: 'refresh',
+      default: null,
+    },
+  },
   methods: {
     createFromTriggerTable(canAddTriggerAttr) {
       this.visible = true
@@ -163,7 +168,9 @@ export default {
             await addTrigger(this.CITypeId, params)
           }
           this.handleCancel()
-          this.refresh()
+          if (this.refresh) {
+            this.refresh()
+          }
         }
       })
     },
@@ -176,7 +183,9 @@ export default {
           deleteTrigger(that.CITypeId, that.triggerId).then(() => {
             that.$message.success('删除成功!')
             that.handleCancel()
-            that.refresh()
+            if (that.refresh) {
+              that.refresh()
+            }
           })
         },
       })
diff --git a/cmdb-ui/src/modules/cmdb/views/model_relation/index.vue b/cmdb-ui/src/modules/cmdb/views/model_relation/index.vue
index bb7c360..437167a 100644
--- a/cmdb-ui/src/modules/cmdb/views/model_relation/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/model_relation/index.vue
@@ -1,65 +1,63 @@
 <template>
-  <div>
-    <a-card :bordered="false">
-      <a-button @click="handleCreate" type="primary" style="margin-bottom: 15px;" icon="plus">新增关系</a-button>
-      <model-relation-table ref="table"></model-relation-table>
-      <a-modal
-        :closable="false"
-        :title="drawerTitle"
-        :visible="visible"
-        @cancel="onClose"
-        @ok="handleSubmit"
-        width="500px"
-      >
-        <a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
-          <a-form-item label="源模型">
-            <a-select
-              showSearch
-              name="source_ci_type_id"
-              v-decorator="['source_ci_type_id', { rules: [{ required: true, message: '请选择源模型' }] }]"
-              @change="handleSourceTypeChange"
-              :filterOption="filterOption"
-            >
-              <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayCITypes">{{
-                CIType.alias || CIType.name
-              }}</a-select-option>
-            </a-select>
-          </a-form-item>
-          <a-form-item label="目标模型">
-            <a-select
-              showSearch
-              name="ci_type_id"
-              v-decorator="['ci_type_id', { rules: [{ required: true, message: '请选择目标模型' }] }]"
-              @change="handleTargetTypeChange"
-              :filterOption="filterOption"
-            >
-              <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayTargetCITypes">
-                {{ CIType.alias || CIType.name }}
-              </a-select-option>
-            </a-select>
-          </a-form-item>
+  <div class="model-relation">
+    <a-button @click="handleCreate" type="primary" style="margin-bottom: 15px;" icon="plus">新增关系</a-button>
+    <model-relation-table ref="table"></model-relation-table>
+    <a-modal
+      :closable="false"
+      :title="drawerTitle"
+      :visible="visible"
+      @cancel="onClose"
+      @ok="handleSubmit"
+      width="500px"
+    >
+      <a-form :form="form" @submit="handleSubmit" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
+        <a-form-item label="源模型">
+          <a-select
+            showSearch
+            name="source_ci_type_id"
+            v-decorator="['source_ci_type_id', { rules: [{ required: true, message: '请选择源模型' }] }]"
+            @change="handleSourceTypeChange"
+            :filterOption="filterOption"
+          >
+            <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayCITypes">{{
+              CIType.alias || CIType.name
+            }}</a-select-option>
+          </a-select>
+        </a-form-item>
+        <a-form-item label="目标模型">
+          <a-select
+            showSearch
+            name="ci_type_id"
+            v-decorator="['ci_type_id', { rules: [{ required: true, message: '请选择目标模型' }] }]"
+            @change="handleTargetTypeChange"
+            :filterOption="filterOption"
+          >
+            <a-select-option :value="CIType.id" :key="CIType.id" v-for="CIType in displayTargetCITypes">
+              {{ CIType.alias || CIType.name }}
+            </a-select-option>
+          </a-select>
+        </a-form-item>
 
-          <a-form-item label="关联关系">
-            <a-select
-              name="relation_type_id"
-              v-decorator="['relation_type_id', { rules: [{ required: true, message: '请选择关联关系' }] }]"
-            >
-              <a-select-option :value="relationType.id" :key="relationType.id" v-for="relationType in relationTypes">{{
-                relationType.name
-              }}</a-select-option>
-            </a-select>
-          </a-form-item>
+        <a-form-item label="关联关系">
+          <a-select
+            name="relation_type_id"
+            v-decorator="['relation_type_id', { rules: [{ required: true, message: '请选择关联关系' }] }]"
+          >
+            <a-select-option :value="relationType.id" :key="relationType.id" v-for="relationType in relationTypes">{{
+              relationType.name
+            }}</a-select-option>
+          </a-select>
+        </a-form-item>
 
-          <a-form-item label="关联约束">
-            <a-select v-decorator="['constraint', { rules: [{ required: true, message: '请选择关联约束' }] }]">
-              <a-select-option value="0">一对多</a-select-option>
-              <a-select-option value="1">一对一</a-select-option>
-              <a-select-option value="2">多对多</a-select-option>
-            </a-select>
-          </a-form-item>
-        </a-form>
-      </a-modal>
-    </a-card>
+        <a-form-item label="关联约束">
+          <a-select v-decorator="['constraint', { rules: [{ required: true, message: '请选择关联约束' }] }]">
+            <a-select-option value="0">一对多</a-select-option>
+            <a-select-option value="1">一对一</a-select-option>
+            <a-select-option value="2">多对多</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-form>
+    </a-modal>
   </div>
 </template>
 
@@ -110,16 +108,13 @@ export default {
   },
   mounted() {
     const _currentId = localStorage.getItem('ops_cityps_currentId')
-    console.log(_currentId)
     if (_currentId) {
       this.currentId = _currentId
     }
     searchResourceType({ page_size: 9999, app_id: 'cmdb' }).then((res) => {
-      console.log('searchResourceType', res)
       this.resource_type = { groups: res.groups, id2perms: res.id2perms }
     })
     this.loadCITypes(!_currentId)
-    console.log(this.CITypeId)
   },
   computed: {
     currentCId() {
@@ -168,7 +163,6 @@ export default {
     },
     getCITypes() {
       getCITypes().then((res) => {
-        console.log('getCITypes', res.ci_types)
         this.CITypes = res.ci_types
       })
     },
@@ -196,9 +190,6 @@ export default {
       e.preventDefault()
       this.form.validateFields((err, values) => {
         if (!err) {
-          // eslint-disable-next-line no-console
-          console.log('Received values of form: ', values)
-
           createRelation(values.source_ci_type_id, values.ci_type_id, values.relation_type_id, values.constraint).then(
             (res) => {
               this.$message.success(`添加成功`)
@@ -215,8 +206,6 @@ export default {
       this.$refs.table.refresh()
     },
     handleDelete(record) {
-      console.log(record)
-
       deleteRelation(record.source_ci_type_id, record.id).then((res) => {
         this.$message.success(`删除成功!`)
 
@@ -236,4 +225,12 @@ export default {
 }
 </script>
 
-<style></style>
+<style lang="less" scoped>
+.model-relation {
+  background-color: #fff;
+  border-radius: 15px;
+  padding: 24px;
+  height: calc(100vh - 64px);
+  margin-bottom: -24px;
+}
+</style>
diff --git a/cmdb-ui/src/modules/cmdb/views/model_relation/modules/modelRelationTable.vue b/cmdb-ui/src/modules/cmdb/views/model_relation/modules/modelRelationTable.vue
index ca6e1e5..05901e3 100644
--- a/cmdb-ui/src/modules/cmdb/views/model_relation/modules/modelRelationTable.vue
+++ b/cmdb-ui/src/modules/cmdb/views/model_relation/modules/modelRelationTable.vue
@@ -1,12 +1,13 @@
 <template>
   <div>
     <vxe-table
+      ref="xTable"
       stripe
       class="ops-stripe-table"
       show-header-overflow
       show-overflow
       resizable
-      :max-height="`${windowHeight - 183}px`"
+      :height="`${windowHeight - 160}px`"
       :data="tableData"
       :sort-config="{ defaultSort: { field: 'created_at', order: 'desc' } }"
     >
@@ -16,21 +17,8 @@
         field="relation_type_id"
         title="关系"
         :filters="[{ data: '' }]"
-        :filter-method="filterRelationMethod"
-        :filter-recover-method="filterRelationRecoverMethod"
+        :filter-multiple="false"
       >
-        <template #filter="{ $panel, column }">
-          <template v-for="(option, index) in column.filters">
-            <input
-              type="type"
-              :key="index"
-              v-model="option.data"
-              @input="$panel.changeOption($event, !!option.data, option)"
-              @keyup.enter="$panel.confirmFilter()"
-              placeholder="按回车确认筛选"
-            />
-          </template>
-        </template>
         <template #default="{ row }">
           <a-tag color="cyan">
             {{ row.relation_type.name }}
@@ -39,9 +27,14 @@
       </vxe-column>
       <vxe-column field="child.alias" title="目标模型"></vxe-column>
       <vxe-column field="constraint" title="关联约束"></vxe-column>
-      <vxe-column field="authorization" title="授权" width="89px">
+      <vxe-column field="authorization" title="操作" width="89px">
         <template #default="{ row }">
-          <a @click="handleOpenGrant(row)"><a-icon type="user-add"/></a>
+          <a-space>
+            <a @click="handleOpenGrant(row)"><a-icon type="user-add"/></a>
+            <a-popconfirm title="确认删除?" @confirm="deleteRelation(row)">
+              <a :style="{ color: 'red' }"><ops-icon type="icon-xianxing-delete"/></a>
+            </a-popconfirm>
+          </a-space>
         </template>
       </vxe-column>
     </vxe-table>
@@ -50,7 +43,7 @@
 </template>
 
 <script>
-import { getCITypeRelations } from '@/modules/cmdb/api/CITypeRelation'
+import { getCITypeRelations, deleteRelation } from '@/modules/cmdb/api/CITypeRelation'
 import { getRelationTypes } from '@/modules/cmdb/api/relationType'
 import CMDBGrant from '../../../components/cmdbGrant'
 
@@ -86,40 +79,35 @@ export default {
         item.constraint = this.handleConstraint(item.constraint)
       })
       this.tableData = res
-      console.log('MainData', res)
     },
     // 获取关系
     async getRelationTypes() {
       const res = await getRelationTypes()
-      const relationTypeMap = new Map()
-      res.forEach((item) => {
-        relationTypeMap.set(item.id, item.name)
-      })
-      this.relationTypeList = relationTypeMap
-      console.log('relationTypeList', this.relationTypeList)
+      this.relationTypeList = res.map((item) => ({ value: item.id, label: item.name }))
+      const $table = this.$refs.xTable
+      if ($table) {
+        const nameColumn = $table.getColumnByField('relation_type_id')
+        if (nameColumn) {
+          $table.setFilter(nameColumn, this.relationTypeList)
+        }
+      }
     },
     // 转换关联关系
     handleConstraint(constraintId) {
       return this.constraintMap[constraintId]
     },
     handleOpenGrant(record) {
-      console.log('record', record)
-      console.log(`${record.parent.name} -> ${record.child.name}`)
-      // this.$refs.grantDrawer.open({ name: `${record.parent.name} -> ${record.child.name}` })
       this.$refs.cmdbGrant.open({
         name: `${record.parent.name} -> ${record.child.name}`,
         typeRelationIds: [record.parent_id, record.child_id],
         cmdbGrantType: 'type_relation',
       })
     },
-    filterRelationMethod({ option, row }) {
-      return row.relation_type.name.includes(String(option.data))
-    },
-    filterRelationRecoverMethod({ option }) {
-      option.data = ''
-    },
-    refresh() {
-      this.getMainData()
+    deleteRelation(row) {
+      deleteRelation(row.parent_id, row.child_id).then((res) => {
+        this.$message.success(`删除成功!`)
+        this.getRelationTypes()
+      })
     },
   },
 }