mirror of https://github.com/veops/cmdb.git
UI: relation type define [done]
This commit is contained in:
parent
ba80ec4403
commit
0a563deb11
|
@ -119,11 +119,12 @@ class PreferenceManager(object):
|
|||
|
||||
id2type = dict()
|
||||
for view_name in result:
|
||||
result[view_name] = toposort.toposort_flatten(
|
||||
{i['child_id']: {i['parent_id']} for i in result[view_name]})
|
||||
for i in result[view_name]:
|
||||
id2type[i['parent_id']] = None
|
||||
id2type[i['child']] = None
|
||||
id2type[i['child_id']] = None
|
||||
|
||||
result[view_name] = toposort.toposort_flatten(
|
||||
{i['child_id']: {i['parent_id']} for i in result[view_name]})
|
||||
|
||||
for type_id in id2type:
|
||||
id2type[type_id] = CITypeCache.get(type_id).to_dict()
|
||||
|
|
|
@ -15,14 +15,6 @@ export function getCITypeParent (CITypeID) {
|
|||
})
|
||||
}
|
||||
|
||||
export function getRelationTypes (CITypeID, parameter) {
|
||||
return axios({
|
||||
url: '/v0.1/relation_types',
|
||||
method: 'get',
|
||||
params: parameter
|
||||
})
|
||||
}
|
||||
|
||||
export function createRelation (parentId, childrenId, relationTypeId) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_type_relations/${parentId}/${childrenId}`,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { axios } from '@/utils/request'
|
||||
|
||||
export function getRelationTypes () {
|
||||
return axios({
|
||||
url: '/v0.1/relation_types',
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
export function addRelationType (payload) {
|
||||
return axios({
|
||||
url: `/v0.1/relation_types`,
|
||||
method: 'POST',
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRelationType (rtId, payload) {
|
||||
return axios({
|
||||
url: `/v0.1/relation_types/${rtId}`,
|
||||
method: 'PUT',
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRelationType (rtId) {
|
||||
return axios({
|
||||
url: `/v0.1/relation_types/${rtId}`,
|
||||
method: 'DELETE'
|
||||
})
|
||||
}
|
|
@ -12,7 +12,23 @@ const cmdbRouter = [
|
|||
name: 'cmdb_preference',
|
||||
meta: { title: '我的订阅', icon: 'book', keepAlive: true }
|
||||
},
|
||||
// views
|
||||
// relation views
|
||||
{
|
||||
path: '/relation_views',
|
||||
component: () => import('@/views/cmdb/relation_views'),
|
||||
name: 'cmdb_relation_views',
|
||||
meta: { title: '关系视图', icon: 'link', keepAlive: true },
|
||||
hideChildrenInMenu: true,
|
||||
children: [
|
||||
{
|
||||
path: '/relation_views/:id',
|
||||
name: 'cmdb_relation_views_item',
|
||||
component: () => import('@/views/cmdb/relation_views'),
|
||||
meta: { title: '关系视图', keepAlive: true },
|
||||
hidden: true
|
||||
}]
|
||||
},
|
||||
// tree views
|
||||
{
|
||||
path: '/tree_views',
|
||||
component: () => import('@/views/cmdb/tree_views'),
|
||||
|
@ -36,33 +52,47 @@ const cmdbRouter = [
|
|||
meta: { 'title': '批量导入', icon: 'upload', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/ci_types',
|
||||
path: '/config//ci_types',
|
||||
name: 'cmdb_ci_type',
|
||||
component: RouteView,
|
||||
redirect: '/ci_type',
|
||||
redirect: '/ci_types',
|
||||
meta: { title: '模型配置', icon: 'setting', permission: ['admin'] },
|
||||
children: [
|
||||
{
|
||||
path: '/ci_types',
|
||||
path: '/config/ci_types',
|
||||
name: 'ci_type',
|
||||
hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
|
||||
component: () => import('@/views/cmdb/ci_type/list'),
|
||||
meta: { title: '模型定义', keepAlive: true }
|
||||
hideChildrenInMenu: true,
|
||||
component: () => import('@/views/cmdb/model_config/ci_type/list'),
|
||||
meta: { title: '模型管理', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/ci_types/:CITypeName/detail/:CITypeId',
|
||||
path: '/config/ci_types/:CITypeName/detail/:CITypeId',
|
||||
name: 'ci_type_detail',
|
||||
hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
|
||||
component: () => import('@/views/cmdb/ci_type/detail'),
|
||||
meta: { title: '模型配置', keepAlive: true, hidden: true },
|
||||
hideChildrenInMenu: true,
|
||||
component: () => import('@/views/cmdb/model_config/ci_type/detail'),
|
||||
meta: { title: '模型管理', keepAlive: true, hidden: true },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/attributes',
|
||||
path: '/config/attributes',
|
||||
name: 'attributes',
|
||||
hideChildrenInMenu: true, // 强制显示 MenuItem 而不是 SubMenu
|
||||
component: () => import('@/views/cmdb/attributes/index'),
|
||||
hideChildrenInMenu: true,
|
||||
component: () => import('@/views/cmdb/model_config/attributes/index'),
|
||||
meta: { title: '属性库', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/config/relation_type',
|
||||
name: 'relation_type',
|
||||
hideChildrenInMenu: true,
|
||||
component: () => import('@/views/cmdb/model_config/relation_type/index'),
|
||||
meta: { title: '关系类型', keepAlive: true }
|
||||
},
|
||||
{
|
||||
path: '/config/preference_relation',
|
||||
name: 'preference_relation',
|
||||
hideChildrenInMenu: true,
|
||||
component: () => import('@/views/cmdb/model_config/preference_relation/index'),
|
||||
meta: { title: '关系视图配置', keepAlive: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -128,7 +158,7 @@ export const generatorDynamicRouter = () => {
|
|||
component: () => import(`@/views/cmdb/ci/index`),
|
||||
name: `cmdb_${item.id}`,
|
||||
meta: { title: item.alias, icon: 'table', keepAlive: true, typeId: item.id },
|
||||
hideChildrenInMenu: true // 强制显示 MenuItem 而不是 SubMenu
|
||||
hideChildrenInMenu: true
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -143,8 +143,8 @@ import {
|
|||
} from '@/api/cmdb/CITypeAttr'
|
||||
import { STable } from '@/components'
|
||||
import { mixin, mixinDevice } from '@/utils/mixin'
|
||||
import AttributeForm from '@/views/cmdb/attributes/module/attributeForm'
|
||||
import { valueTypeMap } from '@/views/cmdb/attributes/module/const'
|
||||
import AttributeForm from '@/views/cmdb/model_config/attributes/module/attributeForm'
|
||||
import { valueTypeMap } from '@/views/cmdb/model_config/attributes/module/const'
|
||||
|
||||
export default {
|
||||
name: 'AttributesTable',
|
|
@ -103,7 +103,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { createRelation, deleteRelation, getCITypeChildren, getRelationTypes } from '@/api/cmdb/CITypeRelation'
|
||||
import { createRelation, deleteRelation, getCITypeChildren } from '@/api/cmdb/CITypeRelation'
|
||||
import { getRelationTypes } from '@/api/cmdb/relationType'
|
||||
import { getCITypes } from '@/api/cmdb/CIType'
|
||||
|
||||
import { STable } from '@/components'
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<script>
|
||||
export default {}
|
||||
</script>
|
|
@ -0,0 +1,280 @@
|
|||
<template>
|
||||
<a-card :bordered="false">
|
||||
|
||||
<div class="action-btn">
|
||||
<a-button @click="handleCreate" type="primary" style="margin-right: 0.3rem;">{{ btnName }}</a-button>
|
||||
</div>
|
||||
|
||||
<s-table
|
||||
:alert="options.alert"
|
||||
:columns="columns"
|
||||
:data="loadData"
|
||||
:pagination="{ showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条记录`, pageSizeOptions: pageSizeOptions}"
|
||||
:showPagination="false"
|
||||
:pageSize="25"
|
||||
:rowKey="record=>record.id"
|
||||
:rowSelection="options.rowSelection"
|
||||
:scroll="scroll"
|
||||
ref="table"
|
||||
size="middle"
|
||||
|
||||
>
|
||||
<div slot="filterDropdown" slot-scope="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }" class="custom-filter-dropdown">
|
||||
<a-input
|
||||
v-ant-ref="c => searchInput = c"
|
||||
:placeholder="` ${column.title}`"
|
||||
:value="selectedKeys[0]"
|
||||
@change="e => setSelectedKeys(e.target.value ? [e.target.value] : [])"
|
||||
@pressEnter="() => handleSearch(selectedKeys, confirm, column)"
|
||||
style="width: 188px; margin-bottom: 8px; display: block;"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="() => handleSearch(selectedKeys, confirm, column)"
|
||||
icon="search"
|
||||
size="small"
|
||||
style="width: 90px; margin-right: 8px"
|
||||
>搜索</a-button>
|
||||
<a-button
|
||||
@click="() => handleReset(clearFilters, column)"
|
||||
size="small"
|
||||
style="width: 90px"
|
||||
>重置</a-button>
|
||||
</div>
|
||||
<a-icon slot="filterIcon" slot-scope="filtered" type="search" :style="{ color: filtered ? '#108ee9' : undefined }" />
|
||||
|
||||
<template slot="nameSearchRender" slot-scope="text">
|
||||
<span v-if="columnSearchText.name">
|
||||
<template v-for="(fragment, i) in text.toString().split(new RegExp(`(?<=${columnSearchText.name})|(?=${columnSearchText.name})`, 'i'))">
|
||||
<mark v-if="fragment.toLowerCase() === columnSearchText.name.toLowerCase()" :key="i" class="highlight">{{ fragment }}</mark>
|
||||
<template v-else>{{ fragment }}</template>
|
||||
</template>
|
||||
</span>
|
||||
<template v-else>{{ text }}</template>
|
||||
</template>
|
||||
|
||||
<template slot="description" slot-scope="text">{{ text }}</template>
|
||||
|
||||
<span slot="action" slot-scope="text, record">
|
||||
<template>
|
||||
<a @click="handleEdit(record)">编辑</a>
|
||||
<a-divider type="vertical"/>
|
||||
|
||||
<a-popconfirm
|
||||
title="确认删除?"
|
||||
@confirm="handleDelete(record)"
|
||||
@cancel="cancel"
|
||||
okText="是"
|
||||
cancelText="否"
|
||||
>
|
||||
<a>删除</a>
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</span>
|
||||
|
||||
</s-table>
|
||||
<relation-type-form ref="relationTypeForm" :handleOk="handleOk"> </relation-type-form>
|
||||
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { STable } from '@/components'
|
||||
import RelationTypeForm from './module/relationTypeForm'
|
||||
import { getRelationTypes, deleteRelationType } from '@/api/cmdb/relationType'
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
STable,
|
||||
RelationTypeForm
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
scroll: { x: 1000, y: 500 },
|
||||
btnName: '新增关系类型',
|
||||
|
||||
formLayout: 'vertical',
|
||||
|
||||
pageSizeOptions: ['10', '25', '50', '100'],
|
||||
|
||||
columnSearchText: {
|
||||
name: ''
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
width: 150,
|
||||
title: '类型名',
|
||||
dataIndex: 'name',
|
||||
sorter: false,
|
||||
scopedSlots: {
|
||||
customRender: 'nameSearchRender',
|
||||
filterDropdown: 'filterDropdown',
|
||||
filterIcon: 'filterIcon'
|
||||
},
|
||||
onFilter: (value, record) => record.name && record.name.toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: (visible) => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
width: 150,
|
||||
title: '操作',
|
||||
key: 'operation',
|
||||
scopedSlots: { customRender: 'action' }
|
||||
}
|
||||
],
|
||||
loadData: parameter => {
|
||||
return getRelationTypes()
|
||||
.then(res => {
|
||||
const result = {}
|
||||
result.data = res
|
||||
console.log('loadData.res', result)
|
||||
return result
|
||||
})
|
||||
},
|
||||
|
||||
mdl: {},
|
||||
// 高级搜索 展开/关闭
|
||||
advanced: false,
|
||||
// 查询参数
|
||||
queryParam: {},
|
||||
// 表头
|
||||
|
||||
selectedRowKeys: [],
|
||||
selectedRows: [],
|
||||
|
||||
// custom table alert & rowSelection
|
||||
options: {
|
||||
alert: false,
|
||||
rowSelection: null
|
||||
},
|
||||
optionAlertShow: false
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
formItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 14 }
|
||||
} : {}
|
||||
},
|
||||
|
||||
horizontalFormItemLayout () {
|
||||
return {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 12 }
|
||||
}
|
||||
},
|
||||
buttonItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
wrapperCol: { span: 14, offset: 4 }
|
||||
} : {}
|
||||
}
|
||||
|
||||
},
|
||||
mounted () {
|
||||
this.setScrollY()
|
||||
},
|
||||
inject: ['reload'],
|
||||
|
||||
methods: {
|
||||
handleSearch (selectedKeys, confirm, column) {
|
||||
confirm()
|
||||
this.columnSearchText[column.dataIndex] = selectedKeys[0]
|
||||
this.queryParam[column.dataIndex] = selectedKeys[0]
|
||||
},
|
||||
|
||||
handleReset (clearFilters, column) {
|
||||
clearFilters()
|
||||
this.columnSearchText[column.dataIndex] = ''
|
||||
this.queryParam[column.dataIndex] = ''
|
||||
},
|
||||
|
||||
setScrollY () {
|
||||
this.scroll.y = window.innerHeight - this.$refs.table.$el.offsetTop - 200
|
||||
},
|
||||
|
||||
handleEdit (record) {
|
||||
console.log(record)
|
||||
this.$refs.relationTypeForm.handleEdit(record)
|
||||
},
|
||||
handleDelete (record) {
|
||||
this.deleteRelationType(record.id)
|
||||
},
|
||||
handleOk () {
|
||||
this.$refs.table.refresh()
|
||||
},
|
||||
|
||||
handleCreate () {
|
||||
this.$refs.relationTypeForm.handleCreate()
|
||||
},
|
||||
|
||||
deleteRelationType (id) {
|
||||
deleteRelationType(id)
|
||||
.then(res => {
|
||||
this.$message.success(`删除成功`)
|
||||
this.handleOk()
|
||||
})
|
||||
.catch(err => this.requestFailed(err))
|
||||
},
|
||||
requestFailed (err) {
|
||||
const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
|
||||
this.$message.error(`${msg}`)
|
||||
},
|
||||
cancel () {
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
watch: {}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search {
|
||||
margin-bottom: 54px;
|
||||
}
|
||||
|
||||
// .fold {
|
||||
// width: calc(100% - 216px);
|
||||
// display: inline-block
|
||||
// }
|
||||
|
||||
.operator {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.action-btn {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.custom-filter-dropdown {
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: rgb(255, 192, 105);
|
||||
padding: 0px;
|
||||
}
|
||||
@media screen and (max-width: 900px) {
|
||||
.fold {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<a-drawer
|
||||
:closable="false"
|
||||
:title="drawerTitle"
|
||||
:visible="drawerVisible"
|
||||
@close="onClose"
|
||||
placement="right"
|
||||
width="30%"
|
||||
>
|
||||
|
||||
<a-form :form="form" :layout="formLayout" @submit="handleSubmit">
|
||||
|
||||
<a-form-item
|
||||
:label-col="formItemLayout.labelCol"
|
||||
:wrapper-col="formItemLayout.wrapperCol"
|
||||
label="类型名"
|
||||
>
|
||||
<a-input
|
||||
name="name"
|
||||
placeholder=""
|
||||
v-decorator="['name', {rules: [{ required: true, message: '请输入资源名'}]} ]"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
<a-input
|
||||
name="id"
|
||||
type="hidden"
|
||||
v-decorator="['id', {rules: []} ]"
|
||||
/>
|
||||
</a-form-item>
|
||||
|
||||
<div
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
borderTop: '1px solid #e9e9e9',
|
||||
padding: '0.8rem 1rem',
|
||||
background: '#fff',
|
||||
|
||||
}"
|
||||
>
|
||||
<a-button @click="handleSubmit" type="primary" style="margin-right: 1rem">确定</a-button>
|
||||
<a-button @click="onClose">取消</a-button>
|
||||
|
||||
</div>
|
||||
|
||||
</a-form>
|
||||
</a-drawer>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { STable } from '@/components'
|
||||
import { addRelationType, updateRelationType } from '@/api/cmdb/relationType'
|
||||
|
||||
export default {
|
||||
name: 'RelationTypeForm',
|
||||
components: {
|
||||
STable
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
drawerTitle: '新增关系类型',
|
||||
drawerVisible: false,
|
||||
formLayout: 'vertical'
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate () {
|
||||
this.form = this.$form.createForm(this)
|
||||
},
|
||||
|
||||
computed: {
|
||||
formItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
labelCol: { span: 4 },
|
||||
wrapperCol: { span: 14 }
|
||||
} : {}
|
||||
},
|
||||
|
||||
horizontalFormItemLayout () {
|
||||
return {
|
||||
labelCol: { span: 5 },
|
||||
wrapperCol: { span: 12 }
|
||||
}
|
||||
},
|
||||
buttonItemLayout () {
|
||||
const { formLayout } = this
|
||||
return formLayout === 'horizontal' ? {
|
||||
wrapperCol: { span: 14, offset: 4 }
|
||||
} : {}
|
||||
}
|
||||
|
||||
},
|
||||
mounted () {
|
||||
},
|
||||
methods: {
|
||||
handleCreate () {
|
||||
this.drawerVisible = true
|
||||
},
|
||||
onClose () {
|
||||
this.form.resetFields()
|
||||
this.drawerVisible = false
|
||||
},
|
||||
onChange (e) {
|
||||
console.log(`checked = ${e}`)
|
||||
},
|
||||
|
||||
handleEdit (record) {
|
||||
this.drawerVisible = true
|
||||
console.log(record)
|
||||
this.$nextTick(() => {
|
||||
this.form.setFieldsValue({
|
||||
id: record.id,
|
||||
name: record.name
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
this.form.validateFields((err, values) => {
|
||||
if (!err) {
|
||||
console.log('Received values of form: ', values)
|
||||
|
||||
if (values.id) {
|
||||
this.updateResourceType(values.id, values)
|
||||
} else {
|
||||
this.createResourceType(values)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
updateResourceType (id, data) {
|
||||
updateRelationType(id, data)
|
||||
.then(res => {
|
||||
this.$message.success(`更新成功`)
|
||||
this.handleOk()
|
||||
this.onClose()
|
||||
}).catch(err => this.requestFailed(err))
|
||||
},
|
||||
|
||||
createResourceType (data) {
|
||||
addRelationType(data)
|
||||
.then(res => {
|
||||
this.$message.success(`添加成功`)
|
||||
this.handleOk()
|
||||
this.onClose()
|
||||
})
|
||||
.catch(err => this.requestFailed(err))
|
||||
},
|
||||
|
||||
requestFailed (err) {
|
||||
const msg = ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试'
|
||||
this.$message.error(`${msg}`)
|
||||
}
|
||||
|
||||
},
|
||||
watch: {},
|
||||
props: {
|
||||
handleOk: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.search {
|
||||
margin-bottom: 54px;
|
||||
}
|
||||
|
||||
.fold {
|
||||
width: calc(100% - 216px);
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.operator {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.action-btn {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
.fold {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,248 @@
|
|||
<template>
|
||||
<a-card :bordered="false">
|
||||
<a-menu v-model="current" mode="horizontal" v-if="ciTypes.length">
|
||||
<a-menu-item :key="ciType.id" v-for="ciType in ciTypes">
|
||||
<router-link
|
||||
:to="{name: 'cmdb_tree_views_item', params: {typeId: ciType.id}}"
|
||||
>{{ ciType.alias || ciTypes.name }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-alert message="请先到 我的订阅 页面完成订阅!" banner v-else></a-alert>
|
||||
<div style="clear: both; margin-top: 20px"></div>
|
||||
<template>
|
||||
<a-row :gutter="8">
|
||||
<a-col :span="5">
|
||||
<a-tree showLine :loadData="onLoadData" @select="onSelect" :treeData="treeData"></a-tree>
|
||||
</a-col>
|
||||
<a-col :span="19">
|
||||
<s-table
|
||||
v-if="ciTypes.length"
|
||||
bordered
|
||||
ref="table"
|
||||
size="middle"
|
||||
rowKey="ci_id"
|
||||
:columns="columns"
|
||||
:data="loadInstances"
|
||||
:scroll="{ x: scrollX, y: scrollY }"
|
||||
:pagination="{ showTotal: (total, range) => `${range[0]}-${range[1]} 共 ${total} 条记录`, pageSizeOptions: pageSizeOptions}"
|
||||
:pageSize="25"
|
||||
showPagination="auto"
|
||||
></s-table>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { STable } from '@/components'
|
||||
|
||||
import { getSubscribeTreeView, getSubscribeAttributes } from '@/api/cmdb/preference'
|
||||
import { searchCI } from '@/api/cmdb/ci'
|
||||
export default {
|
||||
components: { STable },
|
||||
data () {
|
||||
return {
|
||||
treeData: [],
|
||||
triggerSelect: false,
|
||||
treeNode: null,
|
||||
ciTypes: [],
|
||||
levels: [],
|
||||
typeId: null,
|
||||
current: [],
|
||||
instanceList: [],
|
||||
treeKeys: [],
|
||||
columns: [],
|
||||
pageSizeOptions: ['10', '25', '50', '100'],
|
||||
loading: false,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
|
||||
loadInstances: parameter => {
|
||||
const params = parameter || {}
|
||||
// const params = Object.assign(parameter, this.$refs.search.queryParam)
|
||||
let q = `q=_type:${this.typeId}`
|
||||
Object.keys(params).forEach(key => {
|
||||
if (!['pageNo', 'pageSize', 'sortField', 'sortOrder'].includes(key) && params[key] + '' !== '') {
|
||||
if (typeof params[key] === 'object' && params[key].length > 1) {
|
||||
q += `,${key}:(${params[key].join(';')})`
|
||||
} else if (params[key]) {
|
||||
q += `,${key}:${params[key]}`
|
||||
}
|
||||
|
||||
if (typeof params[key] === 'string') {
|
||||
q += '*'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.treeKeys.length > 0) {
|
||||
this.treeKeys.forEach((item, idx) => {
|
||||
q += `,${this.levels[idx].name}:${item}`
|
||||
})
|
||||
}
|
||||
|
||||
if (this.levels.length > this.treeKeys.length) {
|
||||
q += `&facet=${this.levels[this.treeKeys.length].name}`
|
||||
}
|
||||
|
||||
if ('pageNo' in params) {
|
||||
q += `&page=${params['pageNo']}&count=${params['pageSize']}`
|
||||
}
|
||||
|
||||
if ('sortField' in params) {
|
||||
let order = ''
|
||||
if (params['sortOrder'] !== 'ascend') {
|
||||
order = '-'
|
||||
}
|
||||
q += `&sort=${order}${params['sortField']}`
|
||||
}
|
||||
|
||||
return searchCI(q).then(res => {
|
||||
const result = {}
|
||||
result.pageNo = res.page
|
||||
result.pageSize = res.total
|
||||
result.totalCount = res.numfound
|
||||
result.totalPage = Math.ceil(res.numfound / (params.pageSize || 25))
|
||||
result.data = Object.assign([], res.result)
|
||||
result.data.forEach((item, index) => (item.key = item.ci_id))
|
||||
setTimeout(() => {
|
||||
this.setColumnWidth()
|
||||
}, 200)
|
||||
|
||||
if (Object.values(res.facet).length) {
|
||||
this.wrapTreeData(res.facet)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getCITypes()
|
||||
},
|
||||
|
||||
inject: ['reload'],
|
||||
watch: {
|
||||
'$route.path': function (newPath, oldPath) {
|
||||
this.typeId = this.$route.params.typeId
|
||||
this.getCITypes()
|
||||
this.reload()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onSelect (keys) {
|
||||
this.triggerSelect = true
|
||||
if (keys.length) {
|
||||
this.treeKeys = keys[0].split('-').filter(item => item !== '')
|
||||
}
|
||||
|
||||
this.$refs.table.refresh(true)
|
||||
},
|
||||
wrapTreeData (facet) {
|
||||
if (this.triggerSelect) {
|
||||
return
|
||||
}
|
||||
const treeData = []
|
||||
Object.values(facet)[0].forEach(item => {
|
||||
treeData.push({
|
||||
title: `${item[0]} (${item[1]})`,
|
||||
key: this.treeKeys.join('-') + '-' + item[0],
|
||||
isLeaf: this.levels.length - 1 === this.treeKeys.length
|
||||
})
|
||||
})
|
||||
if (this.treeNode === null) {
|
||||
this.treeData = treeData
|
||||
} else {
|
||||
this.treeNode.dataRef.children = treeData
|
||||
this.treeData = [...this.treeData]
|
||||
}
|
||||
},
|
||||
setColumnWidth () {
|
||||
let rows = []
|
||||
try {
|
||||
rows = document.querySelector('.ant-table-body').childNodes[0].childNodes[2].childNodes[0].childNodes
|
||||
} catch (e) {
|
||||
rows = document.querySelector('.ant-table-body').childNodes[0].childNodes[1].childNodes[0].childNodes
|
||||
}
|
||||
let scrollX = 0
|
||||
|
||||
const columns = Object.assign([], this.columns)
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
columns[i].width = rows[i].offsetWidth < 80 ? 80 : rows[i].offsetWidth
|
||||
scrollX += columns[i].width
|
||||
}
|
||||
this.columns = columns
|
||||
|
||||
this.scrollX = scrollX
|
||||
this.scrollY = window.innerHeight - this.$refs.table.$el.offsetTop - 300
|
||||
},
|
||||
|
||||
onLoadData (treeNode) {
|
||||
this.triggerSelect = false
|
||||
return new Promise(resolve => {
|
||||
if (treeNode.dataRef.children) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
this.treeKeys = treeNode.eventKey.split('-').filter(item => item !== '')
|
||||
this.treeNode = treeNode
|
||||
this.$refs.table.refresh(true)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
|
||||
getCITypes () {
|
||||
getSubscribeTreeView().then(res => {
|
||||
this.ciTypes = res
|
||||
if (this.ciTypes.length) {
|
||||
this.typeId = this.$route.params.typeId || this.ciTypes[0].id
|
||||
this.current = [this.typeId]
|
||||
this.loadColumns()
|
||||
this.levels = res.find(item => item.id === this.typeId).levels
|
||||
this.$refs.table && this.$refs.table.refresh(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
loadColumns () {
|
||||
getSubscribeAttributes(this.typeId).then(res => {
|
||||
const prefAttrList = res.attributes
|
||||
|
||||
const columns = []
|
||||
prefAttrList.forEach((item, index) => {
|
||||
const col = {}
|
||||
col.title = item.alias
|
||||
col.dataIndex = item.name
|
||||
if (index !== prefAttrList.length - 1) {
|
||||
col.width = 80
|
||||
}
|
||||
if (item.is_sortable) {
|
||||
col.sorter = true
|
||||
}
|
||||
if (item.is_choice) {
|
||||
const filters = []
|
||||
item.choice_value.forEach(item => filters.push({ text: item, value: item }))
|
||||
col.filters = filters
|
||||
}
|
||||
col.scopedSlots = { customRender: item.name }
|
||||
columns.push(col)
|
||||
})
|
||||
|
||||
this.columns = columns
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ant-menu-horizontal {
|
||||
border-bottom: 1px solid #ebedf0 !important;
|
||||
}
|
||||
.ant-menu-horizontal {
|
||||
border-bottom: 1px solid #ebedf0 !important;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue