diff --git a/cmdb-ui/src/modules/cmdb/components/searchForm/SearchForm.vue b/cmdb-ui/src/modules/cmdb/components/searchForm/SearchForm.vue index dcb6ef4..a5e58e0 100644 --- a/cmdb-ui/src/modules/cmdb/components/searchForm/SearchForm.vue +++ b/cmdb-ui/src/modules/cmdb/components/searchForm/SearchForm.vue @@ -145,7 +145,7 @@ export default { selectedRowKeys: { type: Array, default: () => [], - } + }, }, data() { return { @@ -179,7 +179,12 @@ export default { this.fuzzySearch = '' }, }, - inject: ['setPreferenceSearchCurrent'], + inject: { + setPreferenceSearchCurrent: { + from: 'setPreferenceSearchCurrent', + default: null, + }, + }, mounted() { if (this.type === 'resourceSearch') { this.getCITypeGroups() @@ -234,7 +239,9 @@ export default { } }, emitRefresh() { - this.setPreferenceSearchCurrent(null) + if (this.setPreferenceSearchCurrent) { + this.setPreferenceSearchCurrent(null) + } this.$nextTick(() => { this.$emit('refresh', true) }) diff --git a/cmdb-ui/src/modules/cmdb/lang/en.js b/cmdb-ui/src/modules/cmdb/lang/en.js index b20ad65..2fe8db6 100644 --- a/cmdb-ui/src/modules/cmdb/lang/en.js +++ b/cmdb-ui/src/modules/cmdb/lang/en.js @@ -19,6 +19,7 @@ const cmdb_en = { operationHistory: 'Operation Audit', relationType: 'Relation Type', ad: 'AutoDiscovery', + cidetail: 'CI Detail' }, ciType: { ciType: 'CIType', @@ -467,6 +468,8 @@ const cmdb_en = { tips10: 'Other attributes of the CIType are computed using expressions\n\nA code snippet computes the returned value.', newUpdateField: 'Add a Attribute', attributeSettings: 'Attribute Settings', + share: 'Share', + noPermission: 'No Permission' }, serviceTree: { deleteNode: 'Delete Node', diff --git a/cmdb-ui/src/modules/cmdb/lang/zh.js b/cmdb-ui/src/modules/cmdb/lang/zh.js index ec432f8..461d199 100644 --- a/cmdb-ui/src/modules/cmdb/lang/zh.js +++ b/cmdb-ui/src/modules/cmdb/lang/zh.js @@ -19,6 +19,7 @@ const cmdb_zh = { operationHistory: '操作审计', relationType: '关系类型', ad: '自动发现', + cidetail: 'CI 详情' }, ciType: { ciType: '模型', @@ -466,6 +467,8 @@ const cmdb_zh = { tips10: '模型的其他属性通过表达式的方式计算出来\n\n一个代码片段计算返回的值', newUpdateField: '新增修改字段', attributeSettings: '字段设置', + share: '分享', + noPermission: '暂无权限' }, serviceTree: { deleteNode: '删除节点', diff --git a/cmdb-ui/src/modules/cmdb/router/index.js b/cmdb-ui/src/modules/cmdb/router/index.js index 66ccf6b..54663a9 100644 --- a/cmdb-ui/src/modules/cmdb/router/index.js +++ b/cmdb-ui/src/modules/cmdb/router/index.js @@ -56,6 +56,13 @@ const genCmdbRoutes = async () => { meta: { title: 'cmdb.menu.adCIs', icon: 'ops-cmdb-adc', selectedIcon: 'ops-cmdb-adc-selected', keepAlive: false }, component: () => import('../views/discoveryCI/index.vue') }, + { + path: `/cmdb/cidetail/:typeId/:ciId`, + name: 'cmdb_ci_detail', + hidden: true, + meta: { title: 'cmdb.menu.cidetail', keepAlive: false }, + component: () => import('../views/ci/ciDetailPage.vue') + }, { path: '/cmdb/disabled2', name: 'cmdb_disabled2', diff --git a/cmdb-ui/src/modules/cmdb/views/ci/ciDetailPage.vue b/cmdb-ui/src/modules/cmdb/views/ci/ciDetailPage.vue new file mode 100644 index 0000000..6a18620 --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/views/ci/ciDetailPage.vue @@ -0,0 +1,71 @@ +<template> + <div> + <div class="ci-detail-header">{{ this.type.name }}</div> + <div class="ci-detail-page"> + <CiDetailTab ref="ciDetailTab" :typeId="typeId" /> + </div> + </div> +</template> + +<script> +import CiDetailTab from './modules/ciDetailTab.vue' +import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr' +import { getCIType } from '@/modules/cmdb/api/CIType' + +export default { + name: 'CiDetailPage', + components: { CiDetailTab }, + data() { + return { + typeId: Number(this.$route.params.typeId), + type: {}, + attrList: [], + attributes: {}, + } + }, + provide() { + return { + attrList: () => { + return this.attrList + }, + attributes: () => { + return this.attributes + }, + } + }, + mounted() { + const { ciId = undefined } = this.$route.params + if (ciId) { + this.$refs.ciDetailTab.create(Number(ciId)) + } + getCIType(this.typeId).then((res) => { + this.type = res.ci_types[0] + }) + this.getAttributeList() + }, + methods: { + async getAttributeList() { + await getCITypeAttributesById(this.typeId).then((res) => { + this.attrList = res.attributes + this.attributes = res + }) + }, + }, +} +</script> + +<style lang="less" scoped> +@import '~@/style/static.less'; + +.ci-detail-header { + border-left: 3px solid @primary-color; + padding-left: 10px; + font-size: 16px; + font-weight: 700; + margin-bottom: 10px; +} +.ci-detail-page { + background-color: #fff; + height: calc(100vh - 122px); +} +</style> diff --git a/cmdb-ui/src/modules/cmdb/views/ci/index.vue b/cmdb-ui/src/modules/cmdb/views/ci/index.vue index 2a97fc5..cab9c07 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/index.vue @@ -57,7 +57,7 @@ <span>{{ $t('cmdb.ci.selectRows', { rows: selectedRowKeys.length }) }}</span> </div> </SearchForm> - <CiDetail ref="detail" :typeId="typeId" /> + <CiDetailDrawer ref="detail" :typeId="typeId" /> <ops-table :id="`cmdb-ci-${typeId}`" border @@ -297,7 +297,7 @@ import router, { resetRouter } from '@/router' import SearchForm from '../../components/searchForm/SearchForm.vue' import CreateInstanceForm from './modules/CreateInstanceForm' -import CiDetail from './modules/CiDetail' +import CiDetailDrawer from './modules/ciDetailDrawer.vue' import EditAttrsPopover from './modules/editAttrsPopover' import JsonEditor from '../../components/JsonEditor/jsonEditor.vue' import { searchCI, updateCI, deleteCI } from '@/modules/cmdb/api/ci' @@ -320,7 +320,7 @@ export default { components: { SearchForm, CreateInstanceForm, - CiDetail, + CiDetailDrawer, JsonEditor, PasswordField, EditAttrsPopover, diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailDrawer.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailDrawer.vue new file mode 100644 index 0000000..939066c --- /dev/null +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailDrawer.vue @@ -0,0 +1,50 @@ +<template> + <CustomDrawer + width="80%" + placement="left" + @close=" + () => { + visible = false + } + " + :visible="visible" + :hasTitle="false" + :hasFooter="false" + :bodyStyle="{ padding: 0, height: '100vh' }" + destroyOnClose + > + <CiDetailTab ref="ciDetailTab" :typeId="typeId" :treeViewsLevels="treeViewsLevels" /> + </CustomDrawer> +</template> + +<script> +import CiDetailTab from './ciDetailTab.vue' +export default { + name: 'CiDetailDrawer', + components: { CiDetailTab }, + props: { + typeId: { + type: Number, + required: false, + default: null, + }, + treeViewsLevels: { + type: Array, + default: () => [], + }, + }, + data() { + return { + visible: false, + } + }, + methods: { + create(ciId, activeTabKey = 'tab_1', ciDetailRelationKey = '1') { + this.visible = true + this.$nextTick(() => { + this.$refs.ciDetailTab.create(ciId, activeTabKey, ciDetailRelationKey) + }) + }, + }, +} +</script> diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelation.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelation.vue index 44d0251..38cbd72 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelation.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelation.vue @@ -39,7 +39,11 @@ class="ops-stripe-table" > <template #operation_default="{ row }"> - <a-popconfirm arrowPointAtCenter :title="$t('cmdb.ci.confirmDeleteRelation')" @confirm="deleteRelation(row._id, ciId)"> + <a-popconfirm + arrowPointAtCenter + :title="$t('cmdb.ci.confirmDeleteRelation')" + @confirm="deleteRelation(row._id, ciId)" + > <a :disabled="!canEdit[parent.id]" :style="{ @@ -82,7 +86,11 @@ class="ops-stripe-table" > <template #operation_default="{ row }"> - <a-popconfirm arrowPointAtCenter :title="$t('cmdb.ci.confirmDeleteRelation')" @confirm="deleteRelation(ciId, row._id)"> + <a-popconfirm + arrowPointAtCenter + :title="$t('cmdb.ci.confirmDeleteRelation')" + @confirm="deleteRelation(ciId, row._id)" + > <a :disabled="!canEdit[child.id]" :style="{ @@ -416,6 +424,7 @@ export default { <style lang="less" scoped> .ci-detail-relation { + height: 100%; .ci-detail-relation-table-title { font-size: 16px; font-weight: 700; diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue index 8d019f4..795b091 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue @@ -2,7 +2,7 @@ <div id="ci-detail-relation-topo" class="ci-detail-relation-topo" - :style="{ width: '100%', marginTop: '20px', height: 'calc(100vh - 136px)' }" + :style="{ width: '100%', marginTop: '20px', height: 'calc(100% - 44px)' }" ></div> </template> diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailTab.vue similarity index 78% rename from cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue rename to cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailTab.vue index 87cce17..81a471d 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/CiDetail.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailTab.vue @@ -1,23 +1,13 @@ <template> - <CustomDrawer - width="80%" - placement="left" - @close=" - () => { - visible = false - } - " - :visible="visible" - :hasTitle="false" - :hasFooter="false" - :bodyStyle="{ padding: 0, height: '100vh' }" - wrapClassName="ci-detail" - destroyOnClose - > - <a-tabs v-model="activeTabKey" @change="changeTab"> + <div :style="{ height: '100%' }"> + <a-tabs v-if="hasPermission" class="ci-detail-tab" v-model="activeTabKey" @change="changeTab"> + <a @click="shareCi" slot="tabBarExtraContent" :style="{ marginRight: '24px' }"> + <a-icon type="share-alt" /> + {{ $t('cmdb.ci.share') }} + </a> <a-tab-pane key="tab_1"> <span slot="tab"><a-icon type="book" />{{ $t('cmdb.attribute') }}</span> - <div :style="{ maxHeight: `${windowHeight - 44}px`, overflow: 'auto', padding: '24px' }" class="ci-detail-attr"> + <div class="ci-detail-attr"> <el-descriptions :title="group.name || $t('other')" :key="group.name" @@ -37,18 +27,18 @@ </a-tab-pane> <a-tab-pane key="tab_2"> <span slot="tab"><a-icon type="branches" />{{ $t('cmdb.relation') }}</span> - <div :style="{ padding: '24px' }"> + <div :style="{ height: '100%', padding: '24px' }"> <CiDetailRelation ref="ciDetailRelation" :ciId="ciId" :typeId="typeId" :ci="ci" /> </div> </a-tab-pane> <a-tab-pane key="tab_3"> <span slot="tab"><a-icon type="clock-circle" />{{ $t('cmdb.ci.history') }}</span> - <div :style="{ padding: '24px', height: 'calc(100vh - 44px)' }"> + <div :style="{ padding: '24px', height: '100%' }"> <vxe-table ref="xTable" :data="ciHistory" size="small" - :max-height="`${windowHeight - 94}px`" + height="auto" :span-method="mergeRowMethod" border :scroll-y="{ enabled: false }" @@ -88,12 +78,22 @@ </a-tab-pane> <a-tab-pane key="tab_4"> <span slot="tab"><ops-icon type="itsm_auto_trigger" />{{ $t('cmdb.history.triggerHistory') }}</span> - <div :style="{ padding: '24px', height: 'calc(100vh - 44px)' }"> + <div :style="{ padding: '24px', height: '100%' }"> <TriggerTable :ci_id="ci._id" /> </div> </a-tab-pane> </a-tabs> - </CustomDrawer> + <a-empty + v-else + :image-style="{ + height: '100px', + }" + :style="{ paddingTop: '20%' }" + > + <img slot="image" :src="require('@/assets/data_empty.png')" /> + <span slot="description"> {{ $t('cmdb.ci.noPermission') }} </span> + </a-empty> + </div> </template> <script> @@ -105,8 +105,8 @@ import { getCIById } from '@/modules/cmdb/api/ci' import CiDetailAttrContent from './ciDetailAttrContent.vue' import CiDetailRelation from './ciDetailRelation.vue' import TriggerTable from '../../operation_history/modules/triggerTable.vue' - export default { + name: 'CiDetailTab', components: { ElDescriptions: Descriptions, ElDescriptionsItem: DescriptionsItem, @@ -126,7 +126,6 @@ export default { }, data() { return { - visible: false, ci: {}, attributeGroups: [], activeTabKey: 'tab_1', @@ -134,13 +133,13 @@ export default { ciHistory: [], ciId: null, ci_types: [], + hasPermission: true, } }, computed: { windowHeight() { return this.$store.state.windowHeight }, - operateTypeMap() { return { 0: this.$t('new'), @@ -156,10 +155,22 @@ export default { }, } }, - inject: ['reload', 'handleSearch', 'attrList'], + inject: { + reload: { + from: 'reload', + default: null, + }, + handleSearch: { + from: 'handleSearch', + default: null, + }, + attrList: { + from: 'attrList', + default: () => [], + }, + }, methods: { - create(ciId, activeTabKey = 'tab_1', ciDetailRelationKey = '1') { - this.visible = true + async create(ciId, activeTabKey = 'tab_1', ciDetailRelationKey = '1') { this.activeTabKey = activeTabKey if (activeTabKey === 'tab_2') { this.$nextTick(() => { @@ -167,12 +178,14 @@ export default { }) } this.ciId = ciId - this.getAttributes() - this.getCI() - this.getCIHistory() - getCITypes().then((res) => { - this.ci_types = res.ci_types - }) + await this.getCI() + if (this.hasPermission) { + this.getAttributes() + this.getCIHistory() + getCITypes().then((res) => { + this.ci_types = res.ci_types + }) + } }, getAttributes() { getCITypeGroupById(this.typeId, { need_other: 1 }) @@ -181,11 +194,14 @@ export default { }) .catch((e) => {}) }, - getCI() { - getCIById(this.ciId) + async getCI() { + await getCIById(this.ciId) .then((res) => { - // this.ci = res.ci - this.ci = res.result[0] + if (res.result.length) { + this.ci = res.result[0] + } else { + this.hasPermission = false + } }) .catch((e) => {}) }, @@ -270,9 +286,13 @@ export default { // 修改的字段为树形视图订阅的字段 则全部reload setTimeout(() => { if (_find) { - this.reload() + if (this.reload) { + this.reload() + } } else { - this.handleSearch() + if (this.handleSearch) { + this.handleSearch() + } } }, 500) }, @@ -303,23 +323,49 @@ export default { // 修改的字段为树形视图订阅的字段 则全部reload setTimeout(() => { if (_find) { - this.reload() + if (this.reload) { + this.reload() + } } else { - this.handleSearch() + if (this.handleSearch) { + this.handleSearch() + } } }, 500) }, + shareCi() { + const text = `${document.location.host}/cmdb/cidetail/${this.typeId}/${this.ciId}` + this.$copyText(text) + .then(() => { + this.$message.success(this.$t('copySuccess')) + }) + .catch(() => { + this.$message.error(this.$t('cmdb.ci.copyFailed')) + }) + }, }, } </script> -<style lang="less" scoped></style> <style lang="less"> -.ci-detail { +.ci-detail-tab { + height: 100%; + .ant-tabs-content { + height: calc(100% - 45px); + .ant-tabs-tabpane { + height: 100%; + } + } .ant-tabs-bar { margin: 0; } + .ant-tabs-extra-content { + line-height: 44px; + } .ci-detail-attr { + height: 100%; + overflow: auto; + padding: 24px; .el-descriptions-item__content { cursor: default; &:hover a { diff --git a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue index c32416b..2d95c29 100644 --- a/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue +++ b/cmdb-ui/src/modules/cmdb/views/operation_history/modules/triggerTable.vue @@ -1,5 +1,5 @@ <template> - <div> + <div :style="{ height: '100%' }"> <vxe-table show-overflow show-header-overflow @@ -7,7 +7,7 @@ size="small" class="ops-stripe-table" :data="tableData" - v-bind="ci_id ? { maxHeight: `${windowHeight - 94}px` } : { height: `${windowHeight - 225}px` }" + v-bind="ci_id ? { height: 'auto' } : { height: `${windowHeight - 225}px` }" > <vxe-column field="trigger_name" :title="$t('cmdb.history.triggerName')"> </vxe-column> <vxe-column field="type" :title="$t('type')"> 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 6651023..ee3748d 100644 --- a/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/relation_views/index.vue @@ -316,7 +316,7 @@ <!-- <GrantDrawer ref="grantDrawer" resourceTypeName="RelationView" app_id="cmdb" /> --> <CMDBGrant ref="cmdbGrant" resourceType="RelationView" app_id="cmdb" /> - <ci-detail ref="detail" :typeId="Number(currentTypeId[0])" /> + <CiDetailDrawer ref="detail" :typeId="Number(currentTypeId[0])" /> <create-instance-form ref="create" :typeIdFromRelation="Number(currentTypeId[0])" @@ -354,7 +354,7 @@ import { roleHasPermissionToGrant } from '@/modules/acl/api/permission' import { searchResourceType } from '@/modules/acl/api/resource' import SplitPane from '@/components/SplitPane' import EditAttrsPopover from '../ci/modules/editAttrsPopover.vue' -import CiDetail from '../ci/modules/CiDetail' +import CiDetailDrawer from '../ci/modules/ciDetailDrawer.vue' import CreateInstanceForm from '../ci/modules/CreateInstanceForm' import JsonEditor from '../../components/JsonEditor/jsonEditor.vue' import BatchDownload from '../../components/batchDownload/batchDownload.vue' @@ -375,7 +375,7 @@ export default { SplitPane, ElTree: Tree, EditAttrsPopover, - CiDetail, + CiDetailDrawer, CreateInstanceForm, JsonEditor, BatchDownload, 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 b11650c..9ffa723 100644 --- a/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/tree_views/index.vue @@ -371,7 +371,7 @@ } " /> - <ci-detail ref="detail" :typeId="Number(typeId)" :treeViewsLevels="treeViewsLevels" /> + <CiDetailDrawer ref="detail" :typeId="Number(typeId)" :treeViewsLevels="treeViewsLevels" /> <create-instance-form ref="create" :typeIdFromRelation="Number(typeId)" @@ -404,7 +404,7 @@ import PasswordField from '../../components/passwordField/index.vue' import SplitPane from '@/components/SplitPane' import TreeViewsNode from './modules/treeViewsNode.vue' import EditAttrsPopover from '../ci/modules/editAttrsPopover.vue' -import CiDetail from '../ci/modules/CiDetail' +import CiDetailDrawer from '../ci/modules/ciDetailDrawer.vue' import CreateInstanceForm from '../ci/modules/CreateInstanceForm' import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr' import JsonEditor from '../../components/JsonEditor/jsonEditor.vue' @@ -424,7 +424,7 @@ export default { SplitPane, TreeViewsNode, EditAttrsPopover, - CiDetail, + CiDetailDrawer, CreateInstanceForm, JsonEditor, BatchDownload,