mirror of https://github.com/veops/cmdb.git
Merge pull request #609 from veops/dev_ui_240903
feat: update resource search
This commit is contained in:
commit
42ad2b6dde
|
@ -54,93 +54,171 @@
|
||||||
<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-expand</div>
|
||||||
|
<div class="code-name">&#xe9b6;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">公有云</div>
|
||||||
|
<div class="code-name">&#xe9b1;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">操作系统</div>
|
||||||
|
<div class="code-name">&#xe9b2;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">IPAM</div>
|
||||||
|
<div class="code-name">&#xe9b3;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">hyperV</div>
|
||||||
|
<div class="code-name">&#xe9b4;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">数据中心</div>
|
||||||
|
<div class="code-name">&#xe9b5;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">硬件设备</div>
|
||||||
|
<div class="code-name">&#xe9ad;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">计算机</div>
|
||||||
|
<div class="code-name">&#xe9ae;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">网络设备</div>
|
||||||
|
<div class="code-name">&#xe9af;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">存储设备</div>
|
||||||
|
<div class="code-name">&#xe9b0;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">负载均衡</div>
|
||||||
|
<div class="code-name">&#xe9ab;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">消息队列</div>
|
||||||
|
<div class="code-name">&#xe9ac;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont"></span>
|
||||||
|
<div class="name">websever</div>
|
||||||
|
<div class="code-name">&#xe9aa;</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-middleware</div>
|
<div class="name">中间件</div>
|
||||||
<div class="code-name">&#xe9a9;</div>
|
<div class="code-name">&#xe9a9;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-database</div>
|
<div class="name">数据库</div>
|
||||||
<div class="code-name">&#xe9a7;</div>
|
<div class="code-name">&#xe9a7;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-business</div>
|
<div class="name">业务</div>
|
||||||
<div class="code-name">&#xe9a8;</div>
|
<div class="code-name">&#xe9a8;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise- virtualization</div>
|
<div class="name">虚拟化</div>
|
||||||
<div class="code-name">&#xe9a6;</div>
|
<div class="code-name">&#xe9a6;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-storage_pool</div>
|
<div class="name">存储池</div>
|
||||||
<div class="code-name">&#xe9a4;</div>
|
<div class="code-name">&#xe9a4;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-storage_volume</div>
|
<div class="name">存储卷</div>
|
||||||
<div class="code-name">&#xe9a5;</div>
|
<div class="code-name">&#xe9a5;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">ciase-aix</div>
|
<div class="name">aix</div>
|
||||||
<div class="code-name">&#xe9a3;</div>
|
<div class="code-name">&#xe9a3;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise_pool</div>
|
<div class="name">ip池</div>
|
||||||
<div class="code-name">&#xe99b;</div>
|
<div class="code-name">&#xe99b;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-ip_address</div>
|
<div class="name">ip地址</div>
|
||||||
<div class="code-name">&#xe99c;</div>
|
<div class="code-name">&#xe99c;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-computer_room</div>
|
<div class="name">机房</div>
|
||||||
<div class="code-name">&#xe99d;</div>
|
<div class="code-name">&#xe99d;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-rack</div>
|
<div class="name">机柜</div>
|
||||||
<div class="code-name">&#xe99e;</div>
|
<div class="code-name">&#xe99e;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-pc</div>
|
<div class="name">PC</div>
|
||||||
<div class="code-name">&#xe99f;</div>
|
<div class="code-name">&#xe99f;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-bandwidth_line</div>
|
<div class="name">带宽线路</div>
|
||||||
<div class="code-name">&#xe9a0;</div>
|
<div class="code-name">&#xe9a0;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-fiber</div>
|
<div class="name">光纤交换机</div>
|
||||||
<div class="code-name">&#xe9a1;</div>
|
<div class="code-name">&#xe9a1;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont"></span>
|
<span class="icon iconfont"></span>
|
||||||
<div class="name">caise-disk_array</div>
|
<div class="name">磁盘阵列</div>
|
||||||
<div class="code-name">&#xe9a2;</div>
|
<div class="code-name">&#xe9a2;</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -5622,9 +5700,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=1724834571283') format('woff2'),
|
src: url('iconfont.woff2?t=1725331691589') format('woff2'),
|
||||||
url('iconfont.woff?t=1724834571283') format('woff'),
|
url('iconfont.woff?t=1725331691589') format('woff'),
|
||||||
url('iconfont.ttf?t=1724834571283') format('truetype');
|
url('iconfont.ttf?t=1725331691589') format('truetype');
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||||
|
@ -5650,10 +5728,127 @@
|
||||||
<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-expand"></span>
|
||||||
|
<div class="name">
|
||||||
|
veops-expand
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.veops-expand
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-public_cloud"></span>
|
||||||
|
<div class="name">
|
||||||
|
公有云
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-public_cloud
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-system"></span>
|
||||||
|
<div class="name">
|
||||||
|
操作系统
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-system
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-IPAM"></span>
|
||||||
|
<div class="name">
|
||||||
|
IPAM
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-IPAM
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-hyperV"></span>
|
||||||
|
<div class="name">
|
||||||
|
hyperV
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-hyperV
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-data_center2"></span>
|
||||||
|
<div class="name">
|
||||||
|
数据中心
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-data_center2
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-hardware"></span>
|
||||||
|
<div class="name">
|
||||||
|
硬件设备
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-hardware
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-computer"></span>
|
||||||
|
<div class="name">
|
||||||
|
计算机
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-computer
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-network_devices"></span>
|
||||||
|
<div class="name">
|
||||||
|
网络设备
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-network_devices
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-storage_device"></span>
|
||||||
|
<div class="name">
|
||||||
|
存储设备
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-storage_device
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-load_balancing"></span>
|
||||||
|
<div class="name">
|
||||||
|
负载均衡
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-load_balancing
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-message_queue"></span>
|
||||||
|
<div class="name">
|
||||||
|
消息队列
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-message_queue
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<span class="icon iconfont caise-websever"></span>
|
||||||
|
<div class="name">
|
||||||
|
websever
|
||||||
|
</div>
|
||||||
|
<div class="code-name">.caise-websever
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-middleware"></span>
|
<span class="icon iconfont caise-middleware"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-middleware
|
中间件
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-middleware
|
<div class="code-name">.caise-middleware
|
||||||
</div>
|
</div>
|
||||||
|
@ -5662,7 +5857,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-database"></span>
|
<span class="icon iconfont caise-database"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-database
|
数据库
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-database
|
<div class="code-name">.caise-database
|
||||||
</div>
|
</div>
|
||||||
|
@ -5671,43 +5866,43 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-business"></span>
|
<span class="icon iconfont caise-business"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-business
|
业务
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-business
|
<div class="code-name">.caise-business
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont a-caise-virtualization"></span>
|
<span class="icon iconfont caise-virtualization"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise- virtualization
|
虚拟化
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.a-caise-virtualization
|
<div class="code-name">.caise-virtualization
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-storage_pool"></span>
|
<span class="icon iconfont caise-storage_pool"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-storage_pool
|
存储池
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-storage_pool
|
<div class="code-name">.caise-storage_pool
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont a-caise-storage_volume1"></span>
|
<span class="icon iconfont caise-storage_volume1"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-storage_volume
|
存储卷
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.a-caise-storage_volume1
|
<div class="code-name">.caise-storage_volume1
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont ciase-aix"></span>
|
<span class="icon iconfont ciase-aix"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
ciase-aix
|
aix
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.ciase-aix
|
<div class="code-name">.ciase-aix
|
||||||
</div>
|
</div>
|
||||||
|
@ -5716,7 +5911,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise_pool"></span>
|
<span class="icon iconfont caise_pool"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise_pool
|
ip池
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise_pool
|
<div class="code-name">.caise_pool
|
||||||
</div>
|
</div>
|
||||||
|
@ -5725,7 +5920,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-ip_address"></span>
|
<span class="icon iconfont caise-ip_address"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-ip_address
|
ip地址
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-ip_address
|
<div class="code-name">.caise-ip_address
|
||||||
</div>
|
</div>
|
||||||
|
@ -5734,7 +5929,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-computer_room"></span>
|
<span class="icon iconfont caise-computer_room"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-computer_room
|
机房
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-computer_room
|
<div class="code-name">.caise-computer_room
|
||||||
</div>
|
</div>
|
||||||
|
@ -5743,7 +5938,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-rack"></span>
|
<span class="icon iconfont caise-rack"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-rack
|
机柜
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-rack
|
<div class="code-name">.caise-rack
|
||||||
</div>
|
</div>
|
||||||
|
@ -5752,7 +5947,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-pc"></span>
|
<span class="icon iconfont caise-pc"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-pc
|
PC
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-pc
|
<div class="code-name">.caise-pc
|
||||||
</div>
|
</div>
|
||||||
|
@ -5761,7 +5956,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-bandwidth_line"></span>
|
<span class="icon iconfont caise-bandwidth_line"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-bandwidth_line
|
带宽线路
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-bandwidth_line
|
<div class="code-name">.caise-bandwidth_line
|
||||||
</div>
|
</div>
|
||||||
|
@ -5770,7 +5965,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-fiber"></span>
|
<span class="icon iconfont caise-fiber"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-fiber
|
光纤交换机
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-fiber
|
<div class="code-name">.caise-fiber
|
||||||
</div>
|
</div>
|
||||||
|
@ -5779,7 +5974,7 @@
|
||||||
<li class="dib">
|
<li class="dib">
|
||||||
<span class="icon iconfont caise-disk_array"></span>
|
<span class="icon iconfont caise-disk_array"></span>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
caise-disk_array
|
磁盘阵列
|
||||||
</div>
|
</div>
|
||||||
<div class="code-name">.caise-disk_array
|
<div class="code-name">.caise-disk_array
|
||||||
</div>
|
</div>
|
||||||
|
@ -14002,11 +14197,115 @@
|
||||||
<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-expand"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">veops-expand</div>
|
||||||
|
<div class="code-name">#veops-expand</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-public_cloud"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">公有云</div>
|
||||||
|
<div class="code-name">#caise-public_cloud</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-system"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">操作系统</div>
|
||||||
|
<div class="code-name">#caise-system</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-IPAM"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">IPAM</div>
|
||||||
|
<div class="code-name">#caise-IPAM</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-hyperV"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">hyperV</div>
|
||||||
|
<div class="code-name">#caise-hyperV</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-data_center2"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">数据中心</div>
|
||||||
|
<div class="code-name">#caise-data_center2</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-hardware"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">硬件设备</div>
|
||||||
|
<div class="code-name">#caise-hardware</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-computer"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">计算机</div>
|
||||||
|
<div class="code-name">#caise-computer</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-network_devices"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">网络设备</div>
|
||||||
|
<div class="code-name">#caise-network_devices</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-storage_device"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">存储设备</div>
|
||||||
|
<div class="code-name">#caise-storage_device</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-load_balancing"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">负载均衡</div>
|
||||||
|
<div class="code-name">#caise-load_balancing</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-message_queue"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">消息队列</div>
|
||||||
|
<div class="code-name">#caise-message_queue</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="dib">
|
||||||
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
|
<use xlink:href="#caise-websever"></use>
|
||||||
|
</svg>
|
||||||
|
<div class="name">websever</div>
|
||||||
|
<div class="code-name">#caise-websever</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="#caise-middleware"></use>
|
<use xlink:href="#caise-middleware"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-middleware</div>
|
<div class="name">中间件</div>
|
||||||
<div class="code-name">#caise-middleware</div>
|
<div class="code-name">#caise-middleware</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14014,7 +14313,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-database"></use>
|
<use xlink:href="#caise-database"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-database</div>
|
<div class="name">数据库</div>
|
||||||
<div class="code-name">#caise-database</div>
|
<div class="code-name">#caise-database</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14022,39 +14321,39 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-business"></use>
|
<use xlink:href="#caise-business"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-business</div>
|
<div class="name">业务</div>
|
||||||
<div class="code-name">#caise-business</div>
|
<div class="code-name">#caise-business</div>
|
||||||
</li>
|
</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="#a-caise-virtualization"></use>
|
<use xlink:href="#caise-virtualization"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise- virtualization</div>
|
<div class="name">虚拟化</div>
|
||||||
<div class="code-name">#a-caise-virtualization</div>
|
<div class="code-name">#caise-virtualization</div>
|
||||||
</li>
|
</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="#caise-storage_pool"></use>
|
<use xlink:href="#caise-storage_pool"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-storage_pool</div>
|
<div class="name">存储池</div>
|
||||||
<div class="code-name">#caise-storage_pool</div>
|
<div class="code-name">#caise-storage_pool</div>
|
||||||
</li>
|
</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="#a-caise-storage_volume1"></use>
|
<use xlink:href="#caise-storage_volume1"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-storage_volume</div>
|
<div class="name">存储卷</div>
|
||||||
<div class="code-name">#a-caise-storage_volume1</div>
|
<div class="code-name">#caise-storage_volume1</div>
|
||||||
</li>
|
</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="#ciase-aix"></use>
|
<use xlink:href="#ciase-aix"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">ciase-aix</div>
|
<div class="name">aix</div>
|
||||||
<div class="code-name">#ciase-aix</div>
|
<div class="code-name">#ciase-aix</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14062,7 +14361,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise_pool"></use>
|
<use xlink:href="#caise_pool"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise_pool</div>
|
<div class="name">ip池</div>
|
||||||
<div class="code-name">#caise_pool</div>
|
<div class="code-name">#caise_pool</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14070,7 +14369,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-ip_address"></use>
|
<use xlink:href="#caise-ip_address"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-ip_address</div>
|
<div class="name">ip地址</div>
|
||||||
<div class="code-name">#caise-ip_address</div>
|
<div class="code-name">#caise-ip_address</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14078,7 +14377,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-computer_room"></use>
|
<use xlink:href="#caise-computer_room"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-computer_room</div>
|
<div class="name">机房</div>
|
||||||
<div class="code-name">#caise-computer_room</div>
|
<div class="code-name">#caise-computer_room</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14086,7 +14385,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-rack"></use>
|
<use xlink:href="#caise-rack"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-rack</div>
|
<div class="name">机柜</div>
|
||||||
<div class="code-name">#caise-rack</div>
|
<div class="code-name">#caise-rack</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14094,7 +14393,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-pc"></use>
|
<use xlink:href="#caise-pc"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-pc</div>
|
<div class="name">PC</div>
|
||||||
<div class="code-name">#caise-pc</div>
|
<div class="code-name">#caise-pc</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14102,7 +14401,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-bandwidth_line"></use>
|
<use xlink:href="#caise-bandwidth_line"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-bandwidth_line</div>
|
<div class="name">带宽线路</div>
|
||||||
<div class="code-name">#caise-bandwidth_line</div>
|
<div class="code-name">#caise-bandwidth_line</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14110,7 +14409,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-fiber"></use>
|
<use xlink:href="#caise-fiber"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-fiber</div>
|
<div class="name">光纤交换机</div>
|
||||||
<div class="code-name">#caise-fiber</div>
|
<div class="code-name">#caise-fiber</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
@ -14118,7 +14417,7 @@
|
||||||
<svg class="icon svg-icon" aria-hidden="true">
|
<svg class="icon svg-icon" aria-hidden="true">
|
||||||
<use xlink:href="#caise-disk_array"></use>
|
<use xlink:href="#caise-disk_array"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="name">caise-disk_array</div>
|
<div class="name">磁盘阵列</div>
|
||||||
<div class="code-name">#caise-disk_array</div>
|
<div class="code-name">#caise-disk_array</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
|
@ -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=1724834571283') format('woff2'),
|
src: url('iconfont.woff2?t=1725331691589') format('woff2'),
|
||||||
url('iconfont.woff?t=1724834571283') format('woff'),
|
url('iconfont.woff?t=1725331691589') format('woff'),
|
||||||
url('iconfont.ttf?t=1724834571283') format('truetype');
|
url('iconfont.ttf?t=1725331691589') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
|
@ -13,6 +13,58 @@
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.veops-expand:before {
|
||||||
|
content: "\e9b6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-public_cloud:before {
|
||||||
|
content: "\e9b1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-system:before {
|
||||||
|
content: "\e9b2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-IPAM:before {
|
||||||
|
content: "\e9b3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-hyperV:before {
|
||||||
|
content: "\e9b4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-data_center2:before {
|
||||||
|
content: "\e9b5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-hardware:before {
|
||||||
|
content: "\e9ad";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-computer:before {
|
||||||
|
content: "\e9ae";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-network_devices:before {
|
||||||
|
content: "\e9af";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-storage_device:before {
|
||||||
|
content: "\e9b0";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-load_balancing:before {
|
||||||
|
content: "\e9ab";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-message_queue:before {
|
||||||
|
content: "\e9ac";
|
||||||
|
}
|
||||||
|
|
||||||
|
.caise-websever:before {
|
||||||
|
content: "\e9aa";
|
||||||
|
}
|
||||||
|
|
||||||
.caise-middleware:before {
|
.caise-middleware:before {
|
||||||
content: "\e9a9";
|
content: "\e9a9";
|
||||||
}
|
}
|
||||||
|
@ -25,7 +77,7 @@
|
||||||
content: "\e9a8";
|
content: "\e9a8";
|
||||||
}
|
}
|
||||||
|
|
||||||
.a-caise-virtualization:before {
|
.caise-virtualization:before {
|
||||||
content: "\e9a6";
|
content: "\e9a6";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +85,7 @@
|
||||||
content: "\e9a4";
|
content: "\e9a4";
|
||||||
}
|
}
|
||||||
|
|
||||||
.a-caise-storage_volume1:before {
|
.caise-storage_volume1:before {
|
||||||
content: "\e9a5";
|
content: "\e9a5";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,107 +5,198 @@
|
||||||
"css_prefix_text": "",
|
"css_prefix_text": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"glyphs": [
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"icon_id": "41681675",
|
||||||
|
"name": "veops-expand",
|
||||||
|
"font_class": "veops-expand",
|
||||||
|
"unicode": "e9b6",
|
||||||
|
"unicode_decimal": 59830
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41672951",
|
||||||
|
"name": "公有云",
|
||||||
|
"font_class": "caise-public_cloud",
|
||||||
|
"unicode": "e9b1",
|
||||||
|
"unicode_decimal": 59825
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41672952",
|
||||||
|
"name": "操作系统",
|
||||||
|
"font_class": "caise-system",
|
||||||
|
"unicode": "e9b2",
|
||||||
|
"unicode_decimal": 59826
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41673309",
|
||||||
|
"name": "IPAM",
|
||||||
|
"font_class": "caise-IPAM",
|
||||||
|
"unicode": "e9b3",
|
||||||
|
"unicode_decimal": 59827
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41673312",
|
||||||
|
"name": "hyperV",
|
||||||
|
"font_class": "caise-hyperV",
|
||||||
|
"unicode": "e9b4",
|
||||||
|
"unicode_decimal": 59828
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41673320",
|
||||||
|
"name": "数据中心",
|
||||||
|
"font_class": "caise-data_center2",
|
||||||
|
"unicode": "e9b5",
|
||||||
|
"unicode_decimal": 59829
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41669141",
|
||||||
|
"name": "硬件设备",
|
||||||
|
"font_class": "caise-hardware",
|
||||||
|
"unicode": "e9ad",
|
||||||
|
"unicode_decimal": 59821
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41669249",
|
||||||
|
"name": "计算机",
|
||||||
|
"font_class": "caise-computer",
|
||||||
|
"unicode": "e9ae",
|
||||||
|
"unicode_decimal": 59822
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41669250",
|
||||||
|
"name": "网络设备",
|
||||||
|
"font_class": "caise-network_devices",
|
||||||
|
"unicode": "e9af",
|
||||||
|
"unicode_decimal": 59823
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41669278",
|
||||||
|
"name": "存储设备",
|
||||||
|
"font_class": "caise-storage_device",
|
||||||
|
"unicode": "e9b0",
|
||||||
|
"unicode_decimal": 59824
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41659452",
|
||||||
|
"name": "负载均衡",
|
||||||
|
"font_class": "caise-load_balancing",
|
||||||
|
"unicode": "e9ab",
|
||||||
|
"unicode_decimal": 59819
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41659446",
|
||||||
|
"name": "消息队列",
|
||||||
|
"font_class": "caise-message_queue",
|
||||||
|
"unicode": "e9ac",
|
||||||
|
"unicode_decimal": 59820
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon_id": "41659424",
|
||||||
|
"name": "websever",
|
||||||
|
"font_class": "caise-websever",
|
||||||
|
"unicode": "e9aa",
|
||||||
|
"unicode_decimal": 59818
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41655608",
|
"icon_id": "41655608",
|
||||||
"name": "caise-middleware",
|
"name": "中间件",
|
||||||
"font_class": "caise-middleware",
|
"font_class": "caise-middleware",
|
||||||
"unicode": "e9a9",
|
"unicode": "e9a9",
|
||||||
"unicode_decimal": 59817
|
"unicode_decimal": 59817
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41655599",
|
"icon_id": "41655599",
|
||||||
"name": "caise-database",
|
"name": "数据库",
|
||||||
"font_class": "caise-database",
|
"font_class": "caise-database",
|
||||||
"unicode": "e9a7",
|
"unicode": "e9a7",
|
||||||
"unicode_decimal": 59815
|
"unicode_decimal": 59815
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41655591",
|
"icon_id": "41655591",
|
||||||
"name": "caise-business",
|
"name": "业务",
|
||||||
"font_class": "caise-business",
|
"font_class": "caise-business",
|
||||||
"unicode": "e9a8",
|
"unicode": "e9a8",
|
||||||
"unicode_decimal": 59816
|
"unicode_decimal": 59816
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41655550",
|
"icon_id": "41655550",
|
||||||
"name": "caise- virtualization",
|
"name": "虚拟化",
|
||||||
"font_class": "a-caise-virtualization",
|
"font_class": "caise-virtualization",
|
||||||
"unicode": "e9a6",
|
"unicode": "e9a6",
|
||||||
"unicode_decimal": 59814
|
"unicode_decimal": 59814
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654680",
|
"icon_id": "41654680",
|
||||||
"name": "caise-storage_pool",
|
"name": "存储池",
|
||||||
"font_class": "caise-storage_pool",
|
"font_class": "caise-storage_pool",
|
||||||
"unicode": "e9a4",
|
"unicode": "e9a4",
|
||||||
"unicode_decimal": 59812
|
"unicode_decimal": 59812
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654676",
|
"icon_id": "41654676",
|
||||||
"name": "caise-storage_volume",
|
"name": "存储卷",
|
||||||
"font_class": "a-caise-storage_volume1",
|
"font_class": "caise-storage_volume1",
|
||||||
"unicode": "e9a5",
|
"unicode": "e9a5",
|
||||||
"unicode_decimal": 59813
|
"unicode_decimal": 59813
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654608",
|
"icon_id": "41654608",
|
||||||
"name": "ciase-aix",
|
"name": "aix",
|
||||||
"font_class": "ciase-aix",
|
"font_class": "ciase-aix",
|
||||||
"unicode": "e9a3",
|
"unicode": "e9a3",
|
||||||
"unicode_decimal": 59811
|
"unicode_decimal": 59811
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654233",
|
"icon_id": "41654233",
|
||||||
"name": "caise_pool",
|
"name": "ip池",
|
||||||
"font_class": "caise_pool",
|
"font_class": "caise_pool",
|
||||||
"unicode": "e99b",
|
"unicode": "e99b",
|
||||||
"unicode_decimal": 59803
|
"unicode_decimal": 59803
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654237",
|
"icon_id": "41654237",
|
||||||
"name": "caise-ip_address",
|
"name": "ip地址",
|
||||||
"font_class": "caise-ip_address",
|
"font_class": "caise-ip_address",
|
||||||
"unicode": "e99c",
|
"unicode": "e99c",
|
||||||
"unicode_decimal": 59804
|
"unicode_decimal": 59804
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654249",
|
"icon_id": "41654249",
|
||||||
"name": "caise-computer_room",
|
"name": "机房",
|
||||||
"font_class": "caise-computer_room",
|
"font_class": "caise-computer_room",
|
||||||
"unicode": "e99d",
|
"unicode": "e99d",
|
||||||
"unicode_decimal": 59805
|
"unicode_decimal": 59805
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654271",
|
"icon_id": "41654271",
|
||||||
"name": "caise-rack",
|
"name": "机柜",
|
||||||
"font_class": "caise-rack",
|
"font_class": "caise-rack",
|
||||||
"unicode": "e99e",
|
"unicode": "e99e",
|
||||||
"unicode_decimal": 59806
|
"unicode_decimal": 59806
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654276",
|
"icon_id": "41654276",
|
||||||
"name": "caise-pc",
|
"name": "PC",
|
||||||
"font_class": "caise-pc",
|
"font_class": "caise-pc",
|
||||||
"unicode": "e99f",
|
"unicode": "e99f",
|
||||||
"unicode_decimal": 59807
|
"unicode_decimal": 59807
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654305",
|
"icon_id": "41654305",
|
||||||
"name": "caise-bandwidth_line",
|
"name": "带宽线路",
|
||||||
"font_class": "caise-bandwidth_line",
|
"font_class": "caise-bandwidth_line",
|
||||||
"unicode": "e9a0",
|
"unicode": "e9a0",
|
||||||
"unicode_decimal": 59808
|
"unicode_decimal": 59808
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654323",
|
"icon_id": "41654323",
|
||||||
"name": "caise-fiber",
|
"name": "光纤交换机",
|
||||||
"font_class": "caise-fiber",
|
"font_class": "caise-fiber",
|
||||||
"unicode": "e9a1",
|
"unicode": "e9a1",
|
||||||
"unicode_decimal": 59809
|
"unicode_decimal": 59809
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"icon_id": "41654369",
|
"icon_id": "41654369",
|
||||||
"name": "caise-disk_array",
|
"name": "磁盘阵列",
|
||||||
"font_class": "caise-disk_array",
|
"font_class": "caise-disk_array",
|
||||||
"unicode": "e9a2",
|
"unicode": "e9a2",
|
||||||
"unicode_decimal": 59810
|
"unicode_decimal": 59810
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -61,12 +61,12 @@ export default {
|
||||||
)
|
)
|
||||||
|
|
||||||
// 注册富文本自定义元素
|
// 注册富文本自定义元素
|
||||||
const resume = {
|
// const resume = {
|
||||||
type: 'attachment',
|
// type: 'attachment',
|
||||||
attachmentLabel: '',
|
// attachmentLabel: '',
|
||||||
attachmentValue: '',
|
// attachmentValue: '',
|
||||||
children: [{ text: '' }], // void 元素必须有一个 children ,其中只有一个空字符串,重要!!!
|
// children: [{ text: '' }], // void 元素必须有一个 children ,其中只有一个空字符串,重要!!!
|
||||||
}
|
// }
|
||||||
|
|
||||||
function withAttachment(editor) {
|
function withAttachment(editor) {
|
||||||
// JS 语法
|
// JS 语法
|
||||||
|
|
|
@ -905,6 +905,84 @@ export const multicolorIconList = [
|
||||||
value: 'caise-application',
|
value: 'caise-application',
|
||||||
label: '应用',
|
label: '应用',
|
||||||
list: [{
|
list: [{
|
||||||
|
value: 'caise-disk_array',
|
||||||
|
label: '磁盘阵列'
|
||||||
|
}, {
|
||||||
|
value: 'caise-fiber',
|
||||||
|
label: '光纤交换机'
|
||||||
|
}, {
|
||||||
|
value: 'caise-bandwidth_line',
|
||||||
|
label: '带宽线路'
|
||||||
|
}, {
|
||||||
|
value: 'caise-pc',
|
||||||
|
label: 'PC'
|
||||||
|
}, {
|
||||||
|
value: 'caise-rack',
|
||||||
|
label: '机柜'
|
||||||
|
}, {
|
||||||
|
value: 'caise-computer_room',
|
||||||
|
label: '机房'
|
||||||
|
}, {
|
||||||
|
value: 'caise-ip_address',
|
||||||
|
label: 'ip地址'
|
||||||
|
}, {
|
||||||
|
value: 'caise_pool',
|
||||||
|
label: 'ip池'
|
||||||
|
}, {
|
||||||
|
value: 'ciase-aix',
|
||||||
|
label: 'aix'
|
||||||
|
}, {
|
||||||
|
value: 'caise-storage_volume1',
|
||||||
|
label: '存储卷'
|
||||||
|
}, {
|
||||||
|
value: 'caise-virtualization',
|
||||||
|
label: '虚拟化'
|
||||||
|
}, {
|
||||||
|
value: 'caise-business',
|
||||||
|
label: '业务'
|
||||||
|
}, {
|
||||||
|
value: 'caise-database',
|
||||||
|
label: '数据库'
|
||||||
|
}, {
|
||||||
|
value: 'caise-middleware',
|
||||||
|
label: '中间件'
|
||||||
|
}, {
|
||||||
|
value: 'caise-websever',
|
||||||
|
label: 'websever'
|
||||||
|
}, {
|
||||||
|
value: 'caise-message_queue',
|
||||||
|
label: '消息队列'
|
||||||
|
}, {
|
||||||
|
value: 'caise-load_balancing',
|
||||||
|
label: '负载均衡'
|
||||||
|
}, {
|
||||||
|
value: 'caise-storage_device',
|
||||||
|
label: '存储设备'
|
||||||
|
}, {
|
||||||
|
value: 'caise-network_devices',
|
||||||
|
label: '网络设备'
|
||||||
|
}, {
|
||||||
|
value: 'caise-computer',
|
||||||
|
label: '计算机'
|
||||||
|
}, {
|
||||||
|
value: 'caise-hardware',
|
||||||
|
label: '硬件设备'
|
||||||
|
}, {
|
||||||
|
value: 'caise-data_center2',
|
||||||
|
label: '数据中心'
|
||||||
|
}, {
|
||||||
|
value: 'caise-hyperV',
|
||||||
|
label: 'hyperV'
|
||||||
|
}, {
|
||||||
|
value: 'caise-IPAM',
|
||||||
|
label: 'IPAM'
|
||||||
|
}, {
|
||||||
|
value: 'caise-system',
|
||||||
|
label: '操作系统'
|
||||||
|
}, {
|
||||||
|
value: 'caise-public_cloud',
|
||||||
|
label: '公有云'
|
||||||
|
}, {
|
||||||
value: 'caise-data_center',
|
value: 'caise-data_center',
|
||||||
label: '数据中心'
|
label: '数据中心'
|
||||||
}, {
|
}, {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 177 KiB |
Binary file not shown.
After Width: | Height: | Size: 444 KiB |
|
@ -0,0 +1,85 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="icon || title"
|
||||||
|
class="ci-icon"
|
||||||
|
:style="{
|
||||||
|
'--size': size + 'px'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template v-if="icon">
|
||||||
|
<img
|
||||||
|
v-if="icon.split('$$')[2]"
|
||||||
|
:src="`/api/common-setting/v1/file/${icon.split('$$')[3]}`"
|
||||||
|
/>
|
||||||
|
<ops-icon
|
||||||
|
v-else
|
||||||
|
:style="{
|
||||||
|
color: icon.split('$$')[1],
|
||||||
|
}"
|
||||||
|
:type="icon.split('$$')[0]"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
class="ci-icon-letter"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{ title[0].toUpperCase() }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'CIIcon',
|
||||||
|
props: {
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 如果没有icon, 默认以title 的第一个字符
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: '12'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.ci-icon {
|
||||||
|
font-size: var(--size);
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > img {
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-letter {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
color: #2f54eb;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0px 1px 2px rgba(47, 84, 235, 0.2);
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
transform-origin: center;
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -179,7 +179,7 @@
|
||||||
<a @click="openDetail(row.ci_id || row._id)">
|
<a @click="openDetail(row.ci_id || row._id)">
|
||||||
<a-icon type="unordered-list" />
|
<a-icon type="unordered-list" />
|
||||||
</a>
|
</a>
|
||||||
<a-tooltip :title="$t('cmdb.ci.addRelation')">
|
<a-tooltip :title="$t('cmdb.ci.viewRelation')">
|
||||||
<a @click="openDetail(row.ci_id || row._id, 'tab_2', '2')">
|
<a @click="openDetail(row.ci_id || row._id, 'tab_2', '2')">
|
||||||
<a-icon type="retweet" />
|
<a-icon type="retweet" />
|
||||||
</a>
|
</a>
|
||||||
|
@ -375,7 +375,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
jsonEditorOk(row, column, jsonData) {
|
jsonEditorOk(row, column, jsonData) {
|
||||||
this.$attrs.data.forEach((item) => {
|
this.data.forEach((item) => {
|
||||||
if (item._id === row._id) {
|
if (item._id === row._id) {
|
||||||
item[column.property] = JSON.stringify(jsonData)
|
item[column.property] = JSON.stringify(jsonData)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import i18n from '@/lang'
|
||||||
|
|
||||||
|
export const ruleTypeList = () => {
|
||||||
|
return [
|
||||||
|
{ value: 'and', label: i18n.t('cmdbFilterComp.and') },
|
||||||
|
{ value: 'or', label: i18n.t('cmdbFilterComp.or') },
|
||||||
|
// { value: 'not', label: '非' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const expList = () => {
|
||||||
|
return [
|
||||||
|
{ value: 'is', label: i18n.t('cmdbFilterComp.is') },
|
||||||
|
{ value: '~is', label: i18n.t('cmdbFilterComp.~is') },
|
||||||
|
{ value: 'contain', label: i18n.t('cmdbFilterComp.contain') },
|
||||||
|
{ value: '~contain', label: i18n.t('cmdbFilterComp.~contain') },
|
||||||
|
{ value: 'start_with', label: i18n.t('cmdbFilterComp.start_with') },
|
||||||
|
{ value: '~start_with', label: i18n.t('cmdbFilterComp.~start_with') },
|
||||||
|
{ value: 'end_with', label: i18n.t('cmdbFilterComp.end_with') },
|
||||||
|
{ value: '~end_with', label: i18n.t('cmdbFilterComp.~end_with') },
|
||||||
|
{ value: '~value', label: i18n.t('cmdbFilterComp.~value') }, // 为空的定义有点绕
|
||||||
|
{ value: 'value', label: i18n.t('cmdbFilterComp.value') },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const advancedExpList = () => {
|
||||||
|
return [
|
||||||
|
{ value: 'in', label: i18n.t('cmdbFilterComp.in') },
|
||||||
|
{ value: '~in', label: i18n.t('cmdbFilterComp.~in') },
|
||||||
|
{ value: 'range', label: i18n.t('cmdbFilterComp.range') },
|
||||||
|
{ value: '~range', label: i18n.t('cmdbFilterComp.~range') },
|
||||||
|
{ value: 'compare', label: i18n.t('cmdbFilterComp.compare') },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const compareTypeList = [
|
||||||
|
{ value: '1', label: '>' },
|
||||||
|
{ value: '2', label: '>=' },
|
||||||
|
{ value: '3', label: '<' },
|
||||||
|
{ value: '4', label: '<=' },
|
||||||
|
]
|
|
@ -0,0 +1,320 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{ lineHeight: rowHeight }">
|
||||||
|
<a-space :style="{ display: 'flex', marginBottom: '10px' }" v-for="(item, index) in ruleList" :key="item.id">
|
||||||
|
<div v-if="ruleList.length > 1" :style="{ width: '60px', height: rowHeight, position: 'relative' }">
|
||||||
|
<treeselect
|
||||||
|
v-if="index !== 0"
|
||||||
|
class="custom-treeselect"
|
||||||
|
:style="{ width: '60px', '--custom-height': rowHeight, position: 'absolute', top: '-24px' }"
|
||||||
|
v-model="item.type"
|
||||||
|
:multiple="false"
|
||||||
|
:clearable="false"
|
||||||
|
searchable
|
||||||
|
:options="ruleTypeList"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.value,
|
||||||
|
label: node.label,
|
||||||
|
children: node.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
</treeselect>
|
||||||
|
</div>
|
||||||
|
<treeselect
|
||||||
|
class="custom-treeselect"
|
||||||
|
:style="{ width: '120px', '--custom-height': rowHeight }"
|
||||||
|
v-model="item.property"
|
||||||
|
:multiple="false"
|
||||||
|
:clearable="false"
|
||||||
|
searchable
|
||||||
|
:options="canSearchPreferenceAttrList"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.name,
|
||||||
|
label: node.alias || node.name,
|
||||||
|
children: node.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
appendToBody
|
||||||
|
:zIndex="1050"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="node.id !== '$count'"
|
||||||
|
:title="node.label"
|
||||||
|
slot="option-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
class="property-label"
|
||||||
|
>
|
||||||
|
<ValueTypeMapIcon :attr="node.raw" />
|
||||||
|
{{ node.label }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:title="node.label"
|
||||||
|
slot="option-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
class="property-label"
|
||||||
|
:style="{ borderBottom: '1px solid #E4E7ED', marginBottom: '8px' }"
|
||||||
|
>
|
||||||
|
<ValueTypeMapIcon :attr="node.raw" />
|
||||||
|
{{ node.label }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="property-label"
|
||||||
|
slot="value-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
>
|
||||||
|
<ValueTypeMapIcon :attr="node.raw" /> {{ node.label }}
|
||||||
|
</div>
|
||||||
|
</treeselect>
|
||||||
|
<treeselect
|
||||||
|
class="custom-treeselect"
|
||||||
|
:style="{ width: '90px', '--custom-height': rowHeight }"
|
||||||
|
v-model="item.exp"
|
||||||
|
:multiple="false"
|
||||||
|
:clearable="false"
|
||||||
|
searchable
|
||||||
|
:options="getExpListByProperty(item.property)"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.value,
|
||||||
|
label: node.label,
|
||||||
|
children: node.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
appendToBody
|
||||||
|
@select="(value) => handleChangeExp(value, item, index)"
|
||||||
|
:zIndex="1050"
|
||||||
|
:disabled="disabled"
|
||||||
|
>
|
||||||
|
</treeselect>
|
||||||
|
<ValueControls
|
||||||
|
:rule="ruleList[index]"
|
||||||
|
:attrList="canSearchPreferenceAttrList"
|
||||||
|
:disabled="disabled"
|
||||||
|
:curModelAttrList="curModelAttrList"
|
||||||
|
:rowHeight="rowHeight"
|
||||||
|
@change="(value) => handleChangeValue(value, index)"
|
||||||
|
/>
|
||||||
|
<template v-if="!disabled">
|
||||||
|
<a-tooltip :title="$t('copy')">
|
||||||
|
<a class="operation" @click="handleCopyRule(item)"><ops-icon type="veops-copy"/></a>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip :title="$t('delete')">
|
||||||
|
<a class="operation" @click="handleDeleteRule(item)"><a-icon type="minus-circle"/></a>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip :title="$t('cmdbFilterComp.addHere')">
|
||||||
|
<a class="operation" @click="handleAddRuleAt(item)"><a-icon type="plus-circle"/></a>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
</a-space>
|
||||||
|
<div class="table-filter-add" v-if="!disabled && ruleList.length === 0">
|
||||||
|
<a @click="handleAddRule">+ {{ $t('new') }}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { ruleTypeList, expList, advancedExpList, compareTypeList } from './constants.js'
|
||||||
|
import ValueTypeMapIcon from '@/components/CMDBValueTypeMapIcon'
|
||||||
|
import ValueControls from './valueControls.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Expression',
|
||||||
|
components: {
|
||||||
|
ValueTypeMapIcon,
|
||||||
|
ValueControls
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'change',
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
canSearchPreferenceAttrList: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
curModelAttrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
compareTypeList,
|
||||||
|
rowHeight: '36px',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
ruleList: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('change', val)
|
||||||
|
return val
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ruleTypeList() {
|
||||||
|
return ruleTypeList()
|
||||||
|
},
|
||||||
|
expList() {
|
||||||
|
return expList()
|
||||||
|
},
|
||||||
|
advancedExpList() {
|
||||||
|
return advancedExpList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getExpListByProperty(property) {
|
||||||
|
if (property === '$count') {
|
||||||
|
return [
|
||||||
|
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
|
||||||
|
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
|
||||||
|
{ value: 'compare', label: this.$t('cmdbFilterComp.compare') }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
if (property) {
|
||||||
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
|
if (_find && ['0', '1', '3', '4', '5'].includes(_find.value_type)) {
|
||||||
|
return [
|
||||||
|
{ value: 'is', label: this.$t('cmdbFilterComp.is') },
|
||||||
|
{ value: '~is', label: this.$t('cmdbFilterComp.~is') },
|
||||||
|
{ value: '~value', label: this.$t('cmdbFilterComp.~value') }, // 为空的定义有点绕
|
||||||
|
{ value: 'value', label: this.$t('cmdbFilterComp.value') },
|
||||||
|
...this.advancedExpList
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...this.expList, ...this.advancedExpList]
|
||||||
|
},
|
||||||
|
isChoiceByProperty(property) {
|
||||||
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
|
if (_find) {
|
||||||
|
return _find.is_choice
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
handleAddRule() {
|
||||||
|
this.ruleList.push({
|
||||||
|
id: uuidv4(),
|
||||||
|
type: 'and',
|
||||||
|
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||||
|
exp: 'is',
|
||||||
|
value: null,
|
||||||
|
})
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
handleCopyRule(item) {
|
||||||
|
this.ruleList.push({ ...item, id: uuidv4() })
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
handleDeleteRule(item) {
|
||||||
|
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||||
|
if (idx > -1) {
|
||||||
|
this.ruleList.splice(idx, 1)
|
||||||
|
}
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
handleAddRuleAt(item) {
|
||||||
|
const idx = this.ruleList.findIndex((r) => r.id === item.id)
|
||||||
|
if (idx > -1) {
|
||||||
|
this.ruleList.splice(idx + 1, 0, {
|
||||||
|
id: uuidv4(),
|
||||||
|
type: 'and',
|
||||||
|
property: this.canSearchPreferenceAttrList[0]?.name,
|
||||||
|
exp: 'is',
|
||||||
|
value: null,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
getChoiceValueByProperty(property) {
|
||||||
|
const _find = this.canSearchPreferenceAttrList.find((item) => item.name === property)
|
||||||
|
if (_find) {
|
||||||
|
return _find.choice_value
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
handleChangeExp({ value }, item, index) {
|
||||||
|
const _ruleList = _.cloneDeep(this.ruleList)
|
||||||
|
if (value === 'range') {
|
||||||
|
_ruleList[index] = {
|
||||||
|
..._ruleList[index],
|
||||||
|
min: '',
|
||||||
|
max: '',
|
||||||
|
exp: value,
|
||||||
|
}
|
||||||
|
} else if (value === 'compare') {
|
||||||
|
_ruleList[index] = {
|
||||||
|
..._ruleList[index],
|
||||||
|
compareType: '1',
|
||||||
|
exp: value,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ruleList[index] = {
|
||||||
|
..._ruleList[index],
|
||||||
|
exp: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.ruleList = _ruleList
|
||||||
|
this.$emit('change', this.ruleList)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChangeValue(value, index) {
|
||||||
|
const _ruleList = _.cloneDeep(this.ruleList)
|
||||||
|
_ruleList[index] = value
|
||||||
|
this.$emit('change', _ruleList)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 150px;
|
||||||
|
|
||||||
|
&-range-icon {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.property-label {
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
.attr-filter-tip {
|
||||||
|
color: #86909C;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,211 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Expression
|
||||||
|
v-model="ruleList"
|
||||||
|
:canSearchPreferenceAttrList="canSearchPreferenceAttrList.filter((attr) => !attr.is_password)"
|
||||||
|
:disabled="false"
|
||||||
|
:curModelAttrList="curModelAttrList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { compareTypeList } from './constants.js'
|
||||||
|
|
||||||
|
import Expression from './expression.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AttrFilter',
|
||||||
|
components: {
|
||||||
|
Expression
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
canSearchPreferenceAttrList: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
regQ: {
|
||||||
|
type: String,
|
||||||
|
default: '(?<=q=).+(?=&)|(?<=q=).+$',
|
||||||
|
},
|
||||||
|
CITypeIds: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
curModelAttrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
compareTypeList,
|
||||||
|
visible: false,
|
||||||
|
ruleList: [],
|
||||||
|
filterExp: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
init(open, isInitOne = true) {
|
||||||
|
// isInitOne 初始化exp为空时,ruleList是否默认给一条
|
||||||
|
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||||
|
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
|
||||||
|
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
|
||||||
|
: null
|
||||||
|
if (open && exp) {
|
||||||
|
const expArray = exp.split(',').map((item) => {
|
||||||
|
let has_not = ''
|
||||||
|
const key = item.split(':')[0]
|
||||||
|
const val = item
|
||||||
|
.split(':')
|
||||||
|
.slice(1)
|
||||||
|
.join(':')
|
||||||
|
let type, property, exp, value, min, max, compareType
|
||||||
|
if (key.includes('-')) {
|
||||||
|
type = 'or'
|
||||||
|
if (key.includes('~')) {
|
||||||
|
property = key.substring(2)
|
||||||
|
has_not = '~'
|
||||||
|
} else {
|
||||||
|
property = key.substring(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
type = 'and'
|
||||||
|
if (key.includes('~')) {
|
||||||
|
property = key.substring(1)
|
||||||
|
has_not = '~'
|
||||||
|
} else {
|
||||||
|
property = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const in_reg = /(?<=\().+(?=\))/g
|
||||||
|
const range_reg = /(?<=\[).+(?=\])/g
|
||||||
|
const compare_reg = /(?<=>=|<=|>(?!=)|<(?!=)).+/
|
||||||
|
if (val === '*') {
|
||||||
|
exp = has_not + 'value'
|
||||||
|
value = ''
|
||||||
|
} else if (in_reg.test(val)) {
|
||||||
|
exp = has_not + 'in'
|
||||||
|
value = val.match(in_reg)[0]
|
||||||
|
} else if (range_reg.test(val)) {
|
||||||
|
exp = has_not + 'range'
|
||||||
|
value = val.match(range_reg)[0]
|
||||||
|
min = value.split('_TO_')[0]
|
||||||
|
max = value.split('_TO_')[1]
|
||||||
|
} else if (compare_reg.test(val)) {
|
||||||
|
exp = has_not + 'compare'
|
||||||
|
value = val.match(compare_reg)[0]
|
||||||
|
const _compareType = val.substring(0, val.match(compare_reg)['index'])
|
||||||
|
const idx = compareTypeList.findIndex((item) => item.label === _compareType)
|
||||||
|
compareType = compareTypeList[idx].value
|
||||||
|
} else if (!val.includes('*')) {
|
||||||
|
exp = has_not + 'is'
|
||||||
|
value = val
|
||||||
|
} else {
|
||||||
|
const resList = [
|
||||||
|
['contain', /(?<=\*).*(?=\*)/g],
|
||||||
|
['end_with', /(?<=\*).+/g],
|
||||||
|
['start_with', /.+(?=\*)/g],
|
||||||
|
]
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const reg = resList[i]
|
||||||
|
if (reg[1].test(val)) {
|
||||||
|
exp = has_not + reg[0]
|
||||||
|
value = val.match(reg[1])[0]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: uuidv4(),
|
||||||
|
type,
|
||||||
|
property,
|
||||||
|
exp,
|
||||||
|
value,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
compareType,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.ruleList = [...expArray]
|
||||||
|
} else if (open) {
|
||||||
|
const _canSearchPreferenceAttrList = this.canSearchPreferenceAttrList.filter((attr) => !attr.is_password)
|
||||||
|
this.ruleList = isInitOne
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id: uuidv4(),
|
||||||
|
type: 'and',
|
||||||
|
property:
|
||||||
|
_canSearchPreferenceAttrList && _canSearchPreferenceAttrList.length
|
||||||
|
? _canSearchPreferenceAttrList[0].name
|
||||||
|
: undefined,
|
||||||
|
exp: 'is',
|
||||||
|
value: null,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
if (this.ruleList && this.ruleList.length) {
|
||||||
|
this.ruleList[0].type = 'and' // 增删后,以防万一第一个不是and
|
||||||
|
this.filterExp = ''
|
||||||
|
const expList = this.ruleList.filter((rule) => rule?.property).map((rule) => {
|
||||||
|
let singleRuleExp = ''
|
||||||
|
let _exp = rule.exp
|
||||||
|
if (rule.type === 'or') {
|
||||||
|
singleRuleExp += '-'
|
||||||
|
}
|
||||||
|
if (rule.exp.includes('~')) {
|
||||||
|
singleRuleExp += '~'
|
||||||
|
_exp = rule.exp.split('~')[1]
|
||||||
|
}
|
||||||
|
singleRuleExp += `${rule.property}:`
|
||||||
|
if (_exp === 'is') {
|
||||||
|
singleRuleExp += `${rule.value ?? ''}`
|
||||||
|
}
|
||||||
|
if (_exp === 'contain') {
|
||||||
|
singleRuleExp += `*${rule.value ?? ''}*`
|
||||||
|
}
|
||||||
|
if (_exp === 'start_with') {
|
||||||
|
singleRuleExp += `${rule.value ?? ''}*`
|
||||||
|
}
|
||||||
|
if (_exp === 'end_with') {
|
||||||
|
singleRuleExp += `*${rule.value ?? ''}`
|
||||||
|
}
|
||||||
|
if (_exp === 'value') {
|
||||||
|
singleRuleExp += `*`
|
||||||
|
}
|
||||||
|
if (_exp === 'in') {
|
||||||
|
singleRuleExp += `(${rule.value ?? ''})`
|
||||||
|
}
|
||||||
|
if (_exp === 'range') {
|
||||||
|
singleRuleExp += `[${rule.min}_TO_${rule.max}]`
|
||||||
|
}
|
||||||
|
if (_exp === 'compare') {
|
||||||
|
const idx = compareTypeList.findIndex((item) => item.value === rule.compareType)
|
||||||
|
singleRuleExp += `${compareTypeList[idx].label}${rule.value ?? ''}`
|
||||||
|
}
|
||||||
|
return singleRuleExp
|
||||||
|
})
|
||||||
|
this.filterExp = expList.join(',')
|
||||||
|
this.$emit('setExpFromFilter', this.filterExp)
|
||||||
|
} else {
|
||||||
|
this.$emit('setExpFromFilter', '')
|
||||||
|
}
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,267 @@
|
||||||
|
<template>
|
||||||
|
<div class="control-group">
|
||||||
|
<CIReferenceAttr
|
||||||
|
v-if="getAttr(rule.property).is_reference && (rule.exp === 'is' || rule.exp === '~is')"
|
||||||
|
class="select-filter"
|
||||||
|
:referenceTypeId="getAttr(rule.property).reference_type_id"
|
||||||
|
:value="rule.value"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="(value) => handleChange('value', value)"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
v-else-if="getAttr(rule.property).is_bool && (rule.exp === 'is' || rule.exp === '~is')"
|
||||||
|
class="select-filter"
|
||||||
|
:disabled="disabled"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:value="rule.value"
|
||||||
|
@change="(value) => handleChange('value', value)"
|
||||||
|
>
|
||||||
|
<a-select-option key="1">
|
||||||
|
true
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option key="0">
|
||||||
|
false
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<div
|
||||||
|
class="input-group"
|
||||||
|
v-else-if="isChoiceByProperty(rule.property) && (rule.exp === 'is' || rule.exp === '~is')"
|
||||||
|
>
|
||||||
|
<CIBuiltInAttr
|
||||||
|
v-if="getAttr(rule.property).choice_builtin"
|
||||||
|
:choice_builtin="getAttr(rule.property).choice_builtin"
|
||||||
|
class="select-filter"
|
||||||
|
:treeselectCustomStyle="{ width: '136px', '--custom-height': '36px' }"
|
||||||
|
:value="rule.value"
|
||||||
|
@change="(value) => handleChange('value', value)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a-select
|
||||||
|
v-else
|
||||||
|
class="select-filter"
|
||||||
|
:style="{ width: '175px' }"
|
||||||
|
showSearch
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="rule.value"
|
||||||
|
@change="(value) => handleChange('value', value)"
|
||||||
|
>
|
||||||
|
<a-select-option
|
||||||
|
v-for="(node) in getChoiceValueByProperty(rule.property)"
|
||||||
|
:key="String(node[0])"
|
||||||
|
:title="node[1] ? node[1].label || node[0] : node[0]"
|
||||||
|
>
|
||||||
|
<a-tooltip placement="topLeft" :title="node[1] ? node[1].label || node[0] : node[0]" >
|
||||||
|
{{ node[1] ? node[1].label || node[0] : node[0] }}
|
||||||
|
</a-tooltip>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<!-- <treeselect
|
||||||
|
class="custom-treeselect"
|
||||||
|
:style="{ '--custom-height': rowHeight }"
|
||||||
|
:value="rule.value"
|
||||||
|
@input="(value) => handleChange('value', value)"
|
||||||
|
:multiple="false"
|
||||||
|
:clearable="false"
|
||||||
|
searchable
|
||||||
|
:options="getChoiceValueByProperty(rule.property)"
|
||||||
|
:placeholder="$t('placeholder2')"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node[0],
|
||||||
|
label: node[1] ? node[1].label || node[0] : node[0],
|
||||||
|
children: node.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:zIndex="1050"
|
||||||
|
:disabled="disabled"
|
||||||
|
appendToBody
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:title="node.label"
|
||||||
|
slot="option-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
|
>
|
||||||
|
{{ node.label }}
|
||||||
|
</div>
|
||||||
|
</treeselect> -->
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
compact
|
||||||
|
v-else-if="rule.exp === 'range' || rule.exp === '~range'"
|
||||||
|
class="input-group"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
class="ops-input"
|
||||||
|
:placeholder="$t('min')"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="rule.min"
|
||||||
|
@change="(e) => handleChange('min', e.target.value)"
|
||||||
|
/>
|
||||||
|
<span class="input-group-range-icon">~</span>
|
||||||
|
<a-input
|
||||||
|
class="ops-input"
|
||||||
|
v-model="rule.max"
|
||||||
|
:placeholder="$t('max')"
|
||||||
|
:disabled="disabled"
|
||||||
|
:value="rule.max"
|
||||||
|
@change="(e) => handleChange('max', e.target.value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group" compact v-else-if="rule.exp === 'compare'">
|
||||||
|
<treeselect
|
||||||
|
class="custom-treeselect"
|
||||||
|
:style="{ width: '70px', '--custom-height': rowHeight, 'flex-shrink': 0 }"
|
||||||
|
:value="rule.compareType"
|
||||||
|
@input="(value) => handleChange('compareType', value)"
|
||||||
|
:multiple="false"
|
||||||
|
:clearable="false"
|
||||||
|
searchable
|
||||||
|
:options="compareTypeList"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.value,
|
||||||
|
label: node.label,
|
||||||
|
children: node.children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:zIndex="1050"
|
||||||
|
:disabled="disabled"
|
||||||
|
appendToBody
|
||||||
|
>
|
||||||
|
</treeselect>
|
||||||
|
<a-input :value="rule.value" @change="(e) => handleChange('value', e.target.value)" class="ops-input"/>
|
||||||
|
</div>
|
||||||
|
<div class="input-group" v-else-if="rule.exp !== 'value' && rule.exp !== '~value'">
|
||||||
|
<a-input
|
||||||
|
:value="rule.value"
|
||||||
|
@change="(e) => handleChange('value', e.target.value)"
|
||||||
|
:placeholder="rule.exp === 'in' || rule.exp === '~in' ? $t('cmdbFilterComp.split', { separator: ';' }) : ''"
|
||||||
|
class="ops-input"
|
||||||
|
:disabled="disabled"
|
||||||
|
></a-input>
|
||||||
|
</div>
|
||||||
|
<div v-else :style="{ width: '136px' }"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { compareTypeList } from './constants.js'
|
||||||
|
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
|
||||||
|
import CIBuiltInAttr from '@/components/ciBuiltInAttr/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ValueControls',
|
||||||
|
components: {
|
||||||
|
CIReferenceAttr,
|
||||||
|
CIBuiltInAttr
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
rule: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
attrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 当前模型属性
|
||||||
|
curModelAttrList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 行高
|
||||||
|
rowHeight: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
compareTypeList,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
choiceValue() {
|
||||||
|
const val = /\{\{([^}]+)\}\}/g.exec(this?.rule?.value || '')
|
||||||
|
return val ? val?.[1]?.trim() || '' : this?.value?.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isChoiceByProperty(property) {
|
||||||
|
const _find = this.attrList.find((item) => item.name === property)
|
||||||
|
if (_find) {
|
||||||
|
return _find.is_choice
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
getChoiceValueByProperty(property) {
|
||||||
|
const _find = this.attrList.find((item) => item.name === property)
|
||||||
|
if (_find) {
|
||||||
|
return _find.choice_value
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
handleChange(key, value) {
|
||||||
|
this.$emit('change', {
|
||||||
|
...this.rule,
|
||||||
|
[key]: value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getAttr(property) {
|
||||||
|
return this.attrList.find((item) => item.name === property) || {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.control-group {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 136px;
|
||||||
|
|
||||||
|
&-range-icon {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-filter {
|
||||||
|
height: 36px;
|
||||||
|
width: 136px;
|
||||||
|
|
||||||
|
/deep/ .ant-select-selection {
|
||||||
|
height: 36px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
line-height: 36px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
.ant-select-selection__rendered {
|
||||||
|
height: 36px;
|
||||||
|
line-height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .vue-treeselect__control {
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -307,6 +307,18 @@ const cmdb_en = {
|
||||||
filterUsers: 'Filter Users',
|
filterUsers: 'Filter Users',
|
||||||
enum: 'Enum',
|
enum: 'Enum',
|
||||||
ciGrantTip: `Filter conditions can be changed dynamically using {{}} referenced variables, currently user variables are supported, such as {{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
|
ciGrantTip: `Filter conditions can be changed dynamically using {{}} referenced variables, currently user variables are supported, such as {{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
|
||||||
|
searchInputTip: 'Please search for resource keywords',
|
||||||
|
resourceSearch: 'Resource Search',
|
||||||
|
recentSearch: 'Recent Search',
|
||||||
|
myCollection: 'My Collection',
|
||||||
|
keyword: 'Keywords',
|
||||||
|
CIType: 'CIType',
|
||||||
|
filterPopoverLabel: 'Filter',
|
||||||
|
conditionFilter: 'Condition Filter',
|
||||||
|
advancedFilter: 'Advanced Filter',
|
||||||
|
saveCondition: 'Save Condition',
|
||||||
|
confirmClear: 'Confirm to clear?',
|
||||||
|
currentPage: 'Current Page'
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
unselectAttributes: 'Unselected',
|
unselectAttributes: 'Unselected',
|
||||||
|
@ -629,6 +641,7 @@ if __name__ == "__main__":
|
||||||
attributeDesc: 'Attribute Description',
|
attributeDesc: 'Attribute Description',
|
||||||
selectRows: 'Select: {rows} items',
|
selectRows: 'Select: {rows} items',
|
||||||
addRelation: 'Add Relation',
|
addRelation: 'Add Relation',
|
||||||
|
viewRelation: 'View Relation',
|
||||||
all: 'All',
|
all: 'All',
|
||||||
batchUpdate: 'Batch Update',
|
batchUpdate: 'Batch Update',
|
||||||
batchUpdateConfirm: 'Are you sure you want to make batch updates?',
|
batchUpdateConfirm: 'Are you sure you want to make batch updates?',
|
||||||
|
|
|
@ -307,6 +307,18 @@ const cmdb_zh = {
|
||||||
filterUsers: '筛选用户',
|
filterUsers: '筛选用户',
|
||||||
enum: '枚举',
|
enum: '枚举',
|
||||||
ciGrantTip: `筛选条件可使用{{}}引用变量实现动态变化,目前支持用户变量,如{{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
|
ciGrantTip: `筛选条件可使用{{}}引用变量实现动态变化,目前支持用户变量,如{{user.uid}},{{user.username}},{{user.email}},{{user.nickname}}`,
|
||||||
|
searchInputTip: '请搜索资源关键字',
|
||||||
|
resourceSearch: '资源搜索',
|
||||||
|
recentSearch: '最近搜索',
|
||||||
|
myCollection: '我的收藏',
|
||||||
|
keyword: '关键字',
|
||||||
|
CIType: '模型',
|
||||||
|
filterPopoverLabel: '条件过滤',
|
||||||
|
conditionFilter: '条件过滤',
|
||||||
|
advancedFilter: '高级筛选',
|
||||||
|
saveCondition: '保存条件',
|
||||||
|
confirmClear: '确认清空?',
|
||||||
|
currentPage: '当前页'
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
unselectAttributes: '未选属性',
|
unselectAttributes: '未选属性',
|
||||||
|
@ -628,6 +640,7 @@ if __name__ == "__main__":
|
||||||
attributeDesc: '查看属性配置',
|
attributeDesc: '查看属性配置',
|
||||||
selectRows: '选取:{rows} 项',
|
selectRows: '选取:{rows} 项',
|
||||||
addRelation: '添加关系',
|
addRelation: '添加关系',
|
||||||
|
viewRelation: '查看关系',
|
||||||
all: '全部',
|
all: '全部',
|
||||||
batchUpdate: '批量修改',
|
batchUpdate: '批量修改',
|
||||||
batchUpdateConfirm: '确认要批量修改吗?',
|
batchUpdateConfirm: '确认要批量修改吗?',
|
||||||
|
|
|
@ -54,7 +54,7 @@ const genCmdbRoutes = async () => {
|
||||||
path: '/cmdb/resourcesearch',
|
path: '/cmdb/resourcesearch',
|
||||||
name: 'cmdb_resource_search',
|
name: 'cmdb_resource_search',
|
||||||
meta: { title: 'cmdb.menu.ciSearch', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search', keepAlive: false },
|
meta: { title: 'cmdb.menu.ciSearch', icon: 'ops-cmdb-search', selectedIcon: 'ops-cmdb-search', keepAlive: false },
|
||||||
component: () => import('../views/resource_search/index.vue')
|
component: () => import('../views/resource_search_2/index.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/adc',
|
path: '/cmdb/adc',
|
||||||
|
@ -117,11 +117,10 @@ const genCmdbRoutes = async () => {
|
||||||
meta: { title: 'cmdb.menu.serviceTreeDefine', keepAlive: false, icon: 'ops-cmdb-preferencerelation', selectedIcon: 'ops-cmdb-preferencerelation-selected' }
|
meta: { title: 'cmdb.menu.serviceTreeDefine', keepAlive: false, icon: 'ops-cmdb-preferencerelation', selectedIcon: 'ops-cmdb-preferencerelation-selected' }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/modelrelation',
|
path: '/cmdb/discovery',
|
||||||
name: 'model_relation',
|
name: 'discovery',
|
||||||
hideChildrenInMenu: true,
|
component: () => import('../views/discovery/index'),
|
||||||
component: () => import('../views/model_relation/index'),
|
meta: { title: 'cmdb.menu.ad', keepAlive: false, icon: 'ops-cmdb-adr', selectedIcon: 'ops-cmdb-adr-selected' }
|
||||||
meta: { title: 'cmdb.menu.citypeRelation', keepAlive: false, icon: 'ops-cmdb-modelrelation', selectedIcon: 'ops-cmdb-modelrelation-selected' }
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/operationhistory',
|
path: '/cmdb/operationhistory',
|
||||||
|
@ -130,19 +129,20 @@ const genCmdbRoutes = async () => {
|
||||||
component: () => import('../views/operation_history/index'),
|
component: () => import('../views/operation_history/index'),
|
||||||
meta: { title: 'cmdb.menu.operationHistory', keepAlive: false, icon: 'ops-cmdb-operation', selectedIcon: 'ops-cmdb-operation-selected' }
|
meta: { title: 'cmdb.menu.operationHistory', keepAlive: false, icon: 'ops-cmdb-operation', selectedIcon: 'ops-cmdb-operation-selected' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/cmdb/modelrelation',
|
||||||
|
name: 'model_relation',
|
||||||
|
hideChildrenInMenu: true,
|
||||||
|
component: () => import('../views/model_relation/index'),
|
||||||
|
meta: { title: 'cmdb.menu.citypeRelation', keepAlive: false, icon: 'ops-cmdb-modelrelation', selectedIcon: 'ops-cmdb-modelrelation-selected' }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/cmdb/relationtype',
|
path: '/cmdb/relationtype',
|
||||||
name: 'relation_type',
|
name: 'relation_type',
|
||||||
hideChildrenInMenu: true,
|
hideChildrenInMenu: true,
|
||||||
component: () => import('../views/relation_type/index'),
|
component: () => import('../views/relation_type/index'),
|
||||||
meta: { title: 'cmdb.menu.relationType', keepAlive: false, icon: 'ops-cmdb-relationtype', selectedIcon: 'ops-cmdb-relationtype-selected' }
|
meta: { title: 'cmdb.menu.relationType', keepAlive: false, icon: 'ops-cmdb-relationtype', selectedIcon: 'ops-cmdb-relationtype-selected' }
|
||||||
},
|
}
|
||||||
{
|
|
||||||
path: '/cmdb/discovery',
|
|
||||||
name: 'discovery',
|
|
||||||
component: () => import('../views/discovery/index'),
|
|
||||||
meta: { title: 'cmdb.menu.ad', keepAlive: false, icon: 'ops-cmdb-adr', selectedIcon: 'ops-cmdb-adr-selected' }
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -148,7 +148,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
import JsonEditor from '../../../components/JsonEditor/jsonEditor.vue'
|
||||||
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
|
import CIReferenceAttr from '@/components/ciReferenceAttr/index.vue'
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default {
|
||||||
await this.handleLinkAttrToCiType({ attr_id: this.targetKeys.map((i) => Number(i)) })
|
await this.handleLinkAttrToCiType({ attr_id: this.targetKeys.map((i) => Number(i)) })
|
||||||
if (this.currentGroup) {
|
if (this.currentGroup) {
|
||||||
await this.updateCurrentGroup()
|
await this.updateCurrentGroup()
|
||||||
const { id, name, order, attributes } = this.currentGroup
|
const { name, order, attributes } = this.currentGroup
|
||||||
const attrIds = attributes.filter((i) => !i.inherited).map((i) => i.id)
|
const attrIds = attributes.filter((i) => !i.inherited).map((i) => i.id)
|
||||||
this.targetKeys.forEach((key) => {
|
this.targetKeys.forEach((key) => {
|
||||||
attrIds.push(Number(key))
|
attrIds.push(Number(key))
|
||||||
|
@ -141,7 +141,7 @@ export default {
|
||||||
})
|
})
|
||||||
if (this.currentGroup) {
|
if (this.currentGroup) {
|
||||||
await this.updateCurrentGroup()
|
await this.updateCurrentGroup()
|
||||||
const { id, name, order, attributes } = this.currentGroup
|
const { name, order, attributes } = this.currentGroup
|
||||||
const attrIds = attributes.filter((i) => !i.inherited).map((i) => i.id)
|
const attrIds = attributes.filter((i) => !i.inherited).map((i) => i.id)
|
||||||
attrIds.push(newAttrId)
|
attrIds.push(newAttrId)
|
||||||
await createCITypeGroupById(this.CITypeId, { name, order, attributes: attrIds })
|
await createCITypeGroupById(this.CITypeId, { name, order, attributes: attrIds })
|
||||||
|
|
|
@ -121,7 +121,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import _ from 'lodash'
|
|
||||||
import { ColorPicker } from 'element-ui'
|
import { ColorPicker } from 'element-ui'
|
||||||
import CustomIconSelect from '@/components/CustomIconSelect'
|
import CustomIconSelect from '@/components/CustomIconSelect'
|
||||||
import { defautValueColor, defaultBGColors } from '@/modules/cmdb/utils/const.js'
|
import { defautValueColor, defaultBGColors } from '@/modules/cmdb/utils/const.js'
|
||||||
|
|
|
@ -407,7 +407,7 @@ export default {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
this.type = type
|
this.type = type
|
||||||
this.item = item
|
this.item = item
|
||||||
const { category = 0, name, type_id, attr_id, level } = item
|
const { category = 0, name, type_id, level } = item
|
||||||
const chartType = (item.options || {}).chartType || 'count'
|
const chartType = (item.options || {}).chartType || 'count'
|
||||||
const fontColor = (item.options || {}).fontColor || '#ffffff'
|
const fontColor = (item.options || {}).fontColor || '#ffffff'
|
||||||
const bgColor = (item.options || {}).bgColor || ['#6ABFFE', '#5375EB']
|
const bgColor = (item.options || {}).bgColor || ['#6ABFFE', '#5375EB']
|
||||||
|
|
|
@ -131,7 +131,7 @@ import ModelRelationTable from './modules/modelRelationTable.vue'
|
||||||
import { searchResourceType } from '@/modules/acl/api/resource'
|
import { searchResourceType } from '@/modules/acl/api/resource'
|
||||||
import { getCITypeGroupsConfig } from '@/modules/cmdb/api/ciTypeGroup'
|
import { getCITypeGroupsConfig } from '@/modules/cmdb/api/ciTypeGroup'
|
||||||
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
import { createRelation, deleteRelation, getCITypeChildren, getRelationTypes } from '@/modules/cmdb/api/CITypeRelation'
|
import { createRelation, deleteRelation, getRelationTypes } from '@/modules/cmdb/api/CITypeRelation'
|
||||||
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
import { getCITypeAttributesById } from '@/modules/cmdb/api/CITypeAttr'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
|
|
@ -588,7 +588,7 @@ export default {
|
||||||
this.calcColumns()
|
this.calcColumns()
|
||||||
}
|
}
|
||||||
if (refreshType === 'refreshNumber') {
|
if (refreshType === 'refreshNumber') {
|
||||||
const promises = this.treeKeys.map((key, index) => {
|
this.treeKeys.map((key, index) => {
|
||||||
let ancestor_ids
|
let ancestor_ids
|
||||||
if (
|
if (
|
||||||
Object.keys(this.level2constraint).some(
|
Object.keys(this.level2constraint).some(
|
||||||
|
@ -1432,7 +1432,7 @@ export default {
|
||||||
content: (h) => <div>{that.$t('confirmDelete')}</div>,
|
content: (h) => <div>{that.$t('confirmDelete')}</div>,
|
||||||
async onOk() {
|
async onOk() {
|
||||||
for (let i = 0; i < that.batchTreeKey.length; i++) {
|
for (let i = 0; i < that.batchTreeKey.length; i++) {
|
||||||
const { splitTreeKey, firstCIObj, firstCIId, _tempTree, ancestor_ids } = that.calculateParamsFromTreeKey(
|
const { splitTreeKey, _tempTree, ancestor_ids } = that.calculateParamsFromTreeKey(
|
||||||
that.batchTreeKey[i],
|
that.batchTreeKey[i],
|
||||||
'delete'
|
'delete'
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,105 +1,105 @@
|
||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
width="600px"
|
width="600px"
|
||||||
:bodyStyle="{
|
:bodyStyle="{
|
||||||
paddingTop: 0,
|
paddingTop: 0,
|
||||||
}"
|
}"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
:title="$t('view')"
|
:title="$t('view')"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="readCIIdFilterPermissions && readCIIdFilterPermissions.length">
|
<template v-if="readCIIdFilterPermissions && readCIIdFilterPermissions.length">
|
||||||
<p>
|
<p>
|
||||||
<strong>{{ $t('cmdb.serviceTree.idAuthorizationPolicy') }}</strong>
|
<strong>{{ $t('cmdb.serviceTree.idAuthorizationPolicy') }}</strong>
|
||||||
<a
|
<a
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
showAllReadCIIdFilterPermissions = !showAllReadCIIdFilterPermissions
|
showAllReadCIIdFilterPermissions = !showAllReadCIIdFilterPermissions
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
v-if="readCIIdFilterPermissions.length > 10"
|
v-if="readCIIdFilterPermissions.length > 10"
|
||||||
><a-icon
|
><a-icon
|
||||||
:type="showAllReadCIIdFilterPermissions ? 'caret-down' : 'caret-up'"
|
:type="showAllReadCIIdFilterPermissions ? 'caret-down' : 'caret-up'"
|
||||||
/></a>
|
/></a>
|
||||||
</p>
|
</p>
|
||||||
<a-tag
|
<a-tag
|
||||||
v-for="item in showAllReadCIIdFilterPermissions
|
v-for="item in showAllReadCIIdFilterPermissions
|
||||||
? readCIIdFilterPermissions
|
? readCIIdFilterPermissions
|
||||||
: readCIIdFilterPermissions.slice(0, 10)"
|
: readCIIdFilterPermissions.slice(0, 10)"
|
||||||
:key="item.name"
|
:key="item.name"
|
||||||
color="blue"
|
color="blue"
|
||||||
:style="{ marginBottom: '5px' }"
|
:style="{ marginBottom: '5px' }"
|
||||||
>{{ item.name }}</a-tag
|
>{{ item.name }}</a-tag
|
||||||
>
|
>
|
||||||
<a-tag
|
<a-tag
|
||||||
:style="{ marginBottom: '5px' }"
|
:style="{ marginBottom: '5px' }"
|
||||||
v-if="readCIIdFilterPermissions.length > 10 && !showAllReadCIIdFilterPermissions"
|
v-if="readCIIdFilterPermissions.length > 10 && !showAllReadCIIdFilterPermissions"
|
||||||
>+{{ readCIIdFilterPermissions.length - 10 }}</a-tag
|
>+{{ readCIIdFilterPermissions.length - 10 }}</a-tag
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
<a-empty v-else>
|
<a-empty v-else>
|
||||||
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
<img slot="image" :src="require('@/assets/data_empty.png')" />
|
||||||
<span slot="description"> {{ $t('noData') }} </span>
|
<span slot="description"> {{ $t('noData') }} </span>
|
||||||
</a-empty>
|
</a-empty>
|
||||||
</div>
|
</div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ciTypeFilterPermissions, getCIType } from '../../../api/CIType'
|
import { ciTypeFilterPermissions } from '../../../api/CIType'
|
||||||
import FilterComp from '@/components/CMDBFilterComp'
|
import FilterComp from '@/components/CMDBFilterComp'
|
||||||
import { searchRole } from '@/modules/acl/api/role'
|
import { searchRole } from '@/modules/acl/api/role'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ReadPermissionsModal',
|
name: 'ReadPermissionsModal',
|
||||||
components: { FilterComp },
|
components: { FilterComp },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
filerPerimissions: {},
|
filerPerimissions: {},
|
||||||
readCIIdFilterPermissions: [],
|
readCIIdFilterPermissions: [],
|
||||||
canSearchPreferenceAttrList: [],
|
canSearchPreferenceAttrList: [],
|
||||||
showAllReadCIIdFilterPermissions: false,
|
showAllReadCIIdFilterPermissions: false,
|
||||||
allRoles: [],
|
allRoles: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loadRoles()
|
this.loadRoles()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadRoles() {
|
async loadRoles() {
|
||||||
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
const res = await searchRole({ page_size: 9999, app_id: 'cmdb', is_all: true })
|
||||||
this.allRoles = res.roles
|
this.allRoles = res.roles
|
||||||
},
|
},
|
||||||
async open(treeKey) {
|
async open(treeKey) {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
const _splitTreeKey = treeKey.split('@^@').filter((item) => !!item)
|
const _splitTreeKey = treeKey.split('@^@').filter((item) => !!item)
|
||||||
const _treeKey = _splitTreeKey.slice(_splitTreeKey.length - 1, _splitTreeKey.length)[0].split('%')
|
const _treeKey = _splitTreeKey.slice(_splitTreeKey.length - 1, _splitTreeKey.length)[0].split('%')
|
||||||
|
|
||||||
const typeId = _treeKey[1]
|
const typeId = _treeKey[1]
|
||||||
const _treeKeyPath = _splitTreeKey.map((item) => item.split('%')[0]).join(',')
|
const _treeKeyPath = _splitTreeKey.map((item) => item.split('%')[0]).join(',')
|
||||||
await ciTypeFilterPermissions(typeId).then((res) => {
|
await ciTypeFilterPermissions(typeId).then((res) => {
|
||||||
this.filerPerimissions = res
|
this.filerPerimissions = res
|
||||||
})
|
})
|
||||||
const readCIIdFilterPermissions = []
|
const readCIIdFilterPermissions = []
|
||||||
Object.entries(this.filerPerimissions).forEach(([k, v]) => {
|
Object.entries(this.filerPerimissions).forEach(([k, v]) => {
|
||||||
const { id_filter } = v
|
const { id_filter } = v
|
||||||
if (id_filter && Object.keys(id_filter).includes(_treeKeyPath)) {
|
if (id_filter && Object.keys(id_filter).includes(_treeKeyPath)) {
|
||||||
const _find = this.allRoles.find((item) => item.id === Number(k))
|
const _find = this.allRoles.find((item) => item.id === Number(k))
|
||||||
readCIIdFilterPermissions.push({ name: _find?.name ?? k, rid: k })
|
readCIIdFilterPermissions.push({ name: _find?.name ?? k, rid: k })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.readCIIdFilterPermissions = readCIIdFilterPermissions
|
this.readCIIdFilterPermissions = readCIIdFilterPermissions
|
||||||
console.log(readCIIdFilterPermissions)
|
console.log(readCIIdFilterPermissions)
|
||||||
},
|
},
|
||||||
handleCancel() {
|
handleCancel() {
|
||||||
this.showAllReadCIIdFilterPermissions = false
|
this.showAllReadCIIdFilterPermissions = false
|
||||||
this.visible = false
|
this.visible = false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style></style>
|
<style></style>
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
<template>
|
||||||
|
<div :class="['attr-display', isEllipsis ? 'attr-display-ellipsis' : '']">
|
||||||
|
<template v-if="attr.is_reference && ci[attr.name]" >
|
||||||
|
<a
|
||||||
|
v-for="(ciId) in (attr.is_list ? ci[attr.name] : [ci[attr.name]])"
|
||||||
|
:key="ciId"
|
||||||
|
:href="`/cmdb/cidetail/${attr.reference_type_id}/${ciId}`"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ getReferenceAttrValue(ciId) }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<span v-else-if="attr.value_type === '6' && ci[attr.name]">{{ JSON.stringify(ci[attr.name]) }}</span>
|
||||||
|
<template v-else-if="attr.is_link && ci[attr.name]">
|
||||||
|
<a
|
||||||
|
v-for="(item, linkIndex) in (attr.is_list ? ci[attr.name] : [ci[attr.name]])"
|
||||||
|
:key="linkIndex"
|
||||||
|
:href="
|
||||||
|
item.startsWith('http') || item.startsWith('https')
|
||||||
|
? `${item}`
|
||||||
|
: `http://${item}`
|
||||||
|
"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ getChoiceValueLabel(item) || item }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<PasswordField
|
||||||
|
v-else-if="attr.is_password && ci[attr.name]"
|
||||||
|
:ci_id="ci._id"
|
||||||
|
:attr_id="attr.id"
|
||||||
|
></PasswordField>
|
||||||
|
<template v-else-if="attr.is_choice">
|
||||||
|
<span
|
||||||
|
v-for="value in (attr.is_list ? ci[attr.name] : [ci[attr.name]])"
|
||||||
|
:key="value"
|
||||||
|
:style="{
|
||||||
|
borderRadius: '4px',
|
||||||
|
padding: '1px 5px',
|
||||||
|
margin: '2px',
|
||||||
|
...getChoiceValueStyle(value),
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<ops-icon
|
||||||
|
:style="{ color: getChoiceValueIcon(attr, value).color }"
|
||||||
|
:type="getChoiceValueIcon(attr, value).name"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-html="markSearchValue(getChoiceValueLabel(value) || value)"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
v-html="markSearchValue((attr.is_list && Array.isArray(ci[attr.name])) ? ci[attr.name].join(',') : ci[attr.name])"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PasswordField from '@/modules/cmdb/components/passwordField/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AttrDisplay',
|
||||||
|
components: {
|
||||||
|
PasswordField
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
attr: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
ci: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
isEllipsis: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
referenceShowAttrNameMap: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
referenceCIIdMap: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
searchValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
markSearchValue(text) {
|
||||||
|
if (!text || !this.searchValue) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
const regex = new RegExp(`(${this.searchValue})`, 'gi')
|
||||||
|
return String(text).replace(
|
||||||
|
regex,
|
||||||
|
`<span style="background-color: #D3EEFE; padding: 0 2px;">$1</span>`
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getChoiceValueStyle(attrValue) {
|
||||||
|
const _find = this?.attr?.choice_value?.find?.((item) => String(item?.[0]) === String(attrValue))
|
||||||
|
if (_find) {
|
||||||
|
return _find?.[1]?.style || {}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
getChoiceValueIcon(attrValue) {
|
||||||
|
const _find = this?.attr?.choice_value?.find((item) => String(item?.[0]) === String(attrValue))
|
||||||
|
if (_find) {
|
||||||
|
return _find?.[1]?.icon || {}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
getChoiceValueLabel(attrValue) {
|
||||||
|
const _find = this?.attr?.choice_value?.find((item) => String(item?.[0]) === String(attrValue))
|
||||||
|
if (_find) {
|
||||||
|
return _find?.[1]?.label || ''
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
getReferenceAttrValue(id) {
|
||||||
|
if (this.attr.referenceShowAttrNameMap?.[id]) {
|
||||||
|
return this.attr.referenceShowAttrNameMap[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ci = this?.referenceCIIdMap?.[this?.attr?.reference_type_id]?.[id]
|
||||||
|
if (!ci) {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
const attrName = this.referenceShowAttrNameMap?.[this?.attr.reference_type_id]
|
||||||
|
return ci?.[attrName] || id
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.attr-display {
|
||||||
|
width: 100%;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
&-ellipsis {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,235 @@
|
||||||
|
<template>
|
||||||
|
<a-popover
|
||||||
|
v-model="visible"
|
||||||
|
trigger="click"
|
||||||
|
placement="bottom"
|
||||||
|
@visibleChange="handleVisibleChange"
|
||||||
|
>
|
||||||
|
<div class="filter-btn">
|
||||||
|
<a-icon class="filter-btn-icon" type="filter" />
|
||||||
|
<span class="filter-btn-title">{{ $t('cmdb.ciType.advancedFilter') }}</span>
|
||||||
|
</div>
|
||||||
|
<template slot="content">
|
||||||
|
<div class="filter-content">
|
||||||
|
<a-form :form="form">
|
||||||
|
<a-form-item
|
||||||
|
:label="$t('cmdb.ciType.ciType')"
|
||||||
|
:label-col="formLayout.labelCol"
|
||||||
|
:wrapper-col="formLayout.wrapperCol"
|
||||||
|
>
|
||||||
|
<treeselect
|
||||||
|
:value="selectCITypeIds"
|
||||||
|
class="custom-treeselect custom-treeselect-bgcAndBorder filter-content-ciTypes"
|
||||||
|
:style="{
|
||||||
|
width: '400px',
|
||||||
|
zIndex: '1000',
|
||||||
|
'--custom-height': '32px',
|
||||||
|
'--custom-bg-color': '#FFF',
|
||||||
|
'--custom-border': '1px solid #d9d9d9',
|
||||||
|
'--custom-multiple-lineHeight': '32px',
|
||||||
|
}"
|
||||||
|
:multiple="true"
|
||||||
|
:clearable="true"
|
||||||
|
searchable
|
||||||
|
:options="CITypeGroup"
|
||||||
|
:limit="1"
|
||||||
|
:limitText="(count) => `+ ${count}`"
|
||||||
|
value-consists-of="LEAF_PRIORITY"
|
||||||
|
:placeholder="$t('cmdb.ciType.ciType')"
|
||||||
|
@close="closeCiTypeGroup"
|
||||||
|
@open="openCiTypeGroup"
|
||||||
|
@input="inputCiTypeGroup"
|
||||||
|
:normalizer="
|
||||||
|
(node) => {
|
||||||
|
return {
|
||||||
|
id: node.id || -1,
|
||||||
|
label: node.alias || node.name || $t('other'),
|
||||||
|
title: node.alias || node.name || $t('other'),
|
||||||
|
children: node.ci_types,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:title="node.label"
|
||||||
|
slot="option-label"
|
||||||
|
slot-scope="{ node }"
|
||||||
|
:style="{ width: '100%', whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }"
|
||||||
|
>
|
||||||
|
{{ node.label }}
|
||||||
|
</div>
|
||||||
|
</treeselect>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item
|
||||||
|
:label="$t('cmdb.ciType.filterPopoverLabel')"
|
||||||
|
:label-col="formLayout.labelCol"
|
||||||
|
:wrapper-col="formLayout.wrapperCol"
|
||||||
|
class="filter-content-condition-filter"
|
||||||
|
>
|
||||||
|
<ConditionFilter
|
||||||
|
ref="conditionFilterRef"
|
||||||
|
:canSearchPreferenceAttrList="allAttributesList"
|
||||||
|
:expression="expression"
|
||||||
|
:CITypeIds="selectCITypeIds"
|
||||||
|
:isDropdown="false"
|
||||||
|
@setExpFromFilter="setExpFromFilter"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
|
||||||
|
<div class="filter-content-action">
|
||||||
|
<a-button
|
||||||
|
size="small"
|
||||||
|
@click="saveCondition(false)"
|
||||||
|
>
|
||||||
|
{{ $t('cmdb.ciType.saveCondition') }}
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="saveCondition(true)"
|
||||||
|
>
|
||||||
|
{{ $t('confirm') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import ConditionFilter from '@/modules/cmdb/components/conditionFilter/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FilterPopover',
|
||||||
|
components: {
|
||||||
|
ConditionFilter
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
form: this.$form.createForm(this),
|
||||||
|
formLayout: {
|
||||||
|
labelCol: { span: 3 },
|
||||||
|
wrapperCol: { span: 15 },
|
||||||
|
},
|
||||||
|
lastCiType: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
expression: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
selectCITypeIds: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
CITypeGroup: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
allAttributesList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleVisibleChange(open) {
|
||||||
|
if (open) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.conditionFilterRef.init(true, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openCiTypeGroup() {
|
||||||
|
this.lastCiType = _.cloneDeep(this.selectCITypeIds)
|
||||||
|
},
|
||||||
|
closeCiTypeGroup(value) {
|
||||||
|
if (!_.isEqual(value, this.lastCiType)) {
|
||||||
|
this.$emit('updateAllAttributesList', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inputCiTypeGroup(value) {
|
||||||
|
if (!value || !value.length) {
|
||||||
|
this.$emit('updateAllAttributesList', value)
|
||||||
|
}
|
||||||
|
this.$emit('changeFilter', {
|
||||||
|
name: 'selectCITypeIds',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
setExpFromFilter(filterExp) {
|
||||||
|
const regSort = /(?<=sort=).+/g
|
||||||
|
const expSort = this.expression.match(regSort) ? this.expression.match(regSort)[0] : undefined
|
||||||
|
let expression = ''
|
||||||
|
if (filterExp) {
|
||||||
|
expression = `q=${filterExp}`
|
||||||
|
}
|
||||||
|
if (expSort) {
|
||||||
|
expression += `&sort=${expSort}`
|
||||||
|
}
|
||||||
|
this.$emit('changeFilter', {
|
||||||
|
name: 'expression',
|
||||||
|
value: expression
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
saveCondition(isSubmit) {
|
||||||
|
this.$refs.conditionFilterRef.handleSubmit()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$emit('saveCondition', isSubmit)
|
||||||
|
this.visible = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.filter-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
color: #2F54EB;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #2F54EB;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter-content {
|
||||||
|
width: 600px;
|
||||||
|
|
||||||
|
&-ciTypes {
|
||||||
|
/deep/ .vue-treeselect__value-container {
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-condition-filter {
|
||||||
|
max-height: 250px;
|
||||||
|
// overflow-y: auto;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-action {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,310 @@
|
||||||
|
<template>
|
||||||
|
<div class="history-list">
|
||||||
|
<div
|
||||||
|
class="history-recent"
|
||||||
|
v-if="recentList.length"
|
||||||
|
>
|
||||||
|
<div class="history-title">
|
||||||
|
<a-icon type="eye" class="history-title-icon" />
|
||||||
|
<div class="history-title-text">{{ $t('cmdb.ciType.recentSearch') }}</div>
|
||||||
|
|
||||||
|
<a-popconfirm
|
||||||
|
:title="$t('cmdb.ciType.confirmClear')"
|
||||||
|
placement="topRight"
|
||||||
|
@confirm="clearRecent"
|
||||||
|
>
|
||||||
|
<a-tooltip :title="$t('clear')" >
|
||||||
|
<a-icon
|
||||||
|
type="delete"
|
||||||
|
class="history-title-clear"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-popconfirm>
|
||||||
|
</div>
|
||||||
|
<div class="recent-list">
|
||||||
|
<div
|
||||||
|
v-for="(item) in recentList.slice(0, 10)"
|
||||||
|
:key="item.id"
|
||||||
|
class="recent-list-item"
|
||||||
|
@click="clickRecent(item.option)"
|
||||||
|
>
|
||||||
|
<div class="recent-list-item-text">
|
||||||
|
{{ getRecentSearchText(item.option) }}
|
||||||
|
</div>
|
||||||
|
<a-icon
|
||||||
|
type="close"
|
||||||
|
class="recent-list-item-close"
|
||||||
|
@click.stop="deleteRecent(item.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="history-favor"
|
||||||
|
v-if="favorList.length"
|
||||||
|
>
|
||||||
|
<div class="history-title">
|
||||||
|
<ops-icon type="veops-collect" class="history-title-icon" />
|
||||||
|
<div class="history-title-text">{{ $t('cmdb.ciType.myCollection') }}</div>
|
||||||
|
<div class="history-title-count">({{ favorList.length }})</div>
|
||||||
|
|
||||||
|
<ops-icon
|
||||||
|
type="veops-expand"
|
||||||
|
class="history-title-expand"
|
||||||
|
:style="{
|
||||||
|
transform: `rotate(${isExpand ? '180deg' : '0deg'})`
|
||||||
|
}"
|
||||||
|
@click="isExpand = !isExpand"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="favor-list"
|
||||||
|
:style="{ height: isExpand ? 'auto' : '30px' }"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item) in favorList"
|
||||||
|
:key="item.id"
|
||||||
|
:class="['favor-list-item', detailCIId === item.option.CIId ? 'favor-list-item-selected' : '']"
|
||||||
|
@click="showDetail(item.option)"
|
||||||
|
>
|
||||||
|
<CIIcon
|
||||||
|
:icon="item.option.icon"
|
||||||
|
:title="item.option.CITypeTitle"
|
||||||
|
/>
|
||||||
|
<div class="favor-list-item-title">
|
||||||
|
{{ item.option.title }}
|
||||||
|
</div>
|
||||||
|
<ops-icon
|
||||||
|
type="veops-collected"
|
||||||
|
class="favor-list-item-collected"
|
||||||
|
@click.stop="deleteCollect(item.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import CIIcon from '@/modules/cmdb/components/ciIcon/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HistoryList',
|
||||||
|
components: {
|
||||||
|
CIIcon
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
recentList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
favorList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
detailCIId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: -1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isExpand: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getRecentSearchText(option) {
|
||||||
|
const textArray = []
|
||||||
|
if (option.searchValue) {
|
||||||
|
textArray.push(`${this.$t('cmdb.ciType.keyword')}: ${option.searchValue}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option?.ciTypeNames?.length) {
|
||||||
|
textArray.push(`${this.$t('cmdb.ciType.CIType')}: ${option.ciTypeNames.join(',')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.expression) {
|
||||||
|
textArray.push(`${this.$t('cmdb.ciType.conditionFilter')}: ${option.expression}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return textArray.join('; ')
|
||||||
|
},
|
||||||
|
|
||||||
|
clickRecent(data) {
|
||||||
|
this.$emit('clickRecent', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteRecent(id) {
|
||||||
|
this.$emit('deleteRecent', id)
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCollect(id) {
|
||||||
|
this.$emit('deleteCollect', id)
|
||||||
|
},
|
||||||
|
|
||||||
|
showDetail(data) {
|
||||||
|
this.$emit('showDetail', {
|
||||||
|
id: data.CIId,
|
||||||
|
ciTypeId: data.CITypeId
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
clearRecent() {
|
||||||
|
this.$emit('clearRecent')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.history-list {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.history-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #86909C;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-clear {
|
||||||
|
margin-left: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-expand {
|
||||||
|
margin-left: auto;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-recent {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.recent-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 16px;
|
||||||
|
row-gap: 8px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 4px 13px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 22px;
|
||||||
|
background: rgba(255, 255, 255, 0.50);
|
||||||
|
cursor: pointer;
|
||||||
|
max-width: calc((100% - 16px) / 2);
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-close {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #A5A9BC;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.recent-list-item-text {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recent-list-item-close {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-favor {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
.favor-list {
|
||||||
|
margin-top: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
column-gap: 16px;
|
||||||
|
row-gap: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 30px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 4px 13px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 22px;
|
||||||
|
background: rgba(255, 255, 255, 0.90);
|
||||||
|
cursor: pointer;
|
||||||
|
max-width: calc((100% - 16px) / 2);
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-collected {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 4px;
|
||||||
|
color: #FAD337;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selected {
|
||||||
|
border: 1px solid #7F97FA;
|
||||||
|
background-color: rgba(255, 255, 255, 0.90);
|
||||||
|
|
||||||
|
.favor-list-item-title {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.favor-list-item-title {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,438 @@
|
||||||
|
<template>
|
||||||
|
<div class="instance-detail">
|
||||||
|
<div
|
||||||
|
class="instance-detail-hide"
|
||||||
|
@click="hideDetail"
|
||||||
|
>
|
||||||
|
<a-icon class="instance-detail-hide-icon" type="right" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="instance-detail-null" v-if="!ci._id" >
|
||||||
|
<img
|
||||||
|
:src="require('@/modules/cmdb/assets/no_permission.png')"
|
||||||
|
class="instance-detail-null-img"
|
||||||
|
/>
|
||||||
|
<span class="instance-detail-null-text" >{{ $t('noData') }}</span>
|
||||||
|
</div>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
class="instance-detail-header"
|
||||||
|
>
|
||||||
|
<div class="instance-detail-header-line-1"></div>
|
||||||
|
<div class="instance-detail-header-line-2"></div>
|
||||||
|
<div class="instance-detail-header-row">
|
||||||
|
<CIIcon
|
||||||
|
:icon="ciType.icon"
|
||||||
|
:title="ciType.name || ''"
|
||||||
|
:size="20"
|
||||||
|
/>
|
||||||
|
<div class="instance-detail-header-title">
|
||||||
|
{{ detailTitle }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ops-icon
|
||||||
|
:type="favorId ? 'veops-collected' : 'veops-collect'"
|
||||||
|
:style="{ color: favorId ? '#FAD337' : '#A5A9BC' }"
|
||||||
|
class="instance-detail-header-collect"
|
||||||
|
@click="clickCollect"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<a class="instance-detail-header-share" @click="shareCi">
|
||||||
|
<a-icon type="share-alt" />
|
||||||
|
{{ $t('cmdb.ci.share') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="instance-detail-attr">
|
||||||
|
<div
|
||||||
|
v-for="(group) in attributeGroups"
|
||||||
|
:key="group.id"
|
||||||
|
class="instance-detail-attr-group"
|
||||||
|
>
|
||||||
|
<span class="instance-detail-attr-group-name">{{ group.name || $t('other') }}</span>
|
||||||
|
|
||||||
|
<div class="instance-detail-attr-list">
|
||||||
|
<div
|
||||||
|
v-for="(attr) in group.attributes"
|
||||||
|
:key="attr.id"
|
||||||
|
class="instance-detail-attr-item"
|
||||||
|
>
|
||||||
|
<a-tooltip :title="attr.alias || attr.name || ''">
|
||||||
|
<div class="instance-detail-attr-item-label">
|
||||||
|
<span class="instance-detail-attr-item-label-text">
|
||||||
|
{{ attr.alias || attr.name || '' }}
|
||||||
|
</span>
|
||||||
|
<span class="instance-detail-attr-item-label-colon">:</span>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<div class="instance-detail-attr-item-value">
|
||||||
|
<AttrDisplay
|
||||||
|
:attr="attr"
|
||||||
|
:ci="ci"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { getCIById, searchCI } from '@/modules/cmdb/api/ci'
|
||||||
|
import { getCITypeGroupById, getCITypes, getCIType } from '@/modules/cmdb/api/CIType'
|
||||||
|
|
||||||
|
import AttrDisplay from './attrDisplay.vue'
|
||||||
|
import CIIcon from '@/modules/cmdb/components/ciIcon/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'InstanceDetail',
|
||||||
|
components: {
|
||||||
|
AttrDisplay,
|
||||||
|
CIIcon
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
CIId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
CITypeId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
favorList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ci: {},
|
||||||
|
ciType: {},
|
||||||
|
attributeGroups: [],
|
||||||
|
isNullData: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
watchParams() {
|
||||||
|
return {
|
||||||
|
CIId: this.CIId,
|
||||||
|
CITypeId: this.CITypeId
|
||||||
|
}
|
||||||
|
},
|
||||||
|
detailTitle() {
|
||||||
|
const attrName = this?.ciType?.show_name || this?.ciType?.unique_name || ''
|
||||||
|
return attrName ? (this?.ci?.[attrName] || '') : ''
|
||||||
|
},
|
||||||
|
favorId() {
|
||||||
|
const id = this.favorList.find((item) => item?.option?.CIId === this.CIId)?.id
|
||||||
|
return id ?? null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
watchParams: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal?.CIId !== -1 && newVal?.CITypeId !== -1) {
|
||||||
|
this.initData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initData() {
|
||||||
|
const ci = await this.getCI()
|
||||||
|
if (!ci) {
|
||||||
|
this.isNullData = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await this.getCIType()
|
||||||
|
await this.getAttributes()
|
||||||
|
},
|
||||||
|
|
||||||
|
async getCI() {
|
||||||
|
const res = await getCIById(this.CIId)
|
||||||
|
const ci = res.result?.[0] || {}
|
||||||
|
this.ci = ci
|
||||||
|
return ci
|
||||||
|
},
|
||||||
|
|
||||||
|
async getCIType() {
|
||||||
|
const res = await getCIType(this.CITypeId)
|
||||||
|
this.ciType = res?.ci_types?.[0] || {}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAttributes() {
|
||||||
|
const res = await getCITypeGroupById(this.CITypeId, { need_other: 1 })
|
||||||
|
this.attributeGroups = res
|
||||||
|
this.handleReferenceAttr()
|
||||||
|
},
|
||||||
|
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
|
||||||
|
shareCi() {
|
||||||
|
const text = `${document.location.host}/cmdb/cidetail/${this.CITypeId}/${this.CIId}`
|
||||||
|
this.$copyText(text)
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success(this.$t('copySuccess'))
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error(this.$t('cmdb.ci.copyFailed'))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
clickCollect() {
|
||||||
|
if (this.favorId) {
|
||||||
|
this.$emit('deleteCollect', this.favorId)
|
||||||
|
} else {
|
||||||
|
this.$emit('addCollect', {
|
||||||
|
CIId: this.CIId,
|
||||||
|
CITypeId: this.CITypeId,
|
||||||
|
title: this.detailTitle,
|
||||||
|
icon: this.ciType?.icon,
|
||||||
|
CITypeTitle: this.ciType?.name || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hideDetail() {
|
||||||
|
this.$emit('hideDetail')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.instance-detail {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid #E4E7ED;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-hide {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: -21px;
|
||||||
|
border-radius: 0px 2px 2px 0px;
|
||||||
|
background-color: #2f54eb;
|
||||||
|
width: 13px;
|
||||||
|
height: 43px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 2;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #597ef7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-null {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 100px;
|
||||||
|
|
||||||
|
&-img {
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
color: #86909C;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
width: 100%;
|
||||||
|
height: 75px;
|
||||||
|
background-color: #EBF0F9;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&-line-1 {
|
||||||
|
height: 44px;
|
||||||
|
width: 300px;
|
||||||
|
position: absolute;
|
||||||
|
right: -20px;
|
||||||
|
top: 0px;
|
||||||
|
transform: rotate(40deg);
|
||||||
|
background: rgba(248, 249, 253, 0.60);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-line-2 {
|
||||||
|
height: 44px;
|
||||||
|
width: 300px;
|
||||||
|
position: absolute;
|
||||||
|
right: -110px;
|
||||||
|
top: 0px;
|
||||||
|
transform: rotate(40deg);
|
||||||
|
background: rgba(248, 249, 253, 0.60);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-row {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-collect {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-share {
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-attr {
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
height: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
&-group {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #86909C;
|
||||||
|
width: 25%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-colon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,443 @@
|
||||||
|
<template>
|
||||||
|
<div class="list-wrap">
|
||||||
|
<div class="list-wrap-bg" v-if="!filterList.length">
|
||||||
|
<img :src="require('@/modules/cmdb/assets/resourceSearch/resource_search_bg_2.png')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="tabList.length" class="list-tab">
|
||||||
|
<div class="list-tab-left">
|
||||||
|
<div class="list-tab-label">{{ $t('cmdb.ciType.currentPage') }}: </div>
|
||||||
|
<div
|
||||||
|
v-for="(tab) in tabList"
|
||||||
|
:key="tab.id"
|
||||||
|
:class="['list-tab-item', tab.id === currentTab ? 'list-tab-item-active' : '']"
|
||||||
|
@click="clickTab(tab.id)"
|
||||||
|
>
|
||||||
|
<span class="list-tab-item-title">{{ tab.title }}</span>
|
||||||
|
(<span class="list-tab-item-count">{{ tab.count }}</span>)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-button
|
||||||
|
icon="download"
|
||||||
|
type="primary"
|
||||||
|
class="ops-button-ghost list-tab-export"
|
||||||
|
ghost
|
||||||
|
@click="handleExport"
|
||||||
|
>
|
||||||
|
{{ $t('download') }}
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filterList.length" class="list-container">
|
||||||
|
<div
|
||||||
|
v-for="(item) in filterList"
|
||||||
|
:key="item._id"
|
||||||
|
:class="['list-card', detailCIId === item.ci._id ? 'list-card-selected' : '']"
|
||||||
|
@click="clickInstance(item.ci._id, item.ciTypeObj.id)"
|
||||||
|
>
|
||||||
|
<div class="list-card-header">
|
||||||
|
<div class="list-card-model">
|
||||||
|
<CIIcon
|
||||||
|
:icon="item.ciTypeObj.icon"
|
||||||
|
:title="item.ciTypeObj.name"
|
||||||
|
/>
|
||||||
|
<span class="list-card-model-title">{{ item.ciTypeObj.title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="list-card-title">{{ item.ci[item.ciTypeObj.showAttrName] }}</div>
|
||||||
|
|
||||||
|
<ops-icon
|
||||||
|
v-if="getFavorId(item.ci._id)"
|
||||||
|
type="veops-collected"
|
||||||
|
class="list-card-collect"
|
||||||
|
:style="{ color: '#FAD337' }"
|
||||||
|
@click.stop="deleteCollect(item.ci._id)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ops-icon
|
||||||
|
v-else
|
||||||
|
type="veops-collect"
|
||||||
|
class="list-card-collect"
|
||||||
|
:style="{ color: '#A5A9BC' }"
|
||||||
|
@click.stop="addCollect(item)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="list-card-attr">
|
||||||
|
<div
|
||||||
|
v-for="(attr) in item.attributes"
|
||||||
|
:key="attr.name"
|
||||||
|
class="list-card-attr-item"
|
||||||
|
>
|
||||||
|
<div class="list-card-attr-item-label">{{ attr.alias || attr.name || '' }}: </div>
|
||||||
|
<div class="list-card-attr-item-value">
|
||||||
|
<AttrDisplay
|
||||||
|
:attr="attr"
|
||||||
|
:ci="item.ci"
|
||||||
|
:referenceShowAttrNameMap="referenceShowAttrNameMap"
|
||||||
|
:referenceCIIdMap="referenceCIIdMap"
|
||||||
|
:isEllipsis="true"
|
||||||
|
:searchValue="searchValue"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ExcelJS from 'exceljs'
|
||||||
|
import FileSaver from 'file-saver'
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
|
import AttrDisplay from './attrDisplay.vue'
|
||||||
|
import CIIcon from '@/modules/cmdb/components/ciIcon/index.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'InstanceList',
|
||||||
|
components: {
|
||||||
|
AttrDisplay,
|
||||||
|
CIIcon
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
list: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
tabList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
referenceShowAttrNameMap: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
referenceCIIdMap: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
favorList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
detailCIId: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
searchValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
currentTab: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filterList() {
|
||||||
|
if (!this.currentTab || this.currentTab === -1) {
|
||||||
|
return this.list
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.list.filter((item) => item.ciTypeObj.id === this.currentTab)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
tabList: {
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
handler(newVal) {
|
||||||
|
this.currentTab = newVal?.[0]?.id ?? ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickTab(id) {
|
||||||
|
this.currentTab = id
|
||||||
|
},
|
||||||
|
|
||||||
|
getAttrLabel(attrName, attributes) {
|
||||||
|
const label = attributes.find((attr) => attr.name === attrName)?.alias
|
||||||
|
return label || attrName
|
||||||
|
},
|
||||||
|
|
||||||
|
clickInstance(id, ciTypeId) {
|
||||||
|
this.$emit('showDetail', {
|
||||||
|
id,
|
||||||
|
ciTypeId,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getFavorId(ciId) {
|
||||||
|
const id = this.favorList.find((item) => item?.option?.CIId === ciId)?.id
|
||||||
|
return id ?? null
|
||||||
|
},
|
||||||
|
|
||||||
|
addCollect(data) {
|
||||||
|
this.$emit('addCollect', {
|
||||||
|
CIId: data.ci._id,
|
||||||
|
CITypeId: data.ciTypeObj.id,
|
||||||
|
title: data.ci[data.ciTypeObj.showAttrName],
|
||||||
|
icon: data.ciTypeObj.icon,
|
||||||
|
CITypeTitle: data.ciTypeObj.name
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteCollect(ciId) {
|
||||||
|
const favorId = this.getFavorId(ciId)
|
||||||
|
if (favorId) {
|
||||||
|
this.$emit('deleteCollect', favorId)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
handleExport() {
|
||||||
|
const excel_name = `cmdb-${this.$t('cmdb.ciType.resourceSearch')}-${moment().format('YYYYMMDDHHmmss')}.xlsx`
|
||||||
|
const wb = new ExcelJS.Workbook()
|
||||||
|
|
||||||
|
this.tabList.map((sheet) => {
|
||||||
|
if (sheet.id === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ws = wb.addWorksheet(sheet.title)
|
||||||
|
this.handleSheetData({
|
||||||
|
ws,
|
||||||
|
sheet
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
wb.xlsx.writeBuffer().then((buffer) => {
|
||||||
|
const file = new Blob([buffer], {
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
})
|
||||||
|
FileSaver.saveAs(file, excel_name)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSheetData({
|
||||||
|
ws,
|
||||||
|
sheet
|
||||||
|
}) {
|
||||||
|
const listData = this.list.filter((item) => item.ciTypeObj.id === sheet.id)
|
||||||
|
if (!listData.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnMap = new Map()
|
||||||
|
const columns = listData[0].attributes.filter((attr) => !attr.is_password).map((attr) => {
|
||||||
|
columnMap.set(attr.name, attr)
|
||||||
|
|
||||||
|
return {
|
||||||
|
header: attr.alias || attr.name || '',
|
||||||
|
key: attr.name,
|
||||||
|
width: 20,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ws.columns = columns
|
||||||
|
|
||||||
|
listData.forEach((data) => {
|
||||||
|
const row = {}
|
||||||
|
columns.forEach(({ key }) => {
|
||||||
|
const value = data?.ci?.[key] ?? null
|
||||||
|
const attr = columnMap.get(key)
|
||||||
|
if (attr.valueType === '6') {
|
||||||
|
row[key] = value ? JSON.stringify(value) : value
|
||||||
|
} else if (attr.is_list && Array.isArray(value)) {
|
||||||
|
row[key] = value.join(',')
|
||||||
|
} else {
|
||||||
|
row[key] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ws.addRow(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.list-wrap {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
flex-shrink: 1 !important;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&-bg {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 90px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-tab {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-shrink: 0;
|
||||||
|
column-gap: 14px;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 14px;
|
||||||
|
row-gap: 7px;
|
||||||
|
overflow-x: auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #4E5969;
|
||||||
|
cursor: pointer;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&-count {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #2F54EB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-export {
|
||||||
|
margin-left: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 12px;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex-shrink: 1;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
.list-card {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #FFF;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-selected {
|
||||||
|
border: 1px solid #7F97FA;
|
||||||
|
background-color: #F9FBFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-model {
|
||||||
|
border-radius: 24px;
|
||||||
|
border: 1px solid #E4E7ED;
|
||||||
|
background-color: #FFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 24px;
|
||||||
|
padding: 0 13px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
color: #1D2129;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
margin-left: 11px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-collect {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 9px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-attr {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 25px;
|
||||||
|
column-gap: 40px;
|
||||||
|
row-gap: 20px;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-width: calc((100% - 160px) / 5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
color: #86909C;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-value {
|
||||||
|
color: #1D2129;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-left: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0px 2px 12px 0px rgba(147, 168, 223, 0.20);
|
||||||
|
|
||||||
|
.list-card-collect {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,201 @@
|
||||||
|
<template>
|
||||||
|
<div :class="['search-input', classType ? 'search-input-' + classType : '']">
|
||||||
|
<a-input
|
||||||
|
:value="searchValue"
|
||||||
|
class="search-input-component"
|
||||||
|
:placeholder="$t('cmdb.ciType.searchInputTip')"
|
||||||
|
@change="handleChangeSearchValue"
|
||||||
|
@pressEnter="saveCondition(true)"
|
||||||
|
>
|
||||||
|
<a-icon
|
||||||
|
class="search-input-component-icon"
|
||||||
|
slot="prefix"
|
||||||
|
type="search"
|
||||||
|
@click="saveCondition(true)"
|
||||||
|
/>
|
||||||
|
</a-input>
|
||||||
|
<FilterPopover
|
||||||
|
ref="filterPpoverRef"
|
||||||
|
:CITypeGroup="CITypeGroup"
|
||||||
|
:allAttributesList="allAttributesList"
|
||||||
|
:expression="expression"
|
||||||
|
:selectCITypeIds="selectCITypeIds"
|
||||||
|
@changeFilter="changeFilter"
|
||||||
|
@updateAllAttributesList="updateAllAttributesList"
|
||||||
|
@saveCondition="saveCondition"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div v-if="copyText" class="expression-display">
|
||||||
|
<span class="expression-display-text">{{ copyText }}</span>
|
||||||
|
<a-icon
|
||||||
|
slot="suffix"
|
||||||
|
type="check-circle"
|
||||||
|
class="expression-display-icon"
|
||||||
|
@click="handleCopyExpression"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FilterPopover from './filterPopover.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'SearchInput',
|
||||||
|
components: {
|
||||||
|
FilterPopover
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
searchValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
expression: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
selectCITypeIds: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
CITypeGroup: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
allAttributesList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
classType: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 复制文字展示,与实际文本复制内容区别在于,未选择模型时不展示所有模型拼接数据
|
||||||
|
copyText() {
|
||||||
|
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||||
|
const exp = this.expression.match(regQ) ? this.expression.match(regQ)[0] : null
|
||||||
|
|
||||||
|
const textArray = []
|
||||||
|
if (this.selectCITypeIds?.length) {
|
||||||
|
textArray.push(`_type:(${this.selectCITypeIds.join(';')})`)
|
||||||
|
}
|
||||||
|
if (exp) {
|
||||||
|
textArray.push(exp)
|
||||||
|
}
|
||||||
|
if (this.searchValue) {
|
||||||
|
textArray.push(`*${this.searchValue}*`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return textArray.length ? `q=${textArray.join(',')}` : ''
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateAllAttributesList(value) {
|
||||||
|
this.$emit('updateAllAttributesList', value)
|
||||||
|
},
|
||||||
|
saveCondition(isSubmit) {
|
||||||
|
this.$emit('saveCondition', isSubmit)
|
||||||
|
},
|
||||||
|
handleChangeSearchValue(e) {
|
||||||
|
const value = e.target.value
|
||||||
|
this.changeFilter({
|
||||||
|
name: 'searchValue',
|
||||||
|
value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
changeFilter(data) {
|
||||||
|
this.$emit('changeFilter', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCopyExpression() {
|
||||||
|
const { selectCITypeIds, expression, searchValue } = this
|
||||||
|
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||||
|
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||||
|
|
||||||
|
const ciTypeIds = [...selectCITypeIds]
|
||||||
|
if (!ciTypeIds.length) {
|
||||||
|
this.CITypeGroup.forEach((item) => {
|
||||||
|
const ids = item.ci_types.map((ci_type) => ci_type.id)
|
||||||
|
ciTypeIds.push(...ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const copyText = `${ciTypeIds?.length ? `_type:(${ciTypeIds.join(';')})` : ''}${exp ? `,${exp}` : ''}${searchValue ? `,*${searchValue}*` : ''}`
|
||||||
|
|
||||||
|
this.$copyText(copyText)
|
||||||
|
.then(() => {
|
||||||
|
this.$message.success(this.$t('copySuccess'))
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.$message.error(this.$t('cmdb.ci.copyFailed'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&-component {
|
||||||
|
height: 100%;
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 48px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
color: #2F54EB;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ & > input {
|
||||||
|
height: 100%;
|
||||||
|
margin-left: 10px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-after {
|
||||||
|
height: 38px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.search-input-component {
|
||||||
|
max-width: 524px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.expression-display {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: 20px;
|
||||||
|
max-width: 30%;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #00b42a;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,615 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="resource-search"
|
||||||
|
:style="{ height: `${windowHeight - 93}px` }"
|
||||||
|
>
|
||||||
|
<div v-if="!isSearch" class="resource-search-before">
|
||||||
|
<div class="resource-search-title">
|
||||||
|
<ops-icon class="resource-search-title-icon" type="veops-resource11" />
|
||||||
|
<span class="resource-search-title-text">{{ $t('cmdb.ciType.resourceSearch') }}</span>
|
||||||
|
</div>
|
||||||
|
<SearchInput
|
||||||
|
ref="searchInputRef"
|
||||||
|
:CITypeGroup="CITypeGroup"
|
||||||
|
:allAttributesList="allAttributesList"
|
||||||
|
:searchValue="searchValue"
|
||||||
|
:selectCITypeIds="selectCITypeIds"
|
||||||
|
:expression="expression"
|
||||||
|
@changeFilter="changeFilter"
|
||||||
|
@updateAllAttributesList="updateAllAttributesList"
|
||||||
|
@saveCondition="saveCondition"
|
||||||
|
/>
|
||||||
|
<HistoryList
|
||||||
|
:recentList="recentList"
|
||||||
|
:favorList="favorList"
|
||||||
|
:detailCIId="detailCIId"
|
||||||
|
@clickRecent="clickRecent"
|
||||||
|
@deleteRecent="deleteRecent"
|
||||||
|
@clearRecent="clearRecent"
|
||||||
|
@deleteCollect="deleteCollect"
|
||||||
|
@showDetail="clickFavor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<img class="resource-search-before-bg" :src="require('@/modules/cmdb/assets/resourceSearch/resource_search_bg_1.png')" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource-search-after" v-else>
|
||||||
|
<div
|
||||||
|
class="resource-search-after-left"
|
||||||
|
:style="{ width: showInstanceDetail ? '70%' : '100%' }"
|
||||||
|
>
|
||||||
|
<SearchInput
|
||||||
|
ref="searchInputRef"
|
||||||
|
classType="after"
|
||||||
|
:CITypeGroup="CITypeGroup"
|
||||||
|
:allAttributesList="allAttributesList"
|
||||||
|
:searchValue="searchValue"
|
||||||
|
:selectCITypeIds="selectCITypeIds"
|
||||||
|
:expression="expression"
|
||||||
|
@changeFilter="changeFilter"
|
||||||
|
@updateAllAttributesList="updateAllAttributesList"
|
||||||
|
@saveCondition="saveCondition"
|
||||||
|
/>
|
||||||
|
<HistoryList
|
||||||
|
:recentList="recentList"
|
||||||
|
:favorList="favorList"
|
||||||
|
:detailCIId="detailCIId"
|
||||||
|
@clickRecent="clickRecent"
|
||||||
|
@deleteRecent="deleteRecent"
|
||||||
|
@clearRecent="clearRecent"
|
||||||
|
@deleteCollect="deleteCollect"
|
||||||
|
@showDetail="clickFavor"
|
||||||
|
/>
|
||||||
|
<div class="resource-search-divider"></div>
|
||||||
|
<InstanceList
|
||||||
|
:list="instanceList"
|
||||||
|
:tabList="ciTabList"
|
||||||
|
:referenceShowAttrNameMap="referenceShowAttrNameMap"
|
||||||
|
:referenceCIIdMap="referenceCIIdMap"
|
||||||
|
:favorList="favorList"
|
||||||
|
:detailCIId="detailCIId"
|
||||||
|
:searchValue="currentSearchValue"
|
||||||
|
@showDetail="showDetail"
|
||||||
|
@addCollect="addCollect"
|
||||||
|
@deleteCollect="deleteCollect"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="resource-search-pagination">
|
||||||
|
<a-pagination
|
||||||
|
:showSizeChanger="true"
|
||||||
|
:current="currentPage"
|
||||||
|
size="small"
|
||||||
|
:total="totalNumber"
|
||||||
|
show-quick-jumper
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-size-options="pageSizeOptions"
|
||||||
|
@showSizeChange="handlePageSizeChange"
|
||||||
|
:show-total="
|
||||||
|
(total, range) =>
|
||||||
|
$t('pagination.total', {
|
||||||
|
range0: range[0],
|
||||||
|
range1: range[1],
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
"
|
||||||
|
@change="changePage"
|
||||||
|
>
|
||||||
|
<template slot="buildOptionText" slot-scope="props">
|
||||||
|
<span v-if="props.value !== '100000'">{{ props.value }}{{ $t('itemsPerPage') }}</span>
|
||||||
|
<span v-if="props.value === '100000'">{{ $t('all') }}</span>
|
||||||
|
</template>
|
||||||
|
</a-pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="showInstanceDetail" class="resource-search-after-right">
|
||||||
|
<InstanceDetail
|
||||||
|
:CIId="detailCIId"
|
||||||
|
:CITypeId="detailCITypeId"
|
||||||
|
:favorList="favorList"
|
||||||
|
@addCollect="addCollect"
|
||||||
|
@deleteCollect="deleteCollect"
|
||||||
|
@hideDetail="hideDetail"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { getPreferenceSearch, savePreferenceSearch, getSubscribeAttributes, deletePreferenceSearch } from '@/modules/cmdb/api/preference'
|
||||||
|
import { getCITypeGroups } from '@/modules/cmdb/api/ciTypeGroup'
|
||||||
|
import { searchAttributes, getCITypeAttributesByTypeIds } from '@/modules/cmdb/api/CITypeAttr'
|
||||||
|
import { searchCI } from '@/modules/cmdb/api/ci'
|
||||||
|
import { getCITypes } from '@/modules/cmdb/api/CIType'
|
||||||
|
|
||||||
|
import SearchInput from './components/searchInput.vue'
|
||||||
|
import HistoryList from './components/historyList.vue'
|
||||||
|
import InstanceList from './components/instanceList.vue'
|
||||||
|
import InstanceDetail from './components/instanceDetail.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ResourceSearch',
|
||||||
|
components: {
|
||||||
|
SearchInput,
|
||||||
|
HistoryList,
|
||||||
|
InstanceList,
|
||||||
|
InstanceDetail
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 筛选条件
|
||||||
|
searchValue: '', // 搜索框
|
||||||
|
selectCITypeIds: [], // 已选模型
|
||||||
|
expression: '', // 筛选语句
|
||||||
|
currentSearchValue: '', // 当前已搜索语句
|
||||||
|
|
||||||
|
recentList: [], // 最近搜索
|
||||||
|
favorList: [], // 我的收藏
|
||||||
|
CITypeGroup: [], // CIType 分组
|
||||||
|
CITypes: [],
|
||||||
|
allAttributesList: [],
|
||||||
|
|
||||||
|
isSearch: false, // 是否搜索过
|
||||||
|
currentPage: 1,
|
||||||
|
pageSizeOptions: ['50', '100', '200', '100000'],
|
||||||
|
pageSize: 50,
|
||||||
|
totalNumber: 0,
|
||||||
|
ciTabList: [],
|
||||||
|
instanceList: [],
|
||||||
|
referenceShowAttrNameMap: {},
|
||||||
|
referenceCIIdMap: {},
|
||||||
|
|
||||||
|
showInstanceDetail: false,
|
||||||
|
detailCIId: -1,
|
||||||
|
detailCITypeId: -1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
windowHeight() {
|
||||||
|
return this.$store.state.windowHeight
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.initData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async initData() {
|
||||||
|
await this.getRecentList()
|
||||||
|
await this.getFavorList()
|
||||||
|
await this.getCITypeGroups()
|
||||||
|
await this.getAllCITypes()
|
||||||
|
await this.getAllAttr()
|
||||||
|
},
|
||||||
|
|
||||||
|
async getRecentList() {
|
||||||
|
const recentList = await getPreferenceSearch({
|
||||||
|
name: '__recent__'
|
||||||
|
})
|
||||||
|
recentList.sort((a, b) => b.id - a.id)
|
||||||
|
this.recentList = recentList
|
||||||
|
},
|
||||||
|
|
||||||
|
async getFavorList() {
|
||||||
|
const favorList = await getPreferenceSearch({
|
||||||
|
name: '__favor__'
|
||||||
|
})
|
||||||
|
favorList.sort((a, b) => b.id - a.id)
|
||||||
|
this.favorList = favorList
|
||||||
|
},
|
||||||
|
|
||||||
|
async getCITypeGroups() {
|
||||||
|
const res = await getCITypeGroups({ need_other: true })
|
||||||
|
|
||||||
|
this.CITypeGroup = res
|
||||||
|
.filter((item) => item.ci_types && item.ci_types.length)
|
||||||
|
.map((item) => {
|
||||||
|
item.id = `parent_${item.id || -1}`
|
||||||
|
return { ..._.cloneDeep(item) }
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAllCITypes() {
|
||||||
|
const res = await getCITypes()
|
||||||
|
this.CITypes = res?.ci_types
|
||||||
|
},
|
||||||
|
|
||||||
|
async getAllAttr() {
|
||||||
|
const res = await searchAttributes({ page_size: 9999 })
|
||||||
|
this.allAttributesList = res.attributes
|
||||||
|
this.originAllAttributesList = res.attributes
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateAllAttributesList(value) {
|
||||||
|
if (value && value.length) {
|
||||||
|
const res = await getCITypeAttributesByTypeIds({ type_ids: value.join(',') })
|
||||||
|
this.allAttributesList = res.attributes
|
||||||
|
} else {
|
||||||
|
this.allAttributesList = this.originAllAttributesList
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveCondition(isSubmit) {
|
||||||
|
if (
|
||||||
|
this.searchValue ||
|
||||||
|
this.expression ||
|
||||||
|
this.selectCITypeIds.length
|
||||||
|
) {
|
||||||
|
const needDeleteList = []
|
||||||
|
const differentList = []
|
||||||
|
this.recentList.forEach((item) => {
|
||||||
|
const option = item.option
|
||||||
|
if (
|
||||||
|
option.searchValue === this.searchValue &&
|
||||||
|
option.expression === this.expression &&
|
||||||
|
_.isEqual(option.ciTypeIds, this.selectCITypeIds)
|
||||||
|
) {
|
||||||
|
needDeleteList.push(item.id)
|
||||||
|
} else {
|
||||||
|
differentList.push(item.id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (differentList.length >= 10) {
|
||||||
|
needDeleteList.push(...differentList.slice(9))
|
||||||
|
}
|
||||||
|
if (needDeleteList.length) {
|
||||||
|
await Promise.all(
|
||||||
|
needDeleteList.map((id) => deletePreferenceSearch(id))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciTypeNames = this.selectCITypeIds.map((id) => {
|
||||||
|
const ciType = this.CITypes.find((item) => item.id === id)
|
||||||
|
return ciType?.alias || ciType?.name || id
|
||||||
|
})
|
||||||
|
|
||||||
|
await savePreferenceSearch({
|
||||||
|
option: {
|
||||||
|
searchValue: this.searchValue,
|
||||||
|
expression: this.expression,
|
||||||
|
ciTypeIds: this.selectCITypeIds,
|
||||||
|
ciTypeNames
|
||||||
|
},
|
||||||
|
name: '__recent__'
|
||||||
|
})
|
||||||
|
this.getRecentList()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSubmit) {
|
||||||
|
this.isSearch = true
|
||||||
|
this.currentPage = 1
|
||||||
|
this.hideDetail()
|
||||||
|
this.loadInstance()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteRecent(id) {
|
||||||
|
await deletePreferenceSearch(id)
|
||||||
|
this.getRecentList()
|
||||||
|
},
|
||||||
|
|
||||||
|
async clearRecent() {
|
||||||
|
const deletePromises = this.recentList.map((item) => {
|
||||||
|
return deletePreferenceSearch(item.id)
|
||||||
|
})
|
||||||
|
await Promise.all(deletePromises)
|
||||||
|
this.getRecentList()
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadInstance() {
|
||||||
|
const { selectCITypeIds, expression, searchValue } = this
|
||||||
|
const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||||
|
const exp = expression.match(regQ) ? expression.match(regQ)[0] : null
|
||||||
|
|
||||||
|
const ciTypeIds = [...selectCITypeIds]
|
||||||
|
if (!ciTypeIds.length) {
|
||||||
|
this.CITypeGroup.forEach((item) => {
|
||||||
|
const ids = item.ci_types.map((ci_type) => ci_type.id)
|
||||||
|
ciTypeIds.push(...ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await searchCI({
|
||||||
|
q: `${ciTypeIds?.length ? `_type:(${ciTypeIds.join(';')})` : ''}${exp ? `,${exp}` : ''}${
|
||||||
|
searchValue ? `,*${searchValue}*` : ''
|
||||||
|
}`,
|
||||||
|
count: this.pageSize,
|
||||||
|
page: this.currentPage,
|
||||||
|
sort: '_type'
|
||||||
|
})
|
||||||
|
this.currentSearchValue = searchValue
|
||||||
|
|
||||||
|
this.totalNumber = res?.numfound ?? 0
|
||||||
|
if (!res?.result?.length) {
|
||||||
|
this.ciTabList = []
|
||||||
|
this.instanceList = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciTabMap = new Map()
|
||||||
|
|
||||||
|
let list = res.result
|
||||||
|
list.forEach((item) => {
|
||||||
|
const ciType = this.CITypes.find((type) => type.id === item._type)
|
||||||
|
if (ciTabMap.has(item._type)) {
|
||||||
|
ciTabMap.get(item._type).count++
|
||||||
|
} else {
|
||||||
|
ciTabMap.set(item._type, {
|
||||||
|
id: item._type,
|
||||||
|
count: 1,
|
||||||
|
title: ciType?.alias || ciType?.name || '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const mapEntries = [...ciTabMap.entries()]
|
||||||
|
const subscribedPromises = mapEntries.map((item) => {
|
||||||
|
return getSubscribeAttributes(item[0])
|
||||||
|
})
|
||||||
|
const subscribedRes = await Promise.all(subscribedPromises)
|
||||||
|
list = list.map((item) => {
|
||||||
|
const subscribedIndex = mapEntries.findIndex((mapValue) => mapValue[0] === item._type)
|
||||||
|
const subscribedAttr = subscribedRes?.[subscribedIndex]?.attributes || []
|
||||||
|
const obj = {
|
||||||
|
ci: item,
|
||||||
|
ciTypeObj: {},
|
||||||
|
attributes: subscribedAttr
|
||||||
|
}
|
||||||
|
|
||||||
|
const ciType = this.CITypes.find((type) => type.id === item._type)
|
||||||
|
obj.ciTypeObj = {
|
||||||
|
showAttrName: ciType?.show_name || ciType?.unique_key || '',
|
||||||
|
icon: ciType?.icon || '',
|
||||||
|
title: ciType?.alias || ciType?.name || '',
|
||||||
|
name: ciType?.name || '',
|
||||||
|
id: ciType.id
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj
|
||||||
|
})
|
||||||
|
|
||||||
|
this.instanceList = list
|
||||||
|
const ciTabList = [...ciTabMap.values()]
|
||||||
|
if (list?.length) {
|
||||||
|
ciTabList.unshift({
|
||||||
|
id: -1,
|
||||||
|
title: this.$t('all'),
|
||||||
|
count: list?.length
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.ciTabList = ciTabList
|
||||||
|
|
||||||
|
// 处理引用属性
|
||||||
|
const allAttr = []
|
||||||
|
subscribedRes.map((item) => {
|
||||||
|
allAttr.push(...item.attributes)
|
||||||
|
})
|
||||||
|
this.handlePerference(_.uniqBy(allAttr, 'id'))
|
||||||
|
},
|
||||||
|
|
||||||
|
handlePerference(allAttr) {
|
||||||
|
let needRequiredCIType = []
|
||||||
|
allAttr.forEach((attr) => {
|
||||||
|
if (attr?.is_reference && attr?.reference_type_id) {
|
||||||
|
needRequiredCIType.push(attr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
needRequiredCIType = _.uniq(needRequiredCIType, 'id')
|
||||||
|
|
||||||
|
if (!needRequiredCIType.length) {
|
||||||
|
this.referenceShowAttrNameMap = {}
|
||||||
|
this.referenceCIIdMap = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleReferenceShowAttrName(needRequiredCIType)
|
||||||
|
this.handleReferenceCIIdMap(needRequiredCIType)
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleReferenceShowAttrName(needRequiredCIType) {
|
||||||
|
const res = await getCITypes({
|
||||||
|
type_ids: needRequiredCIType.map((col) => col.reference_type_id).join(',')
|
||||||
|
})
|
||||||
|
|
||||||
|
const map = {}
|
||||||
|
res.ci_types.forEach((ciType) => {
|
||||||
|
map[ciType.id] = ciType?.show_name || ciType?.unique_name || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
this.referenceShowAttrNameMap = map
|
||||||
|
},
|
||||||
|
|
||||||
|
async handleReferenceCIIdMap(needRequiredCIType) {
|
||||||
|
const map = {}
|
||||||
|
this.instanceList.forEach(({ ci }) => {
|
||||||
|
needRequiredCIType.forEach((col) => {
|
||||||
|
const ids = Array.isArray(ci[col.name]) ? ci[col.name] : ci[col.name] ? [ci[col.name]] : []
|
||||||
|
if (ids.length) {
|
||||||
|
if (!map?.[col.reference_type_id]) {
|
||||||
|
map[col.reference_type_id] = {}
|
||||||
|
}
|
||||||
|
ids.forEach((id) => {
|
||||||
|
map[col.reference_type_id][id] = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!Object.keys(map).length) {
|
||||||
|
this.referenceCIIdMap = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const allRes = await Promise.all(
|
||||||
|
Object.keys(map).map((key) => {
|
||||||
|
return searchCI({
|
||||||
|
q: `_type:${key},_id:(${Object.keys(map[key]).join(';')})`,
|
||||||
|
count: 9999
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
allRes.forEach((res) => {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
if (map?.[item._type]?.[item._id]) {
|
||||||
|
map[item._type][item._id] = item
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.referenceCIIdMap = map
|
||||||
|
},
|
||||||
|
|
||||||
|
clickRecent(data) {
|
||||||
|
this.updateAllAttributesList(data.ciTypeIds || [])
|
||||||
|
this.isSearch = true
|
||||||
|
this.currentPage = 1
|
||||||
|
this.searchValue = data?.searchValue || ''
|
||||||
|
this.expression = data?.expression || ''
|
||||||
|
this.selectCITypeIds = data?.ciTypeIds || []
|
||||||
|
|
||||||
|
this.hideDetail()
|
||||||
|
this.loadInstance()
|
||||||
|
},
|
||||||
|
|
||||||
|
handlePageSizeChange(_, pageSize) {
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.currentPage = 1
|
||||||
|
this.loadInstance()
|
||||||
|
},
|
||||||
|
|
||||||
|
changePage(page) {
|
||||||
|
this.currentPage = page
|
||||||
|
this.loadInstance()
|
||||||
|
},
|
||||||
|
|
||||||
|
changeFilter(data) {
|
||||||
|
this[data.name] = data.value
|
||||||
|
},
|
||||||
|
|
||||||
|
showDetail(data) {
|
||||||
|
this.detailCIId = data.id
|
||||||
|
this.detailCITypeId = data.ciTypeId
|
||||||
|
this.showInstanceDetail = true
|
||||||
|
},
|
||||||
|
|
||||||
|
hideDetail() {
|
||||||
|
this.detailCIId = -1
|
||||||
|
this.detailCITypeId = -1
|
||||||
|
this.showInstanceDetail = false
|
||||||
|
},
|
||||||
|
|
||||||
|
async addCollect(data) {
|
||||||
|
if (this?.favorList?.length >= 10) {
|
||||||
|
const deletePromises = this.favorList.slice(9).map((item) => {
|
||||||
|
return deletePreferenceSearch(item.id)
|
||||||
|
})
|
||||||
|
await Promise.all(deletePromises)
|
||||||
|
}
|
||||||
|
await savePreferenceSearch({
|
||||||
|
option: {
|
||||||
|
...data
|
||||||
|
},
|
||||||
|
name: '__favor__'
|
||||||
|
})
|
||||||
|
this.getFavorList()
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteCollect(id) {
|
||||||
|
await deletePreferenceSearch(id)
|
||||||
|
this.getFavorList()
|
||||||
|
},
|
||||||
|
|
||||||
|
clickFavor(data) {
|
||||||
|
this.isSearch = true
|
||||||
|
this.showDetail(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.resource-search {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&-before {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 725px;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: 100px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-bg {
|
||||||
|
position: absolute;
|
||||||
|
left: -24px;
|
||||||
|
bottom: -24px;
|
||||||
|
width: calc(100% + 48px);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
|
||||||
|
&-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1D2129;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-after {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&-left {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-right {
|
||||||
|
margin-left: 20px;
|
||||||
|
width: calc(30% - 20px);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #E4E7ED;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-pagination {
|
||||||
|
text-align: right;
|
||||||
|
margin: 12px 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue