|
@ -54,6 +54,66 @@
|
||||||
<div class="content unicode" style="display: block;">
|
<div class="content unicode" style="display: block;">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-rear</div>
|
||||||
|
<div class="code-name">&#xea02;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-front</div>
|
||||||
|
<div class="code-name">&#xea03;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-xianggang</div>
|
||||||
|
<div class="code-name">&#xea01;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-device (2)</div>
|
||||||
|
<div class="code-name">&#xea00;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-room (1)</div>
|
||||||
|
<div class="code-name">&#xe9ff;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-IDC</div>
|
||||||
|
<div class="code-name">&#xe9fe;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-region</div>
|
||||||
|
<div class="code-name">&#xe9fd;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-device</div>
|
||||||
|
<div class="code-name">&#xe9fb;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-cabinet</div>
|
||||||
|
<div class="code-name">&#xe9fc;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">veops-data_center</div>
|
||||||
|
<div class="code-name">&#xe9f9;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">ops-setting-holiday_management-copy</div>
|
<div class="name">ops-setting-holiday_management-copy</div>
|
||||||
|
@ -6102,9 +6162,9 @@
|
||||||
<pre><code class="language-css"
|
<pre><code class="language-css"
|
||||||
>@font-face {
|
>@font-face {
|
||||||
font-family: 'iconfont';
|
font-family: 'iconfont';
|
||||||
src: url('iconfont.woff2?t=1731312848138') format('woff2'),
|
src: url('iconfont.woff2?t=1732673294759') format('woff2'),
|
||||||
url('iconfont.woff?t=1731312848138') format('woff'),
|
url('iconfont.woff?t=1732673294759') format('woff'),
|
||||||
url('iconfont.ttf?t=1731312848138') format('truetype');
|
url('iconfont.ttf?t=1732673294759') format('truetype');
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||||
|
@ -6130,6 +6190,96 @@
|
||||||
<div class="content font-class">
|
<div class="content font-class">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-rear"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-rear
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-rear
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-front"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-front
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-front
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-xianggang"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-xianggang
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-xianggang
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont a-veops-device2"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-device (2)
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.a-veops-device2
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont a-veops-room1"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-room (1)
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.a-veops-room1
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-IDC"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-IDC
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-IDC
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-region"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-region
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-region
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-device"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-device
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-device
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-cabinet"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-cabinet
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-cabinet
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont veops-data_center"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-data_center
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-data_center
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont ops-setting-holidays"></span>
|
<span class="icon iconfont ops-setting-holidays"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
|
@ -15202,6 +15352,86 @@
|
||||||
<div class="content symbol">
|
<div class="content symbol">
|
||||||
<ul class="icon_lists dib-box">
|
<ul class="icon_lists dib-box">
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-rear"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-rear</div>
|
||||||
|
<div class="code-name">#veops-rear</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-front"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-front</div>
|
||||||
|
<div class="code-name">#veops-front</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-xianggang"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-xianggang</div>
|
||||||
|
<div class="code-name">#veops-xianggang</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#a-veops-device2"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-device (2)</div>
|
||||||
|
<div class="code-name">#a-veops-device2</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#a-veops-room1"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-room (1)</div>
|
||||||
|
<div class="code-name">#a-veops-room1</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-IDC"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-IDC</div>
|
||||||
|
<div class="code-name">#veops-IDC</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-region"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-region</div>
|
||||||
|
<div class="code-name">#veops-region</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-device"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-device</div>
|
||||||
|
<div class="code-name">#veops-device</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-cabinet"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-cabinet</div>
|
||||||
|
<div class="code-name">#veops-cabinet</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#veops-data_center"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-data_center</div>
|
||||||
|
<div class="code-name">#veops-data_center</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#ops-setting-holidays"></use>
|
<use xlink:href="#ops-setting-holidays"></use>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "iconfont"; /* Project id 3857903 */
|
font-family: "iconfont"; /* Project id 3857903 */
|
||||||
src: url('iconfont.woff2?t=1731312848138') format('woff2'),
|
src: url('iconfont.woff2?t=1732673294759') format('woff2'),
|
||||||
url('iconfont.woff?t=1731312848138') format('woff'),
|
url('iconfont.woff?t=1732673294759') format('woff'),
|
||||||
url('iconfont.ttf?t=1731312848138') format('truetype');
|
url('iconfont.ttf?t=1732673294759') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -13,6 +13,46 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.veops-rear:before {
|
||||||
|
content: "\ea02";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-front:before {
|
||||||
|
content: "\ea03";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-xianggang:before {
|
||||||
|
content: "\ea01";
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-veops-device2:before {
|
||||||
|
content: "\ea00";
|
||||||
|
}
|
||||||
|
|
||||||
|
.a-veops-room1:before {
|
||||||
|
content: "\e9ff";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-IDC:before {
|
||||||
|
content: "\e9fe";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-region:before {
|
||||||
|
content: "\e9fd";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-device:before {
|
||||||
|
content: "\e9fb";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-cabinet:before {
|
||||||
|
content: "\e9fc";
|
||||||
|
}
|
||||||
|
|
||||||
|
.veops-data_center:before {
|
||||||
|
content: "\e9f9";
|
||||||
|
}
|
||||||
|
|
||||||
.ops-setting-holidays:before {
|
.ops-setting-holidays:before {
|
||||||
content: "\e9fa";
|
content: "\e9fa";
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,76 @@
|
||||||
"css_prefix_text": "",
|
"css_prefix_text": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "42510712",
|
||||||
|
"name": "veops-rear",
|
||||||
|
"font_class": "veops-rear",
|
||||||
|
"unicode": "ea02",
|
||||||
|
"unicode_decimal": 59906
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42510708",
|
||||||
|
"name": "veops-front",
|
||||||
|
"font_class": "veops-front",
|
||||||
|
"unicode": "ea03",
|
||||||
|
"unicode_decimal": 59907
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42497603",
|
||||||
|
"name": "veops-xianggang",
|
||||||
|
"font_class": "veops-xianggang",
|
||||||
|
"unicode": "ea01",
|
||||||
|
"unicode_decimal": 59905
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42485038",
|
||||||
|
"name": "veops-device (2)",
|
||||||
|
"font_class": "a-veops-device2",
|
||||||
|
"unicode": "ea00",
|
||||||
|
"unicode_decimal": 59904
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42455620",
|
||||||
|
"name": "veops-room (1)",
|
||||||
|
"font_class": "a-veops-room1",
|
||||||
|
"unicode": "e9ff",
|
||||||
|
"unicode_decimal": 59903
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42455607",
|
||||||
|
"name": "veops-IDC",
|
||||||
|
"font_class": "veops-IDC",
|
||||||
|
"unicode": "e9fe",
|
||||||
|
"unicode_decimal": 59902
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42455609",
|
||||||
|
"name": "veops-region",
|
||||||
|
"font_class": "veops-region",
|
||||||
|
"unicode": "e9fd",
|
||||||
|
"unicode_decimal": 59901
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42448953",
|
||||||
|
"name": "veops-device",
|
||||||
|
"font_class": "veops-device",
|
||||||
|
"unicode": "e9fb",
|
||||||
|
"unicode_decimal": 59899
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42448948",
|
||||||
|
"name": "veops-cabinet",
|
||||||
|
"font_class": "veops-cabinet",
|
||||||
|
"unicode": "e9fc",
|
||||||
|
"unicode_decimal": 59900
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "42433324",
|
||||||
|
"name": "veops-data_center",
|
||||||
|
"font_class": "veops-data_center",
|
||||||
|
"unicode": "e9f9",
|
||||||
|
"unicode_decimal": 59897
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "42337844",
|
"icon_id": "42337844",
|
||||||
"name": "ops-setting-holiday_management-copy",
|
"name": "ops-setting-holiday_management-copy",
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
import { axios } from '@/utils/request'
|
||||||
|
|
||||||
|
export function getDCIMTreeView(params) {
|
||||||
|
return axios({
|
||||||
|
url: '/v0.1/dcim/tree_view ',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDCIMById(type, id) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/${type}/${id}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postDCIM(type, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/${type}`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putDCIM(type, id, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/${type}/${id}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteDCIM(type, id) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/${type}/${id}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDCIMRacks(id, params) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/server_room/${id}/racks`,
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postDevice(rackId, deviceId, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteDevice(rackId, deviceId) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
|
||||||
|
method: 'DELETE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function putDevice(rackId, deviceId, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function migrateDevice(rackId, deviceId, data) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/rack/${rackId}/device/${deviceId}/migrate`,
|
||||||
|
method: 'PUT',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDCIMHistoryOperate(params) {
|
||||||
|
return axios({
|
||||||
|
url: `/v0.1/dcim/history/operate`,
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 554 KiB |
After Width: | Height: | Size: 13 KiB |
|
@ -15,7 +15,7 @@
|
||||||
highlight-hover-row
|
highlight-hover-row
|
||||||
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
:checkbox-config="{ reserve: true, highlight: true, range: true }"
|
||||||
:edit-config="{ trigger: 'dblclick', mode: 'row', showIcon: false }"
|
:edit-config="{ trigger: 'dblclick', mode: 'row', showIcon: false }"
|
||||||
:sort-config="{ remote: true, trigger: 'cell' }"
|
:sort-config="sortConfig"
|
||||||
:row-key="true"
|
:row-key="true"
|
||||||
:column-key="true"
|
:column-key="true"
|
||||||
:cell-style="getCellStyle"
|
:cell-style="getCellStyle"
|
||||||
|
@ -170,7 +170,7 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</vxe-table-column>
|
</vxe-table-column>
|
||||||
<vxe-column align="left" field="operate" fixed="right" width="80">
|
<vxe-column v-if="showOperation" align="left" field="operate" fixed="right" width="80">
|
||||||
<template #header>
|
<template #header>
|
||||||
<span>{{ $t('operation') }}</span>
|
<span>{{ $t('operation') }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -272,6 +272,18 @@ export default {
|
||||||
data: {
|
data: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
},
|
||||||
|
sortConfig: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
remote: true,
|
||||||
|
trigger: 'cell'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 是否展示操作列
|
||||||
|
showOperation: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,8 @@ const cmdb_en = {
|
||||||
relationType: 'Relation Type',
|
relationType: 'Relation Type',
|
||||||
ad: 'AutoDiscovery',
|
ad: 'AutoDiscovery',
|
||||||
cidetail: 'CI Detail',
|
cidetail: 'CI Detail',
|
||||||
scene: 'Scene'
|
scene: 'Scene',
|
||||||
|
dcim: 'DCIM'
|
||||||
},
|
},
|
||||||
ciType: {
|
ciType: {
|
||||||
ciType: 'CIType',
|
ciType: 'CIType',
|
||||||
|
@ -844,6 +845,56 @@ if __name__ == "__main__":
|
||||||
batchRecycle: 'Batch Recycle',
|
batchRecycle: 'Batch Recycle',
|
||||||
batchRecycleInProgress: 'Recycle in batches, {total} in total, {successNum} successful, {errorNum} failed',
|
batchRecycleInProgress: 'Recycle in batches, {total} in total, {successNum} successful, {errorNum} failed',
|
||||||
batchRecycleCompleted: 'Batch Recycle Completed',
|
batchRecycleCompleted: 'Batch Recycle Completed',
|
||||||
|
},
|
||||||
|
dcim: {
|
||||||
|
addRegion: 'Add Region',
|
||||||
|
addIDC: 'Add IDC',
|
||||||
|
addServerRoom: 'Add Server Room',
|
||||||
|
addRack: 'Add Rack',
|
||||||
|
editRegion: 'Edit Region',
|
||||||
|
editIDC: 'Edit IDC',
|
||||||
|
editServerRoom: 'Edit Server Room',
|
||||||
|
editRack: 'Edit Rack',
|
||||||
|
rackCount: 'Rack Count',
|
||||||
|
total: 'Total',
|
||||||
|
deviceCount: 'Device Count',
|
||||||
|
utilizationRation: 'Utilization Ration',
|
||||||
|
used: 'Used',
|
||||||
|
unused: 'Unused',
|
||||||
|
rackSearchTip: 'Please search for rack name',
|
||||||
|
viewDetail: 'View Detail',
|
||||||
|
deleteNode: 'Delete Node',
|
||||||
|
editNode: 'Edit Node',
|
||||||
|
roomNullTip: 'Please select the server room on the left first',
|
||||||
|
unitAbnormal: 'Unit Abnormal',
|
||||||
|
rack: 'Rack',
|
||||||
|
unitCount: 'Unit Count',
|
||||||
|
rackView: 'Rack View',
|
||||||
|
deviceList: 'Device List',
|
||||||
|
operationLog: 'Operation Log',
|
||||||
|
frontView: 'Front View',
|
||||||
|
rearView: 'Rear View',
|
||||||
|
addDevice: 'Add Device',
|
||||||
|
device: 'Device',
|
||||||
|
ciType: 'CIType',
|
||||||
|
unitStart: 'Unit Start',
|
||||||
|
toChange: 'To Change',
|
||||||
|
abnormalModalTip1: 'and',
|
||||||
|
abnormalModalTip2: ' location duplication',
|
||||||
|
abnormalModalTip3: 'Please select one of the devices to change',
|
||||||
|
remove: 'Remove',
|
||||||
|
migrate: 'Migrate',
|
||||||
|
deviceMigrate: 'Device Migrate',
|
||||||
|
migrationSuccess: 'Migration Success',
|
||||||
|
removeDeviceTip: 'Confirmed to remove {deviceName} device?',
|
||||||
|
operationTime: 'Operation Time',
|
||||||
|
operationUser: 'Operation User',
|
||||||
|
operationType: 'Operation Type',
|
||||||
|
deviceType: 'Device Type',
|
||||||
|
deviceName: 'Device Name',
|
||||||
|
removeDevice: 'Remove Device',
|
||||||
|
moveDevice: 'Move Device',
|
||||||
|
rackDetail: 'Rack Detail'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default cmdb_en
|
export default cmdb_en
|
||||||
|
|
|
@ -25,7 +25,8 @@ const cmdb_zh = {
|
||||||
relationType: '关系类型',
|
relationType: '关系类型',
|
||||||
ad: '自动发现',
|
ad: '自动发现',
|
||||||
cidetail: 'CI 详情',
|
cidetail: 'CI 详情',
|
||||||
scene: '场景'
|
scene: '场景',
|
||||||
|
dcim: '数据中心'
|
||||||
},
|
},
|
||||||
ciType: {
|
ciType: {
|
||||||
ciType: '模型',
|
ciType: '模型',
|
||||||
|
@ -843,6 +844,56 @@ if __name__ == "__main__":
|
||||||
batchRecycle: '批量回收',
|
batchRecycle: '批量回收',
|
||||||
batchRecycleInProgress: '正在批量回收,共{total}个,成功{successNum}个,失败{errorNum}个',
|
batchRecycleInProgress: '正在批量回收,共{total}个,成功{successNum}个,失败{errorNum}个',
|
||||||
batchRecycleCompleted: '批量回收已完成',
|
batchRecycleCompleted: '批量回收已完成',
|
||||||
|
},
|
||||||
|
dcim: {
|
||||||
|
addRegion: '新增区域',
|
||||||
|
addIDC: '新增数据中心',
|
||||||
|
addServerRoom: '新增机房',
|
||||||
|
addRack: '添加机柜',
|
||||||
|
editRegion: '编辑区域',
|
||||||
|
editIDC: '编辑数据中心',
|
||||||
|
editServerRoom: '编辑机房',
|
||||||
|
editRack: '编辑机柜',
|
||||||
|
rackCount: '机柜数',
|
||||||
|
total: '总数',
|
||||||
|
deviceCount: '设备数',
|
||||||
|
utilizationRation: '利用率',
|
||||||
|
used: '已使用',
|
||||||
|
unused: '未使用',
|
||||||
|
rackSearchTip: '请搜索机柜名称',
|
||||||
|
viewDetail: '查看详情',
|
||||||
|
deleteNode: '删除节点',
|
||||||
|
editNode: '编辑节点',
|
||||||
|
roomNullTip: '请先选择左侧的机房',
|
||||||
|
unitAbnormal: 'U位异常',
|
||||||
|
rack: '机柜',
|
||||||
|
unitCount: 'U位数',
|
||||||
|
rackView: '机柜视图',
|
||||||
|
deviceList: '设备列表',
|
||||||
|
operationLog: '操作记录',
|
||||||
|
frontView: '前视图',
|
||||||
|
rearView: '后视图',
|
||||||
|
addDevice: '添加设备',
|
||||||
|
device: '设备',
|
||||||
|
ciType: '模型',
|
||||||
|
unitStart: '起始U位',
|
||||||
|
toChange: '去修改',
|
||||||
|
abnormalModalTip1: '和',
|
||||||
|
abnormalModalTip2: ' 位置重复',
|
||||||
|
abnormalModalTip3: '请选择其中一台设备进行修改',
|
||||||
|
remove: '移除',
|
||||||
|
migrate: '迁移',
|
||||||
|
deviceMigrate: '设备迁移',
|
||||||
|
migrationSuccess: '迁移成功',
|
||||||
|
removeDeviceTip: '确认要移除 {deviceName} 设备吗?',
|
||||||
|
operationTime: '操作时间',
|
||||||
|
operationUser: '操作人',
|
||||||
|
operationType: '操作类型',
|
||||||
|
deviceType: '设备类型',
|
||||||
|
deviceName: '设备名',
|
||||||
|
removeDevice: '删除设备',
|
||||||
|
moveDevice: '移动设备',
|
||||||
|
rackDetail: '机柜详情'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default cmdb_zh
|
export default cmdb_zh
|
||||||
|
|
|
@ -81,6 +81,12 @@ const genCmdbRoutes = async () => {
|
||||||
name: 'cmdb_ipam',
|
name: 'cmdb_ipam',
|
||||||
meta: { title: 'IPAM', appName: 'cmdb', icon: 'veops-ipam', selectedIcon: 'veops-ipam', keepAlive: false, permission: ['admin', 'cmdb_admin'] }
|
meta: { title: 'IPAM', appName: 'cmdb', icon: 'veops-ipam', selectedIcon: 'veops-ipam', keepAlive: false, permission: ['admin', 'cmdb_admin'] }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/cmdb/dcim',
|
||||||
|
component: () => import('../views/dcim'),
|
||||||
|
name: 'cmdb_dcim',
|
||||||
|
meta: { title: 'cmdb.menu.dcim', appName: 'cmdb', icon: 'veops-data_center', selectedIcon: 'veops-data_center', keepAlive: false, permission: ['cmdb_admin', 'admin'] }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/disabled2',
|
path: '/cmdb/disabled2',
|
||||||
name: 'cmdb_disabled2',
|
name: 'cmdb_disabled2',
|
||||||
|
|
|
@ -178,7 +178,7 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
<a v-if="!isEdit && !attr.is_computed && !attr.sys_computed" @click="handleEdit" :style="{ opacity: 0 }"><a-icon type="edit"/></a>
|
<a v-if="!isEdit && !attr.is_computed && !attr.sys_computed && showEdit" @click="handleEdit" :style="{ opacity: 0 }"><a-icon type="edit"/></a>
|
||||||
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
<JsonEditor ref="jsonEditor" @jsonEditorOk="jsonEditorOk" />
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -204,6 +204,10 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
|
showEdit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -688,19 +688,19 @@ export default {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #A5A9BC;
|
color: #A5A9BC;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="less">
|
.ops-stripe-table {
|
||||||
.ops-stripe-table .vxe-body--row.row--stripe.relation-table-divider {
|
/deep/ .relation-table-divider {
|
||||||
background-color: #b1b8d3 !important;
|
background-color: #b1b8d3 !important;
|
||||||
}
|
|
||||||
.ops-stripe-table .vxe-body--row.relation-table-parent {
|
td {
|
||||||
background-color: #f5f8ff !important;
|
height: 2px !important;
|
||||||
}
|
line-height: 2px !important;
|
||||||
.relation-table-divider {
|
}
|
||||||
td {
|
}
|
||||||
height: 1px !important;
|
|
||||||
line-height: 1px !important;
|
/deep/ .relation-table-parent {
|
||||||
|
background-color: #f5f8ff !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
:width="700"
|
||||||
|
:title="$t(modalTitle)"
|
||||||
|
:confirmLoading="confirmLoading"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="dcimFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="formRules"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:wrapper-col="{ span: 18 }"
|
||||||
|
class="dcim-form"
|
||||||
|
>
|
||||||
|
<a-form-model-item
|
||||||
|
v-for="(item) in formList"
|
||||||
|
:key="item.name"
|
||||||
|
:label="item.alias || item.name"
|
||||||
|
:prop="item.name"
|
||||||
|
>
|
||||||
|
<CIReferenceAttr
|
||||||
|
v-if="item.is_reference"
|
||||||
|
:referenceTypeId="item.reference_type_id"
|
||||||
|
:isList="item.is_list"
|
||||||
|
:referenceShowAttrName="item.showAttrName"
|
||||||
|
:initSelectOption="getInitReferenceSelectOption(item)"
|
||||||
|
v-model="form[item.name]"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
v-else-if="item.is_choice"
|
||||||
|
:mode="item.is_list ? 'multiple' : 'default'"
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
v-model="form[item.name]"
|
||||||
|
>
|
||||||
|
<a-icon slot="suffixIcon" type="caret-down" />
|
||||||
|
<a-select-option
|
||||||
|
v-for="(choiceItem, choiceIndex) in item.selectOption"
|
||||||
|
:key="choiceIndex"
|
||||||
|
:value="choiceItem.value"
|
||||||
|
>
|
||||||
|
{{ choiceItem.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-switch
|
||||||
|
v-else-if="item.is_bool"
|
||||||
|
v-model="form[item.name]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-input-number
|
||||||
|
v-model="form[item.name]"
|
||||||
|
class="dcim-form-input"
|
||||||
|
v-else-if="(item.value_type === '0' || item.value_type === '1') && !item.is_list"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-date-picker
|
||||||
|
v-else-if="(item.value_type === '4' || item.value_type === '3') && !item.is_list"
|
||||||
|
v-model="form[item.name]"
|
||||||
|
class="dcim-form-input"
|
||||||
|
:format="item.value_type === '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||||
|
:valueFormat="item.value_type === '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
|
||||||
|
:showTime="item.value_type === '4' ? false : { format: 'HH:mm:ss' }"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-input
|
||||||
|
v-else
|
||||||
|
:placeholder="$t('placeholder1')"
|
||||||
|
v-model="form[item.name]"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { postDCIM, putDCIM } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
|
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||||
|
import { DCIM_TYPE } from '../constants'
|
||||||
|
|
||||||
|
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIMForm',
|
||||||
|
components: {
|
||||||
|
CIReferenceAttr
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
allAttrList: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
nodeId: null,
|
||||||
|
parentId: null,
|
||||||
|
dcimType: '',
|
||||||
|
|
||||||
|
formList: [],
|
||||||
|
form: {},
|
||||||
|
formRules: {},
|
||||||
|
|
||||||
|
confirmLoading: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
modalTitle() {
|
||||||
|
switch (this.dcimType) {
|
||||||
|
case DCIM_TYPE.REGION:
|
||||||
|
return this.nodeId ? 'cmdb.dcim.editRegion' : 'cmdb.dcim.addRegion'
|
||||||
|
case DCIM_TYPE.IDC:
|
||||||
|
return this.nodeId ? 'cmdb.dcim.editIDC' : 'cmdb.dcim.addIDC'
|
||||||
|
case DCIM_TYPE.SERVER_ROOM:
|
||||||
|
return this.nodeId ? 'cmdb.dcim.editServerRoom' : 'cmdb.dcim.addServerRoom'
|
||||||
|
case DCIM_TYPE.RACK:
|
||||||
|
return this.nodeId ? 'cmdb.dcim.editRack' : 'cmdb.dcim.addRack'
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async open({
|
||||||
|
nodeId = null,
|
||||||
|
parentId = null,
|
||||||
|
dcimType = ''
|
||||||
|
}) {
|
||||||
|
this.nodeId = nodeId
|
||||||
|
|
||||||
|
let nodeData = {}
|
||||||
|
if (nodeId) {
|
||||||
|
const res = await searchCI({
|
||||||
|
q: `_id:${nodeId}`,
|
||||||
|
count: 9999
|
||||||
|
})
|
||||||
|
nodeData = res?.result?.[0] || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.parentId = parentId
|
||||||
|
this.dcimType = dcimType
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
const form = {}
|
||||||
|
const formRules = {}
|
||||||
|
let formList = []
|
||||||
|
|
||||||
|
let attrList = _.cloneDeep(this.allAttrList?.[dcimType]?.attributes)
|
||||||
|
attrList = attrList?.filter?.((attr) => !attr.sys_computed && !attr.is_computed) || []
|
||||||
|
|
||||||
|
if (attrList.length) {
|
||||||
|
attrList.forEach((attr) => {
|
||||||
|
let value = nodeData?.[attr.name] ?? attr?.default?.default ?? undefined
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(value) &&
|
||||||
|
['0', '1', '2', '9'].includes(attr.value_type)
|
||||||
|
) {
|
||||||
|
value = value.join(',')
|
||||||
|
}
|
||||||
|
form[attr.name] = value
|
||||||
|
|
||||||
|
if (attr?.is_choice) {
|
||||||
|
const choice_value = attr?.choice_value || []
|
||||||
|
attr.selectOption = choice_value.map((item) => {
|
||||||
|
return {
|
||||||
|
label: item?.[1]?.label || item?.[0] || '',
|
||||||
|
value: item?.[0]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
formList.push(attr)
|
||||||
|
|
||||||
|
if (attr.is_required) {
|
||||||
|
formRules[attr.name] = [
|
||||||
|
{
|
||||||
|
required: true, message: attr?.is_choice ? this.$t('placeholder2') : this.$t('placeholder1')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
formList = await this.handleReferenceAttr(formList, form)
|
||||||
|
|
||||||
|
this.form = form
|
||||||
|
this.formList = formList
|
||||||
|
this.formRules = formRules
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleReferenceAttr(formList, ci) {
|
||||||
|
const map = {}
|
||||||
|
formList.forEach((attr) => {
|
||||||
|
if (attr?.is_reference && attr?.reference_type_id && ci[attr.name]) {
|
||||||
|
const ids = Array.isArray(ci[attr.name]) ? ci[attr.name] : ci[attr.name] ? [ci[attr.name]] : []
|
||||||
|
if (ids.length) {
|
||||||
|
if (!map?.[attr.reference_type_id]) {
|
||||||
|
map[attr.reference_type_id] = {}
|
||||||
|
}
|
||||||
|
ids.forEach((id) => {
|
||||||
|
map[attr.reference_type_id][id] = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!Object.keys(map).length) {
|
||||||
|
return formList
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciTypesRes = await getCITypes({
|
||||||
|
type_ids: Object.keys(map).join(',')
|
||||||
|
})
|
||||||
|
const showAttrNameMap = {}
|
||||||
|
ciTypesRes.ci_types.forEach((ciType) => {
|
||||||
|
showAttrNameMap[ciType.id] = ciType?.show_name || ciType?.unique_name || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const allRes = await Promise.all(
|
||||||
|
Object.keys(map).map((key) => {
|
||||||
|
return searchCI({
|
||||||
|
q: `_type:${key},_id:(${Object.keys(map[key]).join(';')})`,
|
||||||
|
count: 9999
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const ciNameMap = {}
|
||||||
|
allRes.forEach((res) => {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
ciNameMap[item._id] = item
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
formList.forEach((attr) => {
|
||||||
|
if (attr?.is_reference && attr?.reference_type_id) {
|
||||||
|
attr.showAttrName = showAttrNameMap?.[attr?.reference_type_id] || ''
|
||||||
|
|
||||||
|
const referenceShowAttrNameMap = {}
|
||||||
|
const referenceCIIds = ci[attr.name];
|
||||||
|
(Array.isArray(referenceCIIds) ? referenceCIIds : referenceCIIds ? [referenceCIIds] : []).forEach((id) => {
|
||||||
|
referenceShowAttrNameMap[id] = ciNameMap?.[id]?.[attr.showAttrName] ?? id
|
||||||
|
})
|
||||||
|
attr.referenceShowAttrNameMap = referenceShowAttrNameMap
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return formList
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCancel() {
|
||||||
|
this.visible = false
|
||||||
|
this.nodeId = null
|
||||||
|
this.parentId = null
|
||||||
|
this.dcimType = ''
|
||||||
|
this.form = {}
|
||||||
|
this.formRules = {}
|
||||||
|
this.formList = []
|
||||||
|
this.confirmLoading = false
|
||||||
|
|
||||||
|
this.$refs.dcimFormRef.clearValidate()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
this.$refs.dcimFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.confirmLoading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.nodeId) {
|
||||||
|
await putDCIM(
|
||||||
|
this.dcimType,
|
||||||
|
this.nodeId,
|
||||||
|
{
|
||||||
|
...this.form,
|
||||||
|
parent_id: Number(this.parentId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await postDCIM(
|
||||||
|
this.dcimType,
|
||||||
|
{
|
||||||
|
...this.form,
|
||||||
|
parent_id: Number(this.parentId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.$emit('ok', {
|
||||||
|
dcimType: this.dcimType,
|
||||||
|
editType: this.nodeId ? 'edit' : 'create'
|
||||||
|
})
|
||||||
|
this.handleCancel()
|
||||||
|
} catch (error) {
|
||||||
|
console.log('submit fail', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.confirmLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitReferenceSelectOption(attr) {
|
||||||
|
const option = Object.keys(attr?.referenceShowAttrNameMap || {}).map((key) => {
|
||||||
|
return {
|
||||||
|
key: Number(key),
|
||||||
|
title: attr?.referenceShowAttrNameMap?.[Number(key)] ?? ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dcim-form {
|
||||||
|
padding-right: 12px;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,184 @@
|
||||||
|
<template>
|
||||||
|
<div class="dcim-stats">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in statsList"
|
||||||
|
:key="index"
|
||||||
|
class="dcim-stats-card"
|
||||||
|
>
|
||||||
|
<div class="dcim-stats-card-left">
|
||||||
|
<div class="dcim-stat-card-title">{{ $t(item.title) }}</div>
|
||||||
|
|
||||||
|
<div class="dcim-stats-card-row">
|
||||||
|
<div
|
||||||
|
v-for="(data, dataIndex) in item.countList"
|
||||||
|
:key="dataIndex"
|
||||||
|
class="dcim-stats-card-count"
|
||||||
|
>
|
||||||
|
<span class="dcim-stats-card-count-label">{{ $t(data.label) }}:</span>
|
||||||
|
<span class="dcim-stats-card-count-value">{{ data.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="item.icon"
|
||||||
|
class="dcim-stats-card-icon"
|
||||||
|
>
|
||||||
|
<ops-icon
|
||||||
|
:type="item.icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DCIMStatsChart
|
||||||
|
v-else-if="item.chartData"
|
||||||
|
:chartData="item.chartData"
|
||||||
|
:chartRatio="item.chartRatio"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DCIMStatsChart from './dcimStatsChart.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIMStats',
|
||||||
|
props: {
|
||||||
|
statsData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
DCIMStatsChart
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
statsList() {
|
||||||
|
const {
|
||||||
|
device_count = 0,
|
||||||
|
rack_count = 0,
|
||||||
|
u_count = 0,
|
||||||
|
u_used_count = 0,
|
||||||
|
} = this.statsData || {}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'cmdb.dcim.rackCount',
|
||||||
|
icon: 'veops-cabinet',
|
||||||
|
countList: [
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.total',
|
||||||
|
value: rack_count
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'cmdb.dcim.deviceCount',
|
||||||
|
icon: 'veops-device',
|
||||||
|
countList: [
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.total',
|
||||||
|
value: device_count
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'cmdb.dcim.utilizationRation',
|
||||||
|
countList: [
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.used',
|
||||||
|
value: `${u_used_count}u`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.unused',
|
||||||
|
value: `${u_count - u_used_count}u`
|
||||||
|
}
|
||||||
|
],
|
||||||
|
chartRatio: u_used_count > 0 && u_count > 0 ? Math.round((u_used_count / u_count) * 100) : 0,
|
||||||
|
chartData: [
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.used',
|
||||||
|
value: u_used_count,
|
||||||
|
color: '#009FA9'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'cmdb.dcim.unused',
|
||||||
|
value: u_count - u_used_count,
|
||||||
|
color: '#17D4B0'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dcim-stats {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
column-gap: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&-card {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 24px;
|
||||||
|
background-color: #F7F8FA;
|
||||||
|
filter: drop-shadow(0px 0px 12px rgba(231, 236, 239, 0.10));
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 12px;
|
||||||
|
column-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1D2129;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: 52px;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 32px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,110 @@
|
||||||
|
<template>
|
||||||
|
<div class="stats-chart">
|
||||||
|
<div
|
||||||
|
class="stats-chart-pie"
|
||||||
|
ref="statsChartRef"
|
||||||
|
></div>
|
||||||
|
<div class="stats-chart-ratio">
|
||||||
|
{{ chartRatio }}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIMStatsChart',
|
||||||
|
props: {
|
||||||
|
chartRatio: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
chartData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
chartData: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler(data) {
|
||||||
|
this.updateChart(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.dispose()
|
||||||
|
this.chart = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateChart(data) {
|
||||||
|
const option = {
|
||||||
|
color: data?.map?.((item) => item.color) || [],
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['60%', '85%'],
|
||||||
|
data: data?.map((item) => {
|
||||||
|
return {
|
||||||
|
name: this.$t(item?.label),
|
||||||
|
value: item?.value || 0
|
||||||
|
}
|
||||||
|
}) || [],
|
||||||
|
itemStyle: {
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (!this.chart) {
|
||||||
|
const el = this.$refs.statsChartRef
|
||||||
|
this.chart = echarts.init(el)
|
||||||
|
}
|
||||||
|
this.chart.setOption(option)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.stats-chart {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&-pie {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-ratio {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,376 @@
|
||||||
|
<template>
|
||||||
|
<div class="dcim-main" ref="rackMainRef">
|
||||||
|
<div v-if="!roomId" class="dcim-main-null">
|
||||||
|
<img class="dcim-main-null-img" :src="require(`@/modules/cmdb/assets/dcim/dcim_null.png`)"></img>
|
||||||
|
<div class="dcim-main-null-tip">{{ $t('noData') }}</div>
|
||||||
|
<div class="dcim-main-null-tip2">{{ $t('cmdb.dcim.roomNullTip') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<DCIMStats :statsData="statsData" />
|
||||||
|
|
||||||
|
<div class="dcim-main-row">
|
||||||
|
<div class="dcim-main-filter">
|
||||||
|
<a-input-search
|
||||||
|
v-model="searchValue"
|
||||||
|
:placeholder="$t('cmdb.dcim.rackSearchTip')"
|
||||||
|
class="dcim-main-row-search"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
class="dcim-main-row-select"
|
||||||
|
:getPopupContainer="(trigger) => trigger.parentElement"
|
||||||
|
v-model="currentRackType"
|
||||||
|
>
|
||||||
|
<a-icon slot="suffixIcon" type="caret-down" />
|
||||||
|
<a-select-option
|
||||||
|
v-for="(item) in rackTypeSelectOption"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:class="item.value === 'unitAbnormal' ? 'dcim-main-row-select-unitAbnormal' : ''"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dcim-main-row-right">
|
||||||
|
<div class="dcim-main-layout">
|
||||||
|
<div
|
||||||
|
v-for="(item) in layoutList"
|
||||||
|
:key="item.value"
|
||||||
|
:class="['dcim-main-layout-item', currentLayout === item.value ?'dcim-main-layout-item-active' : '']"
|
||||||
|
@click="handleChangeLayout(item.value)"
|
||||||
|
>
|
||||||
|
<ops-icon :type="item.icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
class="ops-button-ghost"
|
||||||
|
ghost
|
||||||
|
@click="addRack"
|
||||||
|
>
|
||||||
|
<a-icon type="plus-circle" />
|
||||||
|
{{ $t('cmdb.dcim.addRack') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="rack-wrap"
|
||||||
|
>
|
||||||
|
<RackGrid
|
||||||
|
v-if="currentLayout === 'grid'"
|
||||||
|
:rackList="filterRackList"
|
||||||
|
@openRackDetail="openRackDetail"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RackTable
|
||||||
|
v-if="currentLayout === 'table'"
|
||||||
|
:rackList="filterRackList"
|
||||||
|
:columns="columns"
|
||||||
|
:preferenceAttrList="preferenceAttrList"
|
||||||
|
:CITypeId="rackCITYpe.id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RackDetail
|
||||||
|
ref="rackDetailRef"
|
||||||
|
:roomId="roomId"
|
||||||
|
:rackCITYpe="rackCITYpe"
|
||||||
|
:rackList="rackList"
|
||||||
|
@openForm="(data) => $emit('openForm', data)"
|
||||||
|
@refreshRackList="getRackList"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { DCIM_TYPE } from '../../constants.js'
|
||||||
|
import { getDCIMRacks } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import { getCITableColumns } from '@/modules/cmdb/utils/helper'
|
||||||
|
|
||||||
|
import DCIMStats from './dcimStats.vue'
|
||||||
|
import RackGrid from './rackGrid.vue'
|
||||||
|
import RackTable from './rackTable.vue'
|
||||||
|
import RackDetail from '../rackDetail/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIMMain',
|
||||||
|
components: {
|
||||||
|
DCIMStats,
|
||||||
|
RackGrid,
|
||||||
|
RackTable,
|
||||||
|
RackDetail
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
roomId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
attrObj: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
rackCITYpe: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
preferenceAttrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchValue: '',
|
||||||
|
currentRackType: 'all',
|
||||||
|
rackList: [],
|
||||||
|
columns: [],
|
||||||
|
|
||||||
|
statsData: {},
|
||||||
|
|
||||||
|
currentLayout: 'grid',
|
||||||
|
layoutList: [
|
||||||
|
{
|
||||||
|
value: 'grid',
|
||||||
|
icon: 'veops-map_view'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'table',
|
||||||
|
icon: 'monitor-list_view'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rackTypeSelectOption() {
|
||||||
|
const selectOption = [
|
||||||
|
{
|
||||||
|
value: 'all',
|
||||||
|
label: this.$t('all')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const rackTypeAttr = this.attrObj?.attributes?.find?.((item) => item.name === 'rack_type')
|
||||||
|
if (rackTypeAttr?.choice_value?.length) {
|
||||||
|
rackTypeAttr.choice_value.map((item) => {
|
||||||
|
selectOption.push({
|
||||||
|
value: item?.[0] || '',
|
||||||
|
label: item?.[1]?.label || item?.[0] || ''
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
selectOption.push({
|
||||||
|
value: 'unitAbnormal',
|
||||||
|
label: this.$t('cmdb.dcim.unitAbnormal')
|
||||||
|
})
|
||||||
|
|
||||||
|
return selectOption
|
||||||
|
},
|
||||||
|
filterRackList() {
|
||||||
|
let rackList = _.cloneDeep(this.rackList)
|
||||||
|
|
||||||
|
if (this.searchValue) {
|
||||||
|
rackList = rackList.filter((item) => item.name.indexOf(this.searchValue) !== -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentRackType !== 'all') {
|
||||||
|
if (this.currentRackType === 'unitAbnormal') {
|
||||||
|
rackList = rackList.filter((item) => item.u_slot_abnormal)
|
||||||
|
} else {
|
||||||
|
rackList = rackList.filter((item) => item.rack_type === this.currentRackType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rackList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
getRackList: this.getRackList,
|
||||||
|
handleSearch: this.getRackList,
|
||||||
|
attrList: () => {
|
||||||
|
return this?.attrObj?.attributes || []
|
||||||
|
},
|
||||||
|
attributes: () => {
|
||||||
|
return this.attrObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
roomId: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(id) {
|
||||||
|
if (id) {
|
||||||
|
this.initData()
|
||||||
|
} else {
|
||||||
|
this.rackList = []
|
||||||
|
this.statsData = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initData() {
|
||||||
|
try {
|
||||||
|
await this.getRackList()
|
||||||
|
} catch (error) {
|
||||||
|
console.log('initData error', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRackList() {
|
||||||
|
const res = await getDCIMRacks(this.roomId)
|
||||||
|
const rackList = res?.result || []
|
||||||
|
|
||||||
|
const jsonAttrList = this.preferenceAttrList.filter((attr) => attr.value_type === '6')
|
||||||
|
rackList.forEach((item) => {
|
||||||
|
item.free_u_count = item.free_u_count ?? 0
|
||||||
|
item.u_count = item.u_count ?? 0
|
||||||
|
item.u_used_count = item.u_count - item.free_u_count
|
||||||
|
item.u_used_ratio = item.u_used_count > 0 && item.u_count > 0 ? Math.round((item.u_used_count / item.u_count) * 100) : 0
|
||||||
|
|
||||||
|
jsonAttrList.forEach(
|
||||||
|
(jsonAttr) => (item[jsonAttr.name] = item[jsonAttr.name] ? JSON.stringify(item[jsonAttr.name]) : '')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.getColumns(rackList)
|
||||||
|
|
||||||
|
this.rackList = rackList
|
||||||
|
this.statsData = res?.counter || {}
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumns(data) {
|
||||||
|
const width = this.$refs.rackMainRef.clientWidth - 50
|
||||||
|
const columns = getCITableColumns(data, this.preferenceAttrList, width)
|
||||||
|
columns.forEach((item) => {
|
||||||
|
if (item.editRender) {
|
||||||
|
item.editRender.enabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.columns = columns
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChangeLayout(value) {
|
||||||
|
if (this.currentLayout !== value) {
|
||||||
|
this.currentLayout = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
addRack() {
|
||||||
|
this.$emit('openForm', {
|
||||||
|
dcimType: DCIM_TYPE.RACK,
|
||||||
|
parentId: this.roomId
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
openRackDetail(data) {
|
||||||
|
this.$refs.rackDetailRef.open(data._id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dcim-main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&-null {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 95px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tip {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #86909C;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tip2 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
&-search {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-select {
|
||||||
|
width: 120px;
|
||||||
|
margin-left: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
/deep/ &-unitAbnormal {
|
||||||
|
border-top: dashed 1px #e8e8e8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-layout {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
border: solid 1px #E4E7ED;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
height: 100%;
|
||||||
|
width: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-right: solid 1px #E4E7ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
color: #2F54EB;
|
||||||
|
background-color: #F0F5FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rack-wrap {
|
||||||
|
margin-top: 22px;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,326 @@
|
||||||
|
<template>
|
||||||
|
<div class="rack-grid">
|
||||||
|
<template v-if="rackList.length">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in rackList"
|
||||||
|
:key="index"
|
||||||
|
class="rack-grid-item"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="item.u_slot_abnormal"
|
||||||
|
class="rack-grid-item-warning"
|
||||||
|
>
|
||||||
|
<a-icon
|
||||||
|
type="warning"
|
||||||
|
theme="filled"
|
||||||
|
class="rack-grid-item-warning-icon"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="rack-grid-item-warning-text"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.unitAbnormal') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-grid-item-header">
|
||||||
|
<a-tooltip :title="item.name">
|
||||||
|
<div class="rack-grid-item-name">
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<div class="rack-grid-item-store">
|
||||||
|
{{ `${item.u_count || 0}U` }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<img
|
||||||
|
class="rack-grid-item-img"
|
||||||
|
:src="require(`@/modules/cmdb/assets/dcim/rack.png`)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="rack-grid-item-data">
|
||||||
|
<ops-icon
|
||||||
|
type="a-veops-device2"
|
||||||
|
class="rack-grid-item-data-icon"
|
||||||
|
/>
|
||||||
|
<span class="rack-grid-item-data-value">
|
||||||
|
{{ item.u_used_count }}/{{ item.u_count }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="rack-grid-item-data-progress">
|
||||||
|
<div
|
||||||
|
class="rack-grid-item-data-progress-line"
|
||||||
|
:style="{
|
||||||
|
width: item.u_used_ratio + '%'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="rack-grid-item-data-progress-end"
|
||||||
|
:style="{
|
||||||
|
left: item.u_used_ratio + '%'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="rack-grid-item-btn"
|
||||||
|
@click="openRackDetail(item)"
|
||||||
|
>
|
||||||
|
<span class="rack-grid-item-btn-text">{{ $t('cmdb.dcim.viewDetail') }}</span>
|
||||||
|
<a-icon type="right" class="rack-grid-item-btn-icon" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-else class="rack-grid-null">
|
||||||
|
<img class="rack-grid-null-img" :src="require(`@/assets/data_empty.png`)"></img>
|
||||||
|
<div class="rack-grid-null-text">{{ $t('noData') }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RackGrid',
|
||||||
|
props: {
|
||||||
|
rackList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openRackDetail(data) {
|
||||||
|
this.$emit('openRackDetail', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 27px;
|
||||||
|
row-gap: 27px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-height: 100%;
|
||||||
|
padding-bottom: 57px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
width: 205px;
|
||||||
|
height: 219px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background-color: #F9FBFF;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.1s;
|
||||||
|
|
||||||
|
&-warning {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2px 6px;
|
||||||
|
background-color: #FFDEBF;
|
||||||
|
border-radius: 2px;
|
||||||
|
width: max-content;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #FF7D00;
|
||||||
|
margin-right: 2.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FF7D00;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -6px;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -7px;
|
||||||
|
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 7px solid transparent;
|
||||||
|
border-right: 7px solid transparent;
|
||||||
|
border-top: 6px solid #FFDEBF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 25px;
|
||||||
|
background-color: #8FB9F712;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
border-bottom-right-radius: 25px;
|
||||||
|
padding-left: 7px;
|
||||||
|
padding-right: 17px;
|
||||||
|
background-color: #4E5969;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #FFFFFF;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-store {
|
||||||
|
padding-right: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #2F54EB;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
height: 112px;
|
||||||
|
margin-top: 16px;
|
||||||
|
transition: all 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-data {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
margin-left: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-progress {
|
||||||
|
margin-left: 6px;
|
||||||
|
width: 97px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: #C3D0EB;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-line {
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: #7F97FA;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-end {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -8px;
|
||||||
|
margin-left: -8px;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background-color: #3044F112;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: -3px;
|
||||||
|
margin-left: -3px;
|
||||||
|
background-color: #2F54EB;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 17px;
|
||||||
|
bottom: 10px;
|
||||||
|
align-items: center;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-right: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #3F75FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #3F75FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
box-shadow: 0px 22px 33px 0px rgba(41, 65, 126, 0.25);
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
.rack-grid-item-name {
|
||||||
|
background-color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rack-grid-item-img {
|
||||||
|
margin-top: 7px;
|
||||||
|
height: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rack-grid-item-data {
|
||||||
|
margin-top: 9px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-progress {
|
||||||
|
width: 112px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rack-grid-item-btn {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-null {
|
||||||
|
padding-top: 150px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div class="rack-table">
|
||||||
|
<CITable
|
||||||
|
ref="xTable"
|
||||||
|
:attrList="preferenceAttrList"
|
||||||
|
:columns="columns"
|
||||||
|
:data="rackList"
|
||||||
|
:height="tableHeight"
|
||||||
|
:sortConfig="{ remote: false, trigger: 'default' }"
|
||||||
|
:showCheckbox="false"
|
||||||
|
:showOperation="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
|
||||||
|
import CITable from '@/modules/cmdb/components/ciTable/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackTable',
|
||||||
|
components: {
|
||||||
|
CITable
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
rackList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
preferenceAttrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return `${this.windowHeight - 295}px`
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,383 @@
|
||||||
|
<template>
|
||||||
|
<div class="dcim-tree">
|
||||||
|
<div class="dcim-tree-header">
|
||||||
|
<a-input
|
||||||
|
v-model="searchValue"
|
||||||
|
class="dcim-tree-header-search"
|
||||||
|
:placeholder="$t('placeholder1')"
|
||||||
|
/>
|
||||||
|
<a-dropdown>
|
||||||
|
<a-button class="dcim-tree-header-more">
|
||||||
|
<ops-icon type="veops-more" />
|
||||||
|
</a-button>
|
||||||
|
<a-menu slot="overlay">
|
||||||
|
<a-menu-item
|
||||||
|
v-for="(type) in rootAction"
|
||||||
|
:key="type"
|
||||||
|
@click="openForm({
|
||||||
|
dcimType: type
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<a>
|
||||||
|
<a-icon
|
||||||
|
type="plus-circle"
|
||||||
|
class="dcim-tree-header-add-icon"
|
||||||
|
/>
|
||||||
|
{{ $t(addActionTitle[type]) }}
|
||||||
|
</a>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dcim-tree-main">
|
||||||
|
<a-tree
|
||||||
|
v-if="treeData.length"
|
||||||
|
autoExpandParent
|
||||||
|
:treeData="filterTreeData"
|
||||||
|
:selectedKeys="treeKey ? [treeKey] : []"
|
||||||
|
:defaultExpandedKeys="treeKey ? [treeKey] : []"
|
||||||
|
>
|
||||||
|
<template #title="treeNodeData">
|
||||||
|
<div
|
||||||
|
class="dcim-tree-node"
|
||||||
|
@click="clickTreeNode(treeNodeData)"
|
||||||
|
>
|
||||||
|
<ops-icon
|
||||||
|
:type="treeNodeData.icon"
|
||||||
|
class="dcim-tree-node-icon"
|
||||||
|
:style="{ color: treeNodeData.iconColor }"
|
||||||
|
/>
|
||||||
|
<a-tooltip :title="treeNodeData.title">
|
||||||
|
<span
|
||||||
|
class="dcim-tree-node-title"
|
||||||
|
:style="{
|
||||||
|
color: treeKey === treeNodeData.key ? '#2F54EB' : ''
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ treeNodeData.title }}
|
||||||
|
</span>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<div class="dcim-tree-node-right">
|
||||||
|
<span
|
||||||
|
v-if="treeNodeData.count"
|
||||||
|
class="dcim-tree-node-count"
|
||||||
|
>
|
||||||
|
{{ treeNodeData.count }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<a-dropdown>
|
||||||
|
<a class="dcim-tree-node-action">
|
||||||
|
<ops-icon type="veops-more" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a-menu slot="overlay">
|
||||||
|
<a-menu-item
|
||||||
|
v-if="treeNodeData.addType"
|
||||||
|
@click="openForm({
|
||||||
|
dcimType: treeNodeData.addType,
|
||||||
|
parentId: treeNodeData._id
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<a-icon type="plus-circle" />
|
||||||
|
{{ $t(addActionTitle[treeNodeData.addType]) }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item
|
||||||
|
@click="openDetail(treeNodeData)"
|
||||||
|
>
|
||||||
|
<a-icon type="unordered-list" />
|
||||||
|
{{ $t('cmdb.dcim.viewDetail') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item
|
||||||
|
@click="openForm({
|
||||||
|
dcimType: treeNodeData.dcimType,
|
||||||
|
parentId: treeNodeData.parentId,
|
||||||
|
nodeId: treeNodeData._id
|
||||||
|
})"
|
||||||
|
>
|
||||||
|
<ops-icon type="veops-edit" />
|
||||||
|
{{ $t('cmdb.dcim.editNode') }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteNode(treeNodeData)">
|
||||||
|
<ops-icon type="veops-delete" />
|
||||||
|
{{ $t('cmdb.dcim.deleteNode') }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-tree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CIDetailDrawer
|
||||||
|
ref="CIdetailRef"
|
||||||
|
:typeId="viewDetailCITypeId"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { DCIM_TYPE, DCIM_TYPE_NAME_MAP } from '../constants.js'
|
||||||
|
import { deleteDCIM } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import CIDetailDrawer from '@/modules/cmdb/views/ci/modules/ciDetailDrawer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIMTree',
|
||||||
|
components: {
|
||||||
|
CIDetailDrawer
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
treeData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
treeKey: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchValue: '',
|
||||||
|
addActionTitle: {
|
||||||
|
[DCIM_TYPE.REGION]: 'cmdb.dcim.addRegion',
|
||||||
|
[DCIM_TYPE.IDC]: 'cmdb.dcim.addIDC',
|
||||||
|
[DCIM_TYPE.SERVER_ROOM]: 'cmdb.dcim.addServerRoom',
|
||||||
|
},
|
||||||
|
rootAction: [
|
||||||
|
DCIM_TYPE.REGION,
|
||||||
|
DCIM_TYPE.IDC
|
||||||
|
],
|
||||||
|
|
||||||
|
viewDetailCITypeId: 0,
|
||||||
|
viewDetailAttrObj: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filterTreeData() {
|
||||||
|
if (this.searchValue) {
|
||||||
|
const treeData = _.cloneDeep(this.treeData)
|
||||||
|
|
||||||
|
// 过滤筛选
|
||||||
|
const filterTreeData = treeData.filter((data) => {
|
||||||
|
return this.handleTreeDataBySearch(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理同级父节点
|
||||||
|
const newTreeData = []
|
||||||
|
treeData.forEach((item) => {
|
||||||
|
const filterNodeData = filterTreeData.find((data) => data.key === item.key)
|
||||||
|
if (filterNodeData) {
|
||||||
|
newTreeData.push(filterNodeData)
|
||||||
|
} else if (
|
||||||
|
filterTreeData.some((data) => data.parentId === item.key)
|
||||||
|
) {
|
||||||
|
newTreeData.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return newTreeData
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.treeData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: ['getTreeData'],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
handleSearch: this.refreshTreeData,
|
||||||
|
attrList: () => {
|
||||||
|
return this.viewDetailAttrObj?.attributes || []
|
||||||
|
},
|
||||||
|
attributes: () => {
|
||||||
|
return this.viewDetailAttrObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleTreeDataBySearch(data) {
|
||||||
|
const isMatch = data?.title?.indexOf?.(this.searchValue) !== -1
|
||||||
|
if (!data?.children?.length) {
|
||||||
|
return isMatch ? data : null
|
||||||
|
}
|
||||||
|
|
||||||
|
data.children = data.children.filter((data) => {
|
||||||
|
return this.handleTreeDataBySearch(data)
|
||||||
|
})
|
||||||
|
return isMatch || data.children.length ? data : null
|
||||||
|
},
|
||||||
|
|
||||||
|
openForm({
|
||||||
|
dcimType,
|
||||||
|
nodeId = undefined,
|
||||||
|
parentId = ''
|
||||||
|
}) {
|
||||||
|
this.$emit('openForm', {
|
||||||
|
dcimType,
|
||||||
|
nodeId,
|
||||||
|
parentId
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteNode(node) {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('confirmDelete'),
|
||||||
|
onOk: async () => {
|
||||||
|
await deleteDCIM(node.dcimType, node._id)
|
||||||
|
|
||||||
|
if (node.key === this.treeKey) {
|
||||||
|
this.$emit('updateTreeKey', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.refreshTreeData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshTreeData() {
|
||||||
|
this.getTreeData()
|
||||||
|
},
|
||||||
|
|
||||||
|
clickTreeNode(node) {
|
||||||
|
if (node.dcimType === DCIM_TYPE.SERVER_ROOM) {
|
||||||
|
this.$emit('updateTreeKey', node.key)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async openDetail(node) {
|
||||||
|
this.$emit('getAttrList', DCIM_TYPE_NAME_MAP[node.dcimType], node.dcimType, (allAttrList) => {
|
||||||
|
this.viewDetailCITypeId = node._type
|
||||||
|
this.viewDetailAttrObj = allAttrList[node.dcimType]
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.CIdetailRef.create(node._id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dcim-tree {
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 14px;
|
||||||
|
|
||||||
|
&-search {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-more {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 32px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-add-icon {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
/deep/ .ant-tree {
|
||||||
|
.ant-tree-node-content-wrapper {
|
||||||
|
width: calc(100% - 24px);
|
||||||
|
padding: 0px;
|
||||||
|
display: inline-block;
|
||||||
|
height: fit-content;
|
||||||
|
|
||||||
|
.ant-tree-title {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ipam-tree-node_hide_expand {
|
||||||
|
.ant-tree-switcher {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tree-node-content-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tree-switcher-icon {
|
||||||
|
color: #CACDD9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
margin-left: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
margin-left: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #A5A9BC;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
display: none;
|
||||||
|
margin-left: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .ant-dropdown-menu {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .ant-dropdown-menu-item {
|
||||||
|
padding: 5px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.dcim-tree-node-action {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,228 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="deviceListRef"
|
||||||
|
class="device-list"
|
||||||
|
>
|
||||||
|
<div class="device-list-tabs">
|
||||||
|
<div
|
||||||
|
v-for="(item) in tabs"
|
||||||
|
:key="item.id"
|
||||||
|
:class="[
|
||||||
|
'device-list-tabs-item',
|
||||||
|
item.id === tabActive ? 'device-list-tabs-item_active' : ''
|
||||||
|
]"
|
||||||
|
@click="clickTab(item.id)"
|
||||||
|
>
|
||||||
|
<CIIcon :icon="item.icon" />
|
||||||
|
<span class="device-list-tabs-item-name" >{{ item.alias || item.name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<CITable
|
||||||
|
ref="xTable"
|
||||||
|
:attrList="preferenceAttrList"
|
||||||
|
:columns="columns"
|
||||||
|
:data="deviceList"
|
||||||
|
:height="tableHeight"
|
||||||
|
:showCheckbox="false"
|
||||||
|
:showDelete="false"
|
||||||
|
:sortConfig="{ remote: false, trigger: 'default' }"
|
||||||
|
@openDetail="openDetail"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CIDetailDrawer
|
||||||
|
v-if="tabActive"
|
||||||
|
ref="CIdetailRef"
|
||||||
|
:typeId="tabActive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { getSubscribeAttributes } from '@/modules/cmdb/api/preference'
|
||||||
|
import { getCITableColumns } from '@/modules/cmdb/utils/helper'
|
||||||
|
|
||||||
|
import CIIcon from '@/modules/cmdb/components/ciIcon/index.vue'
|
||||||
|
import CITable from '@/modules/cmdb/components/ciTable/index.vue'
|
||||||
|
import CIDetailDrawer from '@/modules/cmdb/views/ci/modules/ciDetailDrawer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DeviceList',
|
||||||
|
components: {
|
||||||
|
CIIcon,
|
||||||
|
CITable,
|
||||||
|
CIDetailDrawer
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
allDeviceList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
CITypeRelations: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabActive: '',
|
||||||
|
tabs: [],
|
||||||
|
|
||||||
|
preferenceAttrList: [],
|
||||||
|
deviceList: [],
|
||||||
|
columns: [],
|
||||||
|
deviceCIType: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return `${this.windowHeight - 210}px`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'getDeviceList',
|
||||||
|
'getRackList'
|
||||||
|
],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
handleSearch: this.refreshData,
|
||||||
|
attrList: () => {
|
||||||
|
return this?.deviceCIType?.attributes || []
|
||||||
|
},
|
||||||
|
attributes: () => {
|
||||||
|
return {
|
||||||
|
attributes: this?.deviceCIType?.attributes || [],
|
||||||
|
unique_id: this?.deviceCIType?.unique_id || 0,
|
||||||
|
unique: this?.deviceCIType?.show_key || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
allDeviceList: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler() {
|
||||||
|
this.initData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initData() {
|
||||||
|
const tabs = []
|
||||||
|
this.allDeviceList.forEach((item) => {
|
||||||
|
const CIType = this.CITypeRelations.find((CIType) => CIType.id === item._type)
|
||||||
|
|
||||||
|
tabs.push({
|
||||||
|
icon: CIType.icon,
|
||||||
|
name: item.ci_type,
|
||||||
|
alias: item.ci_type_alias,
|
||||||
|
id: item._type
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.clickTab(tabs?.[0]?.id ?? '')
|
||||||
|
this.tabs = _.uniqBy(tabs, 'id')
|
||||||
|
},
|
||||||
|
|
||||||
|
clickTab(id) {
|
||||||
|
if (id !== this.tabActive) {
|
||||||
|
this.tabActive = id
|
||||||
|
|
||||||
|
if (this.tabActive) {
|
||||||
|
this.initTableData()
|
||||||
|
} else {
|
||||||
|
this.columns = []
|
||||||
|
this.deviceList = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async initTableData() {
|
||||||
|
const subscribed = await getSubscribeAttributes(this.tabActive)
|
||||||
|
this.preferenceAttrList = subscribed.attributes
|
||||||
|
|
||||||
|
const deviceList = this.allDeviceList.filter((item) => item._type === this.tabActive)
|
||||||
|
|
||||||
|
const deviceCIType = this.CITypeRelations.find((item) => item.id === this.tabActive)
|
||||||
|
this.deviceCIType = deviceCIType || {}
|
||||||
|
|
||||||
|
this.getColumns(deviceList)
|
||||||
|
this.deviceList = deviceList
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumns(data) {
|
||||||
|
const width = this.$refs.deviceListRef.clientWidth - 50
|
||||||
|
const columns = getCITableColumns(data, this.preferenceAttrList, width)
|
||||||
|
columns.forEach((item) => {
|
||||||
|
if (item.editRender) {
|
||||||
|
item.editRender.enabled = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.columns = columns
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshData() {
|
||||||
|
this.getDeviceList()
|
||||||
|
this.getRackList()
|
||||||
|
},
|
||||||
|
|
||||||
|
openDetail(id, activeTabKey, ciDetailRelationKey) {
|
||||||
|
this.$refs.CIdetailRef.create(id, activeTabKey, ciDetailRelationKey)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.device-list {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-tabs {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 9px;
|
||||||
|
row-gap: 5px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background-color: #F7F8FA;
|
||||||
|
border-radius: 1px;
|
||||||
|
border: solid 1px transparent;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_active {
|
||||||
|
border-color: #B1C9FF;
|
||||||
|
background-color: #F9FBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.device-list-tabs-item-name {
|
||||||
|
color: #3F75FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,327 @@
|
||||||
|
<template>
|
||||||
|
<CustomDrawer
|
||||||
|
width="825px"
|
||||||
|
:visible="visible"
|
||||||
|
:bodyStyle="{ height: '100vh', padding: '0px' }"
|
||||||
|
:hasTitle="false"
|
||||||
|
destroyOnClose
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="rack-detail">
|
||||||
|
<div class="rack-header">
|
||||||
|
<div class="rack-header-left">
|
||||||
|
<div class="rack-header-name">
|
||||||
|
<span class="rack-header-name-label">
|
||||||
|
{{ $t('cmdb.dcim.rack') }}
|
||||||
|
</span>
|
||||||
|
<a-tooltip :title="rackData.name">
|
||||||
|
<span class="rack-header-name-value">
|
||||||
|
{{ rackData.name }}
|
||||||
|
</span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<ops-icon
|
||||||
|
type="veops-edit"
|
||||||
|
class="rack-header-edit"
|
||||||
|
@click="clickEdit"
|
||||||
|
/>
|
||||||
|
<ops-icon
|
||||||
|
type="veops-delete"
|
||||||
|
class="rack-header-delete"
|
||||||
|
@click="clickDelete"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-header-right">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in countList"
|
||||||
|
:key="index"
|
||||||
|
class="rack-header-count"
|
||||||
|
>
|
||||||
|
<span class="rack-header-count-name">{{ $t(item.name) }}:</span>
|
||||||
|
<span class="rack-header-count-value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-tabs
|
||||||
|
class="rack-detail-tabs"
|
||||||
|
v-model="tabActive"
|
||||||
|
>
|
||||||
|
<a-tab-pane
|
||||||
|
key="rackView"
|
||||||
|
:tab="$t('cmdb.dcim.rackView')"
|
||||||
|
>
|
||||||
|
<RackView
|
||||||
|
:CITypeRelations="CITypeRelations"
|
||||||
|
:rackData="rackData"
|
||||||
|
:deviceList="deviceList"
|
||||||
|
:rackList="rackList"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane
|
||||||
|
key="rackDetail"
|
||||||
|
:tab="$t('cmdb.dcim.rackDetail')"
|
||||||
|
>
|
||||||
|
<RackGroupAttr
|
||||||
|
:ci="rackData"
|
||||||
|
:rackCITYpeId="rackCITYpe.id"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane
|
||||||
|
key="deviceList"
|
||||||
|
:tab="$t('cmdb.dcim.deviceList')"
|
||||||
|
>
|
||||||
|
<DeviceList
|
||||||
|
:allDeviceList="deviceList"
|
||||||
|
:CITypeRelations="CITypeRelations"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
|
||||||
|
<a-tab-pane
|
||||||
|
key="operationLog"
|
||||||
|
:tab="$t('cmdb.dcim.operationLog')"
|
||||||
|
>
|
||||||
|
<OperationLog
|
||||||
|
v-if="tabActive === 'operationLog'"
|
||||||
|
:rackId="rackId"
|
||||||
|
/>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</CustomDrawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { DCIM_TYPE } from '../../constants.js'
|
||||||
|
import { deleteDCIM } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import { getCITypeChildren } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
|
import { searchCIRelation } from '@/modules/cmdb/api/CIRelation'
|
||||||
|
|
||||||
|
import RackView from './rackView/index.vue'
|
||||||
|
import RackGroupAttr from './rackGroupAttr/index.vue'
|
||||||
|
import DeviceList from './deviceList/index.vue'
|
||||||
|
import OperationLog from './operationLog/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackDetail',
|
||||||
|
components: {
|
||||||
|
RackView,
|
||||||
|
RackGroupAttr,
|
||||||
|
DeviceList,
|
||||||
|
OperationLog
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
roomId: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
rackCITYpe: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
rackList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
rackId: 0,
|
||||||
|
tabActive: 'rackView',
|
||||||
|
|
||||||
|
CITypeRelations: [],
|
||||||
|
deviceList: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rackData() {
|
||||||
|
return this.rackList.find((item) => item._id === this.rackId) || {}
|
||||||
|
},
|
||||||
|
countList() {
|
||||||
|
const {
|
||||||
|
u_count = 0,
|
||||||
|
u_used_ratio = 0,
|
||||||
|
u_slot_abnormal = false
|
||||||
|
} = this.rackData
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'cmdb.dcim.deviceCount',
|
||||||
|
value: this.deviceList?.length || 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cmdb.dcim.unitCount',
|
||||||
|
value: u_count
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cmdb.dcim.unitAbnormal',
|
||||||
|
value: u_slot_abnormal ? this.$t('yes') : this.$t('no')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cmdb.dcim.utilizationRation',
|
||||||
|
value: `${u_used_ratio}%`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'getTreeData',
|
||||||
|
'getRackList'
|
||||||
|
],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
getDeviceList: this.getDeviceList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async open(rackId) {
|
||||||
|
this.rackId = rackId
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
if (!this.CITypeRelations.length) {
|
||||||
|
const res = await getCITypeChildren(this.rackCITYpe.id)
|
||||||
|
this.CITypeRelations = res?.children || []
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.getDeviceList()
|
||||||
|
},
|
||||||
|
|
||||||
|
async getDeviceList() {
|
||||||
|
if (!this.rackId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await searchCIRelation(`root_id=${this.rackId}&level=1&count=10000`)
|
||||||
|
const deviceList = res?.result || []
|
||||||
|
deviceList.sort((a, b) => a.u_start - b.u_start)
|
||||||
|
this.deviceList = deviceList
|
||||||
|
},
|
||||||
|
|
||||||
|
handleClose() {
|
||||||
|
this.rackId = 0
|
||||||
|
this.tabActive = 'rackView'
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
clickEdit() {
|
||||||
|
this.$emit('openForm', {
|
||||||
|
dcimType: DCIM_TYPE.RACK,
|
||||||
|
parentId: this.roomId,
|
||||||
|
nodeId: this.rackId
|
||||||
|
})
|
||||||
|
this.handleClose()
|
||||||
|
},
|
||||||
|
|
||||||
|
clickDelete() {
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content: this.$t('confirmDelete'),
|
||||||
|
onOk: () => {
|
||||||
|
deleteDCIM(DCIM_TYPE.RACK, this.rackId).then(() => {
|
||||||
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
|
this.handleClose()
|
||||||
|
this.getRackList()
|
||||||
|
this.getTreeData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshRackList() {
|
||||||
|
this.$emit('refreshRackList')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-detail {
|
||||||
|
.rack-header {
|
||||||
|
height: 44px;
|
||||||
|
padding: 0px 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #F7F8FA;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #1D2129;
|
||||||
|
max-width: calc(100% - 48px);
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
color: #2F54EB;
|
||||||
|
margin-left: 2px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-edit {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-delete {
|
||||||
|
margin-left: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #FD4C6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 30px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tabs {
|
||||||
|
margin-left: 19px;
|
||||||
|
margin-right: 19px;
|
||||||
|
|
||||||
|
/deep/ .ant-tabs-bar {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,195 @@
|
||||||
|
<template>
|
||||||
|
<div class="operation-log">
|
||||||
|
<ops-table
|
||||||
|
ref="xTable"
|
||||||
|
size="small"
|
||||||
|
show-overflow
|
||||||
|
show-header-overflow
|
||||||
|
highlight-hover-row
|
||||||
|
:data="tableData"
|
||||||
|
:height="tableHeight"
|
||||||
|
:sort-config="{ remote: true }"
|
||||||
|
@sort-change="handleSortChange"
|
||||||
|
>
|
||||||
|
<vxe-table-column
|
||||||
|
:title="$t('cmdb.dcim.operationTime')"
|
||||||
|
field="created_at"
|
||||||
|
sortable
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
:title="$t('cmdb.dcim.operationUser')"
|
||||||
|
field="operationUser"
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
:title="$t('cmdb.dcim.operationType')"
|
||||||
|
field="operate_type"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div
|
||||||
|
class="operation-log-device-type"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: row.deviceTypeData.backgroundColor,
|
||||||
|
color: row.deviceTypeData.textColor
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ $t(row.deviceTypeData.name) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
:title="$t('cmdb.dcim.deviceType')"
|
||||||
|
field="deviceType"
|
||||||
|
></vxe-table-column>
|
||||||
|
<vxe-table-column
|
||||||
|
:title="$t('cmdb.dcim.deviceName')"
|
||||||
|
field="deviceName"
|
||||||
|
></vxe-table-column>
|
||||||
|
</ops-table>
|
||||||
|
|
||||||
|
<div class="operation-log-pagination">
|
||||||
|
<a-pagination
|
||||||
|
:showSizeChanger="true"
|
||||||
|
:current="page"
|
||||||
|
size="small"
|
||||||
|
:total="totalNumber"
|
||||||
|
show-quick-jumper
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-size-options="pageSizeOptions"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
@change="handleChangePage"
|
||||||
|
@showSizeChange="onShowSizeChange"
|
||||||
|
>
|
||||||
|
<template slot="buildOptionText" slot-scope="props">
|
||||||
|
<span v-if="props.value !== '100000'">{{ props.value }}{{ $t('itemsPerPage') }}</span>
|
||||||
|
<span v-if="props.value === '100000'">{{ $t('cmdb.ci.all') }}</span>
|
||||||
|
</template>
|
||||||
|
</a-pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex'
|
||||||
|
import { getDCIMHistoryOperate } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OperationLog',
|
||||||
|
props: {
|
||||||
|
rackId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
pageSizeOptions: ['50', '100', '200'],
|
||||||
|
totalNumber: 0,
|
||||||
|
tableData: [],
|
||||||
|
getTableDataParams: {
|
||||||
|
reverse: 1
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceTypeMap: {
|
||||||
|
0: {
|
||||||
|
textColor: '#00B42A',
|
||||||
|
backgroundColor: '#F6FFED',
|
||||||
|
name: 'cmdb.dcim.addDevice'
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
textColor: '#FD4C6A',
|
||||||
|
backgroundColor: '#FFECE8',
|
||||||
|
name: 'cmdb.dcim.removeDevice'
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
textColor: '#FF7D00',
|
||||||
|
backgroundColor: '#FFECCF',
|
||||||
|
name: 'cmdb.dcim.moveDevice'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
windowHeight: (state) => state.windowHeight,
|
||||||
|
allEmployees: (state) => state.user.allEmployees,
|
||||||
|
}),
|
||||||
|
tableHeight() {
|
||||||
|
return `${this.windowHeight - 187}px`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getTableData() {
|
||||||
|
const res = await getDCIMHistoryOperate({
|
||||||
|
rack_id: this.rackId,
|
||||||
|
count: this.pageSize,
|
||||||
|
page: this.page,
|
||||||
|
...this.getTableDataParams
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableData = res?.result || []
|
||||||
|
tableData.forEach((item) => {
|
||||||
|
const ci = res?.id2ci?.[item?.ci_id] || {}
|
||||||
|
const showKey = res?.type2show_key?.[ci?._type] || ''
|
||||||
|
const user = this.allEmployees.find((emp) => item.uid === emp.acl_uid)
|
||||||
|
|
||||||
|
item.operationUser = user?.nickname || ''
|
||||||
|
item.deviceType = ci?.ci_type_alias || ''
|
||||||
|
item.deviceName = ci?.[showKey] || item?.ci_id || ''
|
||||||
|
item.deviceTypeData = this.deviceTypeMap?.[item?.operate_type] || {}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.tableData = tableData
|
||||||
|
this.totalNumber = res?.numfound || 0
|
||||||
|
},
|
||||||
|
handleChangePage(page) {
|
||||||
|
this.page = page
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
onShowSizeChange(_, pageSize) {
|
||||||
|
this.page = 1
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.getTableData()
|
||||||
|
},
|
||||||
|
handleSortChange(data) {
|
||||||
|
if (data?.order === 'asc') {
|
||||||
|
this.getTableDataParams.reverse = 0
|
||||||
|
} else {
|
||||||
|
this.getTableDataParams.reverse = 1
|
||||||
|
}
|
||||||
|
this.page = 1
|
||||||
|
this.getTableData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.operation-log {
|
||||||
|
&-device-type {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 9px;
|
||||||
|
border-radius: 1px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,151 @@
|
||||||
|
<template>
|
||||||
|
<div class="rack-group-attr">
|
||||||
|
<el-descriptions
|
||||||
|
v-for="group in attributeGroups"
|
||||||
|
class="rack-group-attr-desc"
|
||||||
|
:title="group.name || $t('other')"
|
||||||
|
:key="group.name"
|
||||||
|
border
|
||||||
|
:column="3"
|
||||||
|
>
|
||||||
|
<el-descriptions-item
|
||||||
|
v-for="attr in group.attributes"
|
||||||
|
:label="`${attr.alias || attr.name}`"
|
||||||
|
:key="attr.name"
|
||||||
|
>
|
||||||
|
<ci-detail-attr-content
|
||||||
|
:ci="ci"
|
||||||
|
:attr="attr"
|
||||||
|
:attributeGroups="attributeGroups"
|
||||||
|
:showEdit="false"
|
||||||
|
/>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { getCITypeGroupById, getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
|
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||||
|
|
||||||
|
import { Descriptions, DescriptionsItem } from 'element-ui'
|
||||||
|
import CiDetailAttrContent from '@/modules/cmdb/views/ci/modules/ciDetailAttrContent.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackGroupAttr',
|
||||||
|
components: {
|
||||||
|
ElDescriptions: Descriptions,
|
||||||
|
ElDescriptionsItem: DescriptionsItem,
|
||||||
|
CiDetailAttrContent
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
ci: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
rackCITYpeId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attributeGroups: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getAttributes()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getAttributes() {
|
||||||
|
getCITypeGroupById(this.rackCITYpeId, { need_other: 1 })
|
||||||
|
.then((res) => {
|
||||||
|
this.attributeGroups = res
|
||||||
|
|
||||||
|
this.handleReferenceAttr()
|
||||||
|
})
|
||||||
|
.catch((e) => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleReferenceAttr() {
|
||||||
|
const map = {}
|
||||||
|
this.attributeGroups.forEach((group) => {
|
||||||
|
group.attributes.forEach((attr) => {
|
||||||
|
if (attr?.is_reference && attr?.reference_type_id && this.ci[attr.name]) {
|
||||||
|
const ids = Array.isArray(this.ci[attr.name]) ? this.ci[attr.name] : this.ci[attr.name] ? [this.ci[attr.name]] : []
|
||||||
|
if (ids.length) {
|
||||||
|
if (!map?.[attr.reference_type_id]) {
|
||||||
|
map[attr.reference_type_id] = {}
|
||||||
|
}
|
||||||
|
ids.forEach((id) => {
|
||||||
|
map[attr.reference_type_id][id] = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!Object.keys(map).length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciTypesRes = await getCITypes({
|
||||||
|
type_ids: Object.keys(map).join(',')
|
||||||
|
})
|
||||||
|
const showAttrNameMap = {}
|
||||||
|
ciTypesRes.ci_types.forEach((ciType) => {
|
||||||
|
showAttrNameMap[ciType.id] = ciType?.show_name || ciType?.unique_name || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const allRes = await Promise.all(
|
||||||
|
Object.keys(map).map((key) => {
|
||||||
|
return searchCI({
|
||||||
|
q: `_type:${key},_id:(${Object.keys(map[key]).join(';')})`,
|
||||||
|
count: 9999
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const ciNameMap = {}
|
||||||
|
allRes.forEach((res) => {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
ciNameMap[item._id] = item
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const newAttrGroups = _.cloneDeep(this.attributeGroups)
|
||||||
|
|
||||||
|
newAttrGroups.forEach((group) => {
|
||||||
|
group.attributes.forEach((attr) => {
|
||||||
|
if (attr?.is_reference && attr?.reference_type_id) {
|
||||||
|
attr.showAttrName = showAttrNameMap?.[attr?.reference_type_id] || ''
|
||||||
|
|
||||||
|
const referenceShowAttrNameMap = {}
|
||||||
|
const referenceCIIds = this.ci[attr.name];
|
||||||
|
(Array.isArray(referenceCIIds) ? referenceCIIds : referenceCIIds ? [referenceCIIds] : []).forEach((id) => {
|
||||||
|
referenceShowAttrNameMap[id] = ciNameMap?.[id]?.[attr.showAttrName] ?? id
|
||||||
|
})
|
||||||
|
attr.referenceShowAttrNameMap = referenceShowAttrNameMap
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$set(this, 'attributeGroups', newAttrGroups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-group-attr {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
&-desc {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,129 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
:okText="$t('cmdb.dcim.toChange')"
|
||||||
|
:width="350"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<div class="abnormal-modal-title">
|
||||||
|
<a-icon
|
||||||
|
type="info-circle"
|
||||||
|
theme="filled"
|
||||||
|
class="abnormal-modal-title-icon"
|
||||||
|
/>
|
||||||
|
<span class="abnormal-modal-title-text">
|
||||||
|
{{ $t('cmdb.dcim.unitAbnormal') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="abnormal-modal-content">
|
||||||
|
<div class="abnormal-modal-content-row">
|
||||||
|
<span
|
||||||
|
v-for="(item, index) in abnormalList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
{{ item.CITypeName }}
|
||||||
|
<span class="abnormal-modal-content-name" >
|
||||||
|
{{ item.name }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="index !== abnormalList.length - 1"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.abnormalModalTip1') }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span>{{ $t('cmdb.dcim.abnormalModalTip2') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="abnormal-modal-content-row">
|
||||||
|
{{ $t('cmdb.dcim.abnormalModalTip3') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-radio-group
|
||||||
|
v-model="currentSelect"
|
||||||
|
>
|
||||||
|
<a-radio
|
||||||
|
v-for="(item) in abnormalList"
|
||||||
|
:value="item.id"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'AbnormalModal',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
abnormalList: [],
|
||||||
|
currentSelect: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
const abnormalList = [data]
|
||||||
|
if (data?.abnormalList?.length) {
|
||||||
|
abnormalList.push(...data.abnormalList)
|
||||||
|
}
|
||||||
|
this.abnormalList = abnormalList
|
||||||
|
|
||||||
|
this.currentSelect = abnormalList?.[0]?.id ?? undefined
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCancel() {
|
||||||
|
this.currentSelect = undefined
|
||||||
|
this.abnormalList = []
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
if (!this.currentSelect) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = this.abnormalList.find((item) => item.id === this.currentSelect)
|
||||||
|
this.$emit('ok', device)
|
||||||
|
|
||||||
|
this.handleCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped >
|
||||||
|
.abnormal-modal-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #FF7D00;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.abnormal-modal-content {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22px;
|
||||||
|
margin: 9px 0px;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,199 @@
|
||||||
|
<template>
|
||||||
|
<div class="device-select">
|
||||||
|
<a-input-search
|
||||||
|
@search="handleSearch"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-radio-group
|
||||||
|
v-if="CIList.length"
|
||||||
|
:value="currentSelect"
|
||||||
|
class="device-select-group"
|
||||||
|
@change="handleCIChange"
|
||||||
|
>
|
||||||
|
<a-radio
|
||||||
|
v-for="(item) in CIList"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
class="device-select-item"
|
||||||
|
>
|
||||||
|
<a-tooltip :title="item.name" placement="topLeft">
|
||||||
|
{{ item.name }}
|
||||||
|
</a-tooltip>
|
||||||
|
</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
|
<div v-else class="device-select-null">
|
||||||
|
<img class="device-select-null-img" :src="require(`@/assets/data_empty.png`)"></img>
|
||||||
|
<div class="device-select-null-text">{{ $t('noData') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="device-select-pagination">
|
||||||
|
<a-pagination
|
||||||
|
:showSizeChanger="true"
|
||||||
|
:current="page"
|
||||||
|
size="small"
|
||||||
|
:total="totalNumber"
|
||||||
|
show-quick-jumper
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-size-options="pageSizeOptions"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
@change="handleChangePage"
|
||||||
|
@showSizeChange="onShowSizeChange"
|
||||||
|
>
|
||||||
|
<template slot="buildOptionText" slot-scope="props">
|
||||||
|
<span v-if="props.value !== '100000'">{{ props.value }}{{ $t('itemsPerPage') }}</span>
|
||||||
|
<span v-if="props.value === '100000'">{{ $t('cmdb.ci.all') }}</span>
|
||||||
|
</template>
|
||||||
|
</a-pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DeviceSelect',
|
||||||
|
props: {
|
||||||
|
currentSelect: {
|
||||||
|
type: [Number, undefined],
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
CITypeId: {
|
||||||
|
type: [Number, undefined],
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
|
currentCITYpe: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
pageSizeOptions: ['20', '50', '100'],
|
||||||
|
totalNumber: 0,
|
||||||
|
CIList: [],
|
||||||
|
|
||||||
|
searchValue: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
CITypeId: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(newVal, oldVal) {
|
||||||
|
this.page = 1
|
||||||
|
this.searchValue = ''
|
||||||
|
|
||||||
|
if (newVal && newVal !== oldVal) {
|
||||||
|
this.getCIList()
|
||||||
|
} else {
|
||||||
|
this.CIList = []
|
||||||
|
this.totalNumber = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getCIList() {
|
||||||
|
const res = await searchCI({
|
||||||
|
q: `_type:${this.CITypeId}${this.searchValue ? `,*${this.searchValue}*` : ''}`,
|
||||||
|
count: this.pageSize,
|
||||||
|
page: this.page
|
||||||
|
})
|
||||||
|
let CIList = res?.result || []
|
||||||
|
|
||||||
|
if (CIList.length) {
|
||||||
|
CIList = CIList.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item?._id,
|
||||||
|
name: item?.[this?.currentCITYpe?.show_key] || item?._id || '',
|
||||||
|
unitCount: item?.u_count ?? 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CIList = CIList
|
||||||
|
this.totalNumber = res?.numfound || 0
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSearch(value) {
|
||||||
|
this.searchValue = value
|
||||||
|
this.page = 1
|
||||||
|
this.getCIList()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChangePage(page) {
|
||||||
|
this.page = page
|
||||||
|
this.getCIList()
|
||||||
|
},
|
||||||
|
|
||||||
|
onShowSizeChange(_, pageSize) {
|
||||||
|
this.page = 1
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.getCIList()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCIChange(e) {
|
||||||
|
const value = e.target.value
|
||||||
|
const findCI = this.CIList.find((item) => item.value === value)
|
||||||
|
|
||||||
|
this.$emit('change', findCI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.device-select {
|
||||||
|
width: 650px;
|
||||||
|
|
||||||
|
&-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
row-gap: 20px;
|
||||||
|
margin: 12px 0px;
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
width: 48%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-null {
|
||||||
|
margin: 30px 0px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
width: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,243 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:visible="visible"
|
||||||
|
:width="500"
|
||||||
|
:title="$t('cmdb.dcim.addDevice')"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="deviceFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="formRules"
|
||||||
|
:label-col="{ span: 5 }"
|
||||||
|
:wrapper-col="{ span: 19 }"
|
||||||
|
class="device-form"
|
||||||
|
>
|
||||||
|
<a-form-model-item
|
||||||
|
:label="$t('cmdb.dcim.ciType')"
|
||||||
|
prop="CITypeId"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model="form.CITypeId"
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
optionFilterProp="title"
|
||||||
|
@change="handleCITypeChange"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="(item) in CITypeRelations"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:title="item.alias || item.name"
|
||||||
|
>
|
||||||
|
{{ item.alias || item.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item
|
||||||
|
:label="$t('cmdb.dcim.device')"
|
||||||
|
prop="deviceId"
|
||||||
|
>
|
||||||
|
<a-popover trigger="click" placement="bottom">
|
||||||
|
<DeviceSelect
|
||||||
|
slot="content"
|
||||||
|
:CITypeId="form.CITypeId"
|
||||||
|
:currentCITYpe="currentCITYpe"
|
||||||
|
:currentSelect="form.deviceId"
|
||||||
|
@change="handleDeviceChange"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="device-form-select"
|
||||||
|
>
|
||||||
|
{{ deviceName }}
|
||||||
|
</div>
|
||||||
|
</a-popover>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item
|
||||||
|
:label="$t('cmdb.dcim.unitStart')"
|
||||||
|
prop="unitStart"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.unitStart"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
class="device-form-input"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item
|
||||||
|
v-if="showUnitCount"
|
||||||
|
:label="$t('cmdb.dcim.unitCount')"
|
||||||
|
prop="unitCount"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.unitCount"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
class="device-form-input"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { postDevice } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
|
||||||
|
import DeviceSelect from './deviceSelect.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DeviceForm',
|
||||||
|
components: {
|
||||||
|
DeviceSelect
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
CITypeRelations: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
rackId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
form: {
|
||||||
|
CITypeId: undefined,
|
||||||
|
deviceId: undefined,
|
||||||
|
unitStart: undefined,
|
||||||
|
unitCount: undefined
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceName: '',
|
||||||
|
showUnitCount: true,
|
||||||
|
formRules: {
|
||||||
|
CITypeId: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder2')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
deviceId: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder2')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
unitStart: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder1')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
unitCount: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder1')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentCITYpe() {
|
||||||
|
return this.CITypeRelations.find((CIType) => CIType?.id === this.form.CITypeId) || {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(deviceData) {
|
||||||
|
this.visible = true
|
||||||
|
|
||||||
|
if (deviceData) {
|
||||||
|
this.form = {
|
||||||
|
CITypeId: deviceData?.CITypeId ?? undefined,
|
||||||
|
deviceId: deviceData?.deviceId ?? undefined,
|
||||||
|
unitStart: deviceData?.unitStart ?? undefined,
|
||||||
|
unitCount: deviceData?.unitCount ?? undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.form.unitCount) {
|
||||||
|
this.showUnitCount = false
|
||||||
|
}
|
||||||
|
this.deviceName = deviceData?.name || ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCancel() {
|
||||||
|
this.form = {
|
||||||
|
CITypeId: undefined,
|
||||||
|
deviceId: undefined,
|
||||||
|
unitStart: undefined,
|
||||||
|
unitCount: undefined
|
||||||
|
}
|
||||||
|
this.deviceName = ''
|
||||||
|
this.showUnitCount = true
|
||||||
|
this.$refs.deviceFormRef.clearValidate()
|
||||||
|
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
this.$refs.deviceFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await postDevice(
|
||||||
|
this.rackId,
|
||||||
|
this.form.deviceId,
|
||||||
|
{
|
||||||
|
u_start: this.form.unitStart,
|
||||||
|
u_count: this.form.unitCount
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
this.handleCancel()
|
||||||
|
this.$message.success(this.$t('addSuccess'))
|
||||||
|
this.$emit('ok')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDeviceChange({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
unitCount
|
||||||
|
}) {
|
||||||
|
this.form.deviceId = value
|
||||||
|
this.deviceName = name
|
||||||
|
|
||||||
|
this.form.unitCount = unitCount || undefined
|
||||||
|
this.showUnitCount = !unitCount
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCITypeChange() {
|
||||||
|
this.form.deviceId = undefined
|
||||||
|
this.deviceName = ''
|
||||||
|
this.showUnitCount = true
|
||||||
|
this.form.unitCount = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.device-form {
|
||||||
|
&-select {
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 2px;
|
||||||
|
line-height: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #597ef7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,296 @@
|
||||||
|
<template>
|
||||||
|
<div v-if="unitList.length" class="rack-view">
|
||||||
|
<div class="rack-view-col">
|
||||||
|
<RackUnitView
|
||||||
|
viewType="front"
|
||||||
|
:countList="countList"
|
||||||
|
:unitList="unitList"
|
||||||
|
:rackId="rackData._id"
|
||||||
|
@migrateDevice="migrateDevice"
|
||||||
|
@openDeviceForm="openDeviceForm"
|
||||||
|
@draggable="handleDraggable"
|
||||||
|
@refreshRackAllData="refreshRackAllData"
|
||||||
|
@openDeviceDetail="openDeviceDetail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-view-col">
|
||||||
|
<RackUnitView
|
||||||
|
viewType="rear"
|
||||||
|
:countList="countList"
|
||||||
|
:unitList="unitList"
|
||||||
|
:rackId="rackData._id"
|
||||||
|
@migrateDevice="migrateDevice"
|
||||||
|
@openDeviceForm="openDeviceForm"
|
||||||
|
@draggable="handleDraggable"
|
||||||
|
@refreshRackAllData="refreshRackAllData"
|
||||||
|
@openDeviceDetail="openDeviceDetail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DeviceForm
|
||||||
|
ref="deviceFormRef"
|
||||||
|
:CITypeRelations="CITypeRelations"
|
||||||
|
:rackId="rackData._id"
|
||||||
|
@ok="refreshRackAllData"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MigrateModal
|
||||||
|
ref="migrateModalRef"
|
||||||
|
:rackList="rackList"
|
||||||
|
@ok="refreshRackAllData"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CIDetailDrawer
|
||||||
|
ref="CIdetailRef"
|
||||||
|
:typeId="deviceCITypeId"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { putDevice } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import { DEVICE_CITYPE_NAME } from '../../../constants.js'
|
||||||
|
|
||||||
|
import RackUnitView from './rackUnitView.vue'
|
||||||
|
import DeviceForm from './deviceForm/index.vue'
|
||||||
|
import MigrateModal from './migrateModal.vue'
|
||||||
|
import CIDetailDrawer from '@/modules/cmdb/views/ci/modules/ciDetailDrawer.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackView',
|
||||||
|
components: {
|
||||||
|
RackUnitView,
|
||||||
|
DeviceForm,
|
||||||
|
MigrateModal,
|
||||||
|
CIDetailDrawer
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
CITypeRelations: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
rackData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
deviceList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
rackList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
unitList: [],
|
||||||
|
countList: [],
|
||||||
|
|
||||||
|
deviceAttrList: [],
|
||||||
|
deviceCITypeId: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject: [
|
||||||
|
'getRackList',
|
||||||
|
'getDeviceList'
|
||||||
|
],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
handleSearch: this.refreshRackAllData,
|
||||||
|
attrList: () => {
|
||||||
|
return this.deviceAttrList
|
||||||
|
},
|
||||||
|
attributes: () => {
|
||||||
|
return {
|
||||||
|
attributes: this.deviceAttrList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
deviceList: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(deviceList) {
|
||||||
|
this.initData(deviceList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initData(deviceList) {
|
||||||
|
const CITypeMap = this.CITypeRelations.reduce((map, cur) => {
|
||||||
|
map[cur.id] = cur
|
||||||
|
return map
|
||||||
|
}, {})
|
||||||
|
|
||||||
|
const _deviceList = _.cloneDeep(deviceList)
|
||||||
|
|
||||||
|
// 建立设备map, 并处理U位异常情况
|
||||||
|
const deviceMap = {}
|
||||||
|
_deviceList.forEach((device, index) => {
|
||||||
|
const CITYpe = CITypeMap?.[device?._type] || {}
|
||||||
|
|
||||||
|
device.deviceImage = this.getDeviceViewImage(CITYpe?.name)
|
||||||
|
device.name = device?.[CITYpe?.show_key] || device._id || ''
|
||||||
|
device.icon = CITYpe?.icon || ''
|
||||||
|
device.CITypeName = CITYpe?.alias || CITYpe?.name || ''
|
||||||
|
device.id = device._id
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
const abnormalDevice = _deviceList.slice(0, index).find((item) => {
|
||||||
|
const unitCount = item.abnormal ? item.abnormalUnitcount : item.u_count
|
||||||
|
|
||||||
|
return item.u_start <= device.u_start && device.u_start <= (item.u_start + unitCount - 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (abnormalDevice) {
|
||||||
|
abnormalDevice.abnormal = true
|
||||||
|
const endCount = Math.max(abnormalDevice.u_start + abnormalDevice.u_count, device.u_start + device.u_count)
|
||||||
|
abnormalDevice.abnormalUnitcount = endCount - abnormalDevice.u_start
|
||||||
|
|
||||||
|
if (abnormalDevice?.abnormalList?.length) {
|
||||||
|
abnormalDevice.abnormalList.push(device)
|
||||||
|
} else {
|
||||||
|
abnormalDevice.abnormalList = [device]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deviceMap[device.u_start] = device
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deviceMap[device.u_start] = device
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let unitIndex = 1
|
||||||
|
const unitList = []
|
||||||
|
|
||||||
|
while (unitIndex <= this.rackData.u_count) {
|
||||||
|
if (deviceMap[unitIndex]) {
|
||||||
|
const device = deviceMap[unitIndex]
|
||||||
|
const unitCount = device?.abnormal ? device.abnormalUnitcount : device.u_count
|
||||||
|
|
||||||
|
unitList.push({
|
||||||
|
...device,
|
||||||
|
unitCount,
|
||||||
|
type: 'device',
|
||||||
|
key: uuidv4(),
|
||||||
|
abnormal: device?.abnormal ?? false,
|
||||||
|
abnormalList: device.abnormalList
|
||||||
|
})
|
||||||
|
|
||||||
|
unitIndex += unitCount
|
||||||
|
device.assign = true
|
||||||
|
} else {
|
||||||
|
unitList.push({
|
||||||
|
type: 'gap',
|
||||||
|
unitCount: 1,
|
||||||
|
key: uuidv4()
|
||||||
|
})
|
||||||
|
unitIndex += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unitList = _.reverse(unitList)
|
||||||
|
this.countList = Array.from({ length: this.rackData.u_count }, (_, i) => this.rackData.u_count - i)
|
||||||
|
},
|
||||||
|
|
||||||
|
getDeviceViewImage(name) {
|
||||||
|
const image = {
|
||||||
|
front: require('@/modules/cmdb/assets/dcim/device/server_front.png'),
|
||||||
|
rear: require('@/modules/cmdb/assets/dcim/device/server_rear.png')
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case DEVICE_CITYPE_NAME.ROUTER:
|
||||||
|
image.front = require('@/modules/cmdb/assets/dcim/device/router_front.png')
|
||||||
|
image.rear = require('@/modules/cmdb/assets/dcim/device/router_rear.png')
|
||||||
|
break
|
||||||
|
case DEVICE_CITYPE_NAME.FIRE_WALL:
|
||||||
|
image.front = require('@/modules/cmdb/assets/dcim/device/firewall_front.png')
|
||||||
|
image.rear = require('@/modules/cmdb/assets/dcim/device/firewall_rear.png')
|
||||||
|
break
|
||||||
|
case DEVICE_CITYPE_NAME.SERVER:
|
||||||
|
image.front = require('@/modules/cmdb/assets/dcim/device/server_front.png')
|
||||||
|
image.rear = require('@/modules/cmdb/assets/dcim/device/server_rear.png')
|
||||||
|
break
|
||||||
|
case DEVICE_CITYPE_NAME.RAID:
|
||||||
|
image.front = require('@/modules/cmdb/assets/dcim/device/raid_front.png')
|
||||||
|
image.rear = require('@/modules/cmdb/assets/dcim/device/raid_rear.png')
|
||||||
|
break
|
||||||
|
case DEVICE_CITYPE_NAME.SWITCH:
|
||||||
|
case DEVICE_CITYPE_NAME.FC_SWITCH:
|
||||||
|
case DEVICE_CITYPE_NAME.F5:
|
||||||
|
image.front = require('@/modules/cmdb/assets/dcim/device/switch_front.png')
|
||||||
|
image.rear = require('@/modules/cmdb/assets/dcim/device/switch_rear.png')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return image
|
||||||
|
},
|
||||||
|
|
||||||
|
openDeviceForm(deviceData) {
|
||||||
|
this.$refs.deviceFormRef.open(deviceData)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDraggable({
|
||||||
|
startUnit,
|
||||||
|
deviceId,
|
||||||
|
oldUnitList
|
||||||
|
}) {
|
||||||
|
putDevice(
|
||||||
|
this.rackData._id,
|
||||||
|
deviceId,
|
||||||
|
{
|
||||||
|
to_u_start: startUnit
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
this.getDeviceList()
|
||||||
|
}).catch((error) => {
|
||||||
|
console.log('putDevice fail', error)
|
||||||
|
this.unitList = oldUnitList
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
migrateDevice(deviceId) {
|
||||||
|
this.$refs.migrateModalRef.open({
|
||||||
|
deviceId,
|
||||||
|
rackId: this.rackData._id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshRackAllData() {
|
||||||
|
this.getRackList()
|
||||||
|
this.getDeviceList()
|
||||||
|
},
|
||||||
|
|
||||||
|
async openDeviceDetail(data) {
|
||||||
|
const deviceCIType = this.CITypeRelations.find((item) => item.id === data._type)
|
||||||
|
this.deviceAttrList = deviceCIType?.attributes || []
|
||||||
|
this.deviceCITypeId = data?._type
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.CIdetailRef.create(data._id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-view {
|
||||||
|
display: flex;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
max-height: calc(100vh - 160px);
|
||||||
|
|
||||||
|
&-col {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,136 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
:title="$t('cmdb.dcim.deviceMigrate')"
|
||||||
|
:visible="visible"
|
||||||
|
:width="500"
|
||||||
|
@ok="handleOk"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form-model
|
||||||
|
ref="deviceMigrateFormRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="formRules"
|
||||||
|
:label-col="{ span: 6 }"
|
||||||
|
:wrapper-col="{ span: 18 }"
|
||||||
|
class="device-migrate"
|
||||||
|
>
|
||||||
|
<a-form-model-item
|
||||||
|
:label="$t('cmdb.dcim.rack')"
|
||||||
|
prop="to_rack_id"
|
||||||
|
>
|
||||||
|
<a-select
|
||||||
|
v-model="form.to_rack_id"
|
||||||
|
showSearch
|
||||||
|
allowClear
|
||||||
|
optionFilterProp="title"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="(rack) in rackList"
|
||||||
|
:key="rack._id"
|
||||||
|
:value="rack._id"
|
||||||
|
:title="rack.name"
|
||||||
|
>
|
||||||
|
{{ rack.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-model-item>
|
||||||
|
|
||||||
|
<a-form-model-item
|
||||||
|
:label="$t('cmdb.dcim.unitStart')"
|
||||||
|
prop="to_u_start"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.to_u_start"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
class="device-migrate-input"
|
||||||
|
/>
|
||||||
|
</a-form-model-item>
|
||||||
|
</a-form-model>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { migrateDevice } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'MigrateModal',
|
||||||
|
props: {
|
||||||
|
rackList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
form: {
|
||||||
|
to_rack_id: undefined,
|
||||||
|
to_u_start: undefined,
|
||||||
|
},
|
||||||
|
formRules: {
|
||||||
|
to_rack_id: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder2')
|
||||||
|
}
|
||||||
|
],
|
||||||
|
to_u_start: [
|
||||||
|
{
|
||||||
|
required: true, message: this.$t('placeholder1')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
deviceId: '',
|
||||||
|
rackId: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(data) {
|
||||||
|
this.visible = true
|
||||||
|
this.deviceId = data?.deviceId || ''
|
||||||
|
this.rackId = data?.rackId || ''
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCancel() {
|
||||||
|
this.deviceId = ''
|
||||||
|
this.rackId = ''
|
||||||
|
this.form = {
|
||||||
|
to_rack_id: undefined,
|
||||||
|
to_u_start: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.deviceMigrateFormRef.clearValidate()
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
|
||||||
|
handleOk() {
|
||||||
|
this.$refs.deviceMigrateFormRef.validate(async (valid) => {
|
||||||
|
if (!valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
migrateDevice(
|
||||||
|
this.rackId,
|
||||||
|
this.deviceId,
|
||||||
|
{
|
||||||
|
...this.form
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
this.$message.success(this.$t('cmdb.dcim.migrationSuccess'))
|
||||||
|
this.handleCancel()
|
||||||
|
this.$emit('ok')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.device-migrate {
|
||||||
|
&-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,109 @@
|
||||||
|
<template>
|
||||||
|
<div class="rack-header">
|
||||||
|
<div class="rack-header-part-1">
|
||||||
|
<div
|
||||||
|
class="rack-header-part-1-line"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: viewType === 'front' ? '#A4FFF8' : '#FFFFFF'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="rack-header-part-2"
|
||||||
|
:style="{
|
||||||
|
padding: viewType === 'front' ? '0 8px' : '0 22px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="viewType === 'front'"
|
||||||
|
class="rack-header-part-2-left"
|
||||||
|
>
|
||||||
|
<RackHeaderCircle/>
|
||||||
|
<RackHeaderCircle/>
|
||||||
|
<RackHeaderCircle/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-header-part-2-right">
|
||||||
|
<div
|
||||||
|
v-for="(item) in 300"
|
||||||
|
:key="item"
|
||||||
|
class="rack-header-part-2-right-item"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import RackHeaderCircle from './rackHeaderCircle.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackHeader',
|
||||||
|
components: {
|
||||||
|
RackHeaderCircle
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
viewType: {
|
||||||
|
type: String,
|
||||||
|
default: 'front'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-header {
|
||||||
|
width: 100%;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-part-1 {
|
||||||
|
height: 5px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #3D4151;
|
||||||
|
padding-top: 3px;
|
||||||
|
|
||||||
|
&-line {
|
||||||
|
width: 100%;
|
||||||
|
height: 0.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-part-2 {
|
||||||
|
height: 21px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #86909C;
|
||||||
|
border-bottom: solid 1px #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 5px;
|
||||||
|
margin-right: 7px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 2px;
|
||||||
|
width: 100%;
|
||||||
|
height: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #C8CDD2;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<div class="circle-container">
|
||||||
|
<div class="circle shadow-1"></div>
|
||||||
|
<div class="circle shadow-2"></div>
|
||||||
|
<div class="circle shadow-3"></div>
|
||||||
|
<div class="circle inner-circle"></div>
|
||||||
|
<div class="circle inner-shadow"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RackHeaderCircle'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.circle-container {
|
||||||
|
position: relative;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #20e757;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-1 {
|
||||||
|
filter: blur(4px);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-2 {
|
||||||
|
filter: blur(2px);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-3 {
|
||||||
|
filter: blur(1px);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-circle {
|
||||||
|
background-color: #6cffe5;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-shadow {
|
||||||
|
background-color: #6affe4;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
filter: blur(1px);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,575 @@
|
||||||
|
<template>
|
||||||
|
<div class="rack-container">
|
||||||
|
<div class="rack-title">
|
||||||
|
<ops-icon
|
||||||
|
:type="titleData.icon"
|
||||||
|
class="rack-title-icon"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="rack-title-text"
|
||||||
|
>
|
||||||
|
{{ $t(titleData.text) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<RackHeader :viewType="viewType" />
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="rack-container-main"
|
||||||
|
:style="{
|
||||||
|
flexDirection: viewType === 'front' ? 'row' : 'row-reverse'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="rack-container-main-left">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in countList"
|
||||||
|
:key="index"
|
||||||
|
class="rack-container-main-left-count"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: item % 2 === 0 ? '#3D4151' : '#5E6772',
|
||||||
|
height: unitHeight + 'px',
|
||||||
|
lineHeight: unitHeight + 'px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-container-main-list">
|
||||||
|
<draggable
|
||||||
|
filter=".undraggable"
|
||||||
|
:list="unitList"
|
||||||
|
@start="handleDraggableStart"
|
||||||
|
@end="handleDraggableEnd"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in unitList"
|
||||||
|
:key="item.key"
|
||||||
|
:class="[item.type === 'gap' || item.abnormal ? 'undraggable' : '']"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="item.type === 'device'"
|
||||||
|
:class="['rack-container-main-list-device', item.abnormal ? '' : 'rack-container-main-list-device_normal']"
|
||||||
|
:style="{
|
||||||
|
height: unitHeight * item.unitCount + 'px'
|
||||||
|
}"
|
||||||
|
@click="clickDevice(item)"
|
||||||
|
>
|
||||||
|
<div class="rack-container-main-list-device-action">
|
||||||
|
<div
|
||||||
|
class="rack-container-main-list-device-action-btn"
|
||||||
|
@click.stop="removeDevice(item)"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.remove') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="rack-container-main-list-device-action-btn"
|
||||||
|
@click.stop="migrateDevice(item)"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.migrate') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="item.abnormal"
|
||||||
|
class="rack-container-main-list-device-abnormal"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="rack-container-main-list-device-abnormal-text"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.unitAbnormal') }}
|
||||||
|
</span>
|
||||||
|
<a-icon
|
||||||
|
type="right"
|
||||||
|
class="rack-container-main-list-device-abnormal-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-container-main-list-device-header"></div>
|
||||||
|
<img
|
||||||
|
v-for="(unitIndex) in item.unitCount"
|
||||||
|
:key="unitIndex"
|
||||||
|
:src="item.deviceImage[viewType]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="rack-container-main-list-device-sider"
|
||||||
|
:style="{
|
||||||
|
right: viewType === 'front' ? '-154px' : '-157px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(nameItem, nameIndex) in getNameList(item)"
|
||||||
|
:key="nameIndex"
|
||||||
|
class="rack-container-main-list-device-name"
|
||||||
|
@click.stop="openDeviceDetail(nameItem)"
|
||||||
|
>
|
||||||
|
<CIIcon size="14" :icon="nameItem.icon" />
|
||||||
|
<span class="rack-container-main-list-device-name-text">{{ nameItem.name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="item.type === 'gap'"
|
||||||
|
:class="['rack-container-main-list-gap', viewType === 'rear' ? 'rack-container-main-list-gap_rear' : '']"
|
||||||
|
:style="{
|
||||||
|
height: unitHeight + 'px'
|
||||||
|
}"
|
||||||
|
@click="addDevice(index)"
|
||||||
|
>
|
||||||
|
<ops-icon
|
||||||
|
type="monitor-add"
|
||||||
|
class="rack-container-main-list-gap-icon"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="rack-container-main-list-gap-text"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.dcim.addDevice') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-container-main-right">
|
||||||
|
<div class="rack-container-main-right-part-1"></div>
|
||||||
|
<div class="rack-container-main-right-part-2"></div>
|
||||||
|
|
||||||
|
<img
|
||||||
|
v-if="viewType === 'front'"
|
||||||
|
:src="require(`@/modules/cmdb/assets/dcim/rack_front_part.png`)"
|
||||||
|
class="rack-container-main-right-part-3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rack-container-footer">
|
||||||
|
<template v-if="viewType === 'front'">
|
||||||
|
<div class="rack-container-footer-dot"></div>
|
||||||
|
<div class="rack-container-footer-dot"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AbnormalModal
|
||||||
|
ref="abnormalModalRef"
|
||||||
|
@ok="editDevice"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { deleteDevice } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
|
||||||
|
import RackHeader from './rackHeader/index.vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import CIIcon from '@/modules/cmdb/components/ciIcon/index.vue'
|
||||||
|
import AbnormalModal from './abnormalModal.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'RackUnitView',
|
||||||
|
components: {
|
||||||
|
RackHeader,
|
||||||
|
draggable,
|
||||||
|
CIIcon,
|
||||||
|
AbnormalModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
viewType: {
|
||||||
|
type: String,
|
||||||
|
default: 'front'
|
||||||
|
},
|
||||||
|
countList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
unitList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
rackId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
oldDraggableList: [],
|
||||||
|
draggableDevice: {},
|
||||||
|
|
||||||
|
unitHeight: 24
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
titleData() {
|
||||||
|
return {
|
||||||
|
icon: this.viewType === 'front' ? 'veops-front' : 'veops-rear',
|
||||||
|
text: this.viewType === 'front' ? 'cmdb.dcim.frontView' : 'cmdb.dcim.rearView'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addDevice(index) {
|
||||||
|
const sliceUnitList = this.unitList.slice(0, index)
|
||||||
|
const unitCount = sliceUnitList.reduce((acc, cur) => acc + cur.unitCount, 0)
|
||||||
|
|
||||||
|
this.$emit('openDeviceForm', {
|
||||||
|
unitStart: this.countList.length - unitCount
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
editDevice(data) {
|
||||||
|
this.$emit('openDeviceForm', {
|
||||||
|
CITypeId: data?._type,
|
||||||
|
deviceId: data?.id,
|
||||||
|
unitStart: data?.u_start,
|
||||||
|
unitCount: data?.u_count,
|
||||||
|
name: data?.name
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDraggableStart(e) {
|
||||||
|
this.oldDraggableList = _.cloneDeep(this.unitList)
|
||||||
|
this.draggableDevice = this.oldDraggableList?.[e.oldIndex] || {}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDraggableEnd(e) {
|
||||||
|
if (e.newIndex === e.oldIndex) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const sliceUnitList = this.unitList.slice(0, e.newIndex)
|
||||||
|
const unitCount = sliceUnitList.reduce((acc, cur) => acc + cur.unitCount, 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拖拽后的起始U位 = 总U数 - 该设备以上的U数 - 该设备U数 + 1
|
||||||
|
*/
|
||||||
|
const startUnit = this.countList.length - unitCount - this.draggableDevice.unitCount + 1
|
||||||
|
|
||||||
|
if (this?.draggableDevice?.id) {
|
||||||
|
this.$emit('draggable', {
|
||||||
|
startUnit,
|
||||||
|
deviceId: this.draggableDevice.id,
|
||||||
|
oldUnitList: this.oldDraggableList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.draggableDevice = {}
|
||||||
|
this.oldDraggableList = []
|
||||||
|
},
|
||||||
|
|
||||||
|
getNameList(item) {
|
||||||
|
const nameList = [item]
|
||||||
|
|
||||||
|
if (item?.abnormalList?.length) {
|
||||||
|
nameList.push(...item.abnormalList)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameList
|
||||||
|
},
|
||||||
|
|
||||||
|
clickDevice(data) {
|
||||||
|
if (data.abnormal) {
|
||||||
|
this.$refs.abnormalModalRef.open(data)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
removeDevice(data) {
|
||||||
|
const content = this.$t('cmdb.dcim.removeDeviceTip', {
|
||||||
|
deviceName: `${data.CITypeName} ${data.name}`
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('warning'),
|
||||||
|
content,
|
||||||
|
onOk: () => {
|
||||||
|
deleteDevice(
|
||||||
|
this.rackId,
|
||||||
|
data.id
|
||||||
|
).then(() => {
|
||||||
|
this.$message.success(this.$t('deleteSuccess'))
|
||||||
|
this.$emit('refreshRackAllData')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
migrateDevice(data) {
|
||||||
|
this.$emit('migrateDevice', data.id)
|
||||||
|
},
|
||||||
|
|
||||||
|
openDeviceDetail(deviceData) {
|
||||||
|
this.$emit('openDeviceDetail', deviceData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.rack-container {
|
||||||
|
width: 236px;
|
||||||
|
|
||||||
|
.rack-title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #4E5969;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
min-width: 17px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: solid 1px rgba(116, 138, 171, 0.25);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-list {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&-device {
|
||||||
|
background-color: #2C2D31;
|
||||||
|
border-bottom: solid 1px rgba(116, 138, 171, 0.25);
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
width: 195px;
|
||||||
|
height: 6px;
|
||||||
|
clip-path: polygon(20px 0, 175px 0, 195px 100%, 0px 100%);
|
||||||
|
background-color: #5D6271;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 195px;
|
||||||
|
height: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #10D4FF;
|
||||||
|
background: linear-gradient(90deg, rgba(0, 0, 0, 0.80) 0%, rgba(102, 102, 102, 0.80) 100%);
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 0 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-left: solid 1px rgba(165, 169, 188, 0.44);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #10D4FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-abnormal {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #F00;
|
||||||
|
background-color: rgba(128, 47, 47, 0.66);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-left: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.rack-container-main-list-device-name-text {
|
||||||
|
color: #3F75FF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-sider {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 140px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
row-gap: 6px;
|
||||||
|
padding-left: 7px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 5%;
|
||||||
|
left: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 90%;
|
||||||
|
border: solid 1px #10D4FF;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&_normal:hover {
|
||||||
|
.rack-container-main-list-device-action {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-gap {
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: solid 1px rgba(116, 138, 171, 0.25);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #EBEFF8;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: rgba(0, 87, 255, 0.80);
|
||||||
|
margin-left: 6px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&_rear {
|
||||||
|
background-color: #CACDD9;
|
||||||
|
border-bottom: solid 1px #E4E7ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #D5DDEE;
|
||||||
|
|
||||||
|
.rack-container-main-list-gap-icon {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rack-container-main-list-gap-text {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
background-color: #86909C;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-part-1 {
|
||||||
|
width: 7px;
|
||||||
|
height: 100%;
|
||||||
|
border-right: solid 1px rgba(255, 255, 255, 0.33);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-part-2 {
|
||||||
|
width: 7px;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(270deg, rgba(134, 144, 156, 0.00) 0%, rgba(69, 78, 89, 0.88) 100%);
|
||||||
|
filter: blur(0.25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-part-3 {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
width: 21px;
|
||||||
|
height: 57.6px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-footer {
|
||||||
|
height: 12px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #86909C;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0px 14px;
|
||||||
|
|
||||||
|
&-dot {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #E8EBEE;
|
||||||
|
border: solid 1px #FFFFFF;
|
||||||
|
box-shadow: 3px 3px 7px 0px rgba(136, 150, 163, 0.58) inset, -3px -3px 7px 0px #FFF inset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,36 @@
|
||||||
|
export const DCIM_TYPE = {
|
||||||
|
REGION: 'region',
|
||||||
|
IDC: 'idc',
|
||||||
|
SERVER_ROOM: 'server_room',
|
||||||
|
RACK: 'rack'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DCIM_CITYPE_NAME = {
|
||||||
|
REGION: 'dcim_region',
|
||||||
|
IDC: 'dcim_idc',
|
||||||
|
SERVER_ROOM: 'dcim_server_room',
|
||||||
|
RACK: 'dcim_rack'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEVICE_CITYPE_NAME = {
|
||||||
|
SWITCH: 'switch',
|
||||||
|
FC_SWITCH: 'fc_switch',
|
||||||
|
F5: 'bigip',
|
||||||
|
ROUTER: 'router',
|
||||||
|
FIRE_WALL: 'firewall',
|
||||||
|
SERVER: 'server',
|
||||||
|
RAID: 'raid'
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTypeNameMap = (typeObj, typeNameObj) => {
|
||||||
|
const map = {}
|
||||||
|
|
||||||
|
Object.keys(typeObj).forEach(key => {
|
||||||
|
map[typeObj[key]] = typeNameObj[key]
|
||||||
|
map[typeNameObj[key]] = typeObj[key]
|
||||||
|
})
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DCIM_TYPE_NAME_MAP = createTypeNameMap(DCIM_TYPE, DCIM_CITYPE_NAME)
|
|
@ -0,0 +1,267 @@
|
||||||
|
<template>
|
||||||
|
<TwoColumnLayout
|
||||||
|
class="dcim"
|
||||||
|
appName="cmdb-dcim"
|
||||||
|
calcBasedParent
|
||||||
|
>
|
||||||
|
<template #one>
|
||||||
|
<DCIMTree
|
||||||
|
:treeData="treeData"
|
||||||
|
:treeKey="treeKey"
|
||||||
|
@getAttrList="getAttrList"
|
||||||
|
@updateTreeKey="updateTreeKey"
|
||||||
|
@openForm="openForm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DCIMForm
|
||||||
|
ref="dcimFormRef"
|
||||||
|
:allAttrList="allAttrList"
|
||||||
|
@ok="handleDCIMFormOk"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #two>
|
||||||
|
<DCIMMain
|
||||||
|
v-if="!initLoading && rackCITYpe.id"
|
||||||
|
ref="dcimMainRef"
|
||||||
|
:roomId="treeKey"
|
||||||
|
:attrObj="allAttrList[DCIM_TYPE.RACK]"
|
||||||
|
:rackCITYpe="rackCITYpe"
|
||||||
|
:preferenceAttrList="rackPreferenceAttrList"
|
||||||
|
@openForm="openForm"
|
||||||
|
@refreshTreeData="getTreeData"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</TwoColumnLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getDCIMTreeView } from '@/modules/cmdb/api/dcim.js'
|
||||||
|
import { DCIM_CITYPE_NAME, DCIM_TYPE, DCIM_TYPE_NAME_MAP } from './constants.js'
|
||||||
|
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||||
|
import { getCIType } from '@/modules/cmdb/api/CIType.js'
|
||||||
|
import { getSubscribeAttributes } from '@/modules/cmdb/api/preference'
|
||||||
|
|
||||||
|
import TwoColumnLayout from '@/components/TwoColumnLayout'
|
||||||
|
import DCIMTree from './components/dcimTree.vue'
|
||||||
|
import DCIMForm from './components/dcimForm.vue'
|
||||||
|
import DCIMMain from './components/dcimMain/index.vue'
|
||||||
|
|
||||||
|
const TREE_STORAGE_KEY = 'ops_dcim_tree_active'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DCIM',
|
||||||
|
components: {
|
||||||
|
TwoColumnLayout,
|
||||||
|
DCIMTree,
|
||||||
|
DCIMForm,
|
||||||
|
DCIMMain
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
DCIM_TYPE,
|
||||||
|
treeKey: localStorage.getItem(TREE_STORAGE_KEY) || '',
|
||||||
|
treeData: [],
|
||||||
|
allAttrList: {
|
||||||
|
[DCIM_TYPE.REGION]: {},
|
||||||
|
[DCIM_TYPE.IDC]: {},
|
||||||
|
[DCIM_TYPE.SERVER_ROOM]: {},
|
||||||
|
[DCIM_TYPE.RACK]: {}
|
||||||
|
},
|
||||||
|
|
||||||
|
initLoading: true,
|
||||||
|
rackCITYpe: {},
|
||||||
|
rackPreferenceAttrList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
this.initLoading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.getTreeData()
|
||||||
|
await this.getRackData()
|
||||||
|
} catch (error) {
|
||||||
|
console.log('initData fail', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initLoading = false
|
||||||
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
getTreeData: this.getTreeData
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getTreeData() {
|
||||||
|
const res = await getDCIMTreeView()
|
||||||
|
let treeData = []
|
||||||
|
|
||||||
|
if (res?.result?.length) {
|
||||||
|
treeData = res.result.map((data) => {
|
||||||
|
return this.handleTreeData(data, res.type2name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentNode = this.findNodeById(treeData, this.treeKey)
|
||||||
|
if (!currentNode) {
|
||||||
|
this.updateTreeKey('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const flatRreeData = []
|
||||||
|
treeData.forEach((item) => {
|
||||||
|
flatRreeData.push({
|
||||||
|
...item,
|
||||||
|
class: 'ipam-tree-node_hide_expand',
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
if (item.children.length) {
|
||||||
|
flatRreeData.push(...item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.treeData = flatRreeData
|
||||||
|
},
|
||||||
|
|
||||||
|
handleTreeData(data, type2name, parentId = '') {
|
||||||
|
const title = data?.[type2name?.[data?._type]] || ''
|
||||||
|
const dcimType = DCIM_TYPE_NAME_MAP[data.ci_type]
|
||||||
|
let icon = ''
|
||||||
|
let iconColor = '#A5A9BC'
|
||||||
|
let addType = ''
|
||||||
|
|
||||||
|
const key = String(data._id)
|
||||||
|
|
||||||
|
switch (data.ci_type) {
|
||||||
|
case DCIM_CITYPE_NAME.REGION:
|
||||||
|
icon = 'veops-region'
|
||||||
|
iconColor = '#2F54EB'
|
||||||
|
addType = DCIM_TYPE.IDC
|
||||||
|
break
|
||||||
|
case DCIM_CITYPE_NAME.IDC:
|
||||||
|
icon = 'veops-IDC'
|
||||||
|
addType = DCIM_TYPE.SERVER_ROOM
|
||||||
|
break
|
||||||
|
case DCIM_CITYPE_NAME.SERVER_ROOM:
|
||||||
|
icon = 'a-veops-room1'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data?.children?.length) {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
key,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
iconColor,
|
||||||
|
parentId,
|
||||||
|
addType,
|
||||||
|
dcimType,
|
||||||
|
count: data?.rack_count || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = data.children.map((item) => {
|
||||||
|
return this.handleTreeData(item, type2name, key)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
key,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
iconColor,
|
||||||
|
addType,
|
||||||
|
parentId,
|
||||||
|
children,
|
||||||
|
dcimType,
|
||||||
|
count: children.reduce((acc, item) => {
|
||||||
|
return acc + item.count
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
findNodeById(nodes, id) {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.key === id) {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
const foundNode = this.findNodeById(node.children, id)
|
||||||
|
if (foundNode) {
|
||||||
|
return foundNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRackData() {
|
||||||
|
await this.getAttrList(DCIM_CITYPE_NAME.RACK, DCIM_TYPE.RACK)
|
||||||
|
|
||||||
|
const CITypeRes = await getCIType(DCIM_CITYPE_NAME.RACK)
|
||||||
|
this.rackCITYpe = CITypeRes?.ci_types?.[0] || {}
|
||||||
|
|
||||||
|
if (this.rackCITYpe.id) {
|
||||||
|
const subscribed = await getSubscribeAttributes(this.rackCITYpe.id)
|
||||||
|
this.rackPreferenceAttrList = subscribed.attributes
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAttrList(id, type, cb) {
|
||||||
|
if (Object.keys(this?.allAttrList?.[type] || {})?.length === 0) {
|
||||||
|
const res = await getCITypeAttributesById(id)
|
||||||
|
this.$set(this.allAttrList, type, res || {})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
cb(this.allAttrList)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async openForm(data) {
|
||||||
|
await this.getAttrList(DCIM_TYPE_NAME_MAP[data.dcimType], data.dcimType)
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.dcimFormRef.open(data)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTreeKey(key) {
|
||||||
|
this.treeKey = key
|
||||||
|
localStorage.setItem(TREE_STORAGE_KEY, key)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleDCIMFormOk({
|
||||||
|
dcimType,
|
||||||
|
editType
|
||||||
|
}) {
|
||||||
|
switch (dcimType) {
|
||||||
|
case DCIM_TYPE.REGION:
|
||||||
|
case DCIM_TYPE.IDC:
|
||||||
|
case DCIM_TYPE.SERVER_ROOM:
|
||||||
|
this.getTreeData()
|
||||||
|
break
|
||||||
|
case DCIM_TYPE.RACK:
|
||||||
|
this.getRackList()
|
||||||
|
if (editType === 'create') {
|
||||||
|
this.getTreeData()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getRackList() {
|
||||||
|
if (this.$refs.dcimMainRef) {
|
||||||
|
this.$refs.dcimMainRef.getRackList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|