Detail.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. <template>
  2. <detail
  3. :on-manager="onManager"
  4. :data="data"
  5. :base-info="baseInfo"
  6. :extra-info="extraInfo"
  7. :name-rules="[{ required: true, message: $t('compute.text_210') }]"
  8. resource="hosts"
  9. status-module="host" />
  10. </template>
  11. <script>
  12. import {
  13. getUserTagColumn,
  14. getExtTagColumn,
  15. } from '@/utils/common/detailColumn'
  16. import WindowsMixin from '@/mixins/windows'
  17. import { getEnabledTableColumn, getBrandTableColumn, getCopyWithContentTableColumn, getStatusTableColumn, getPublicScopeTableColumn, getOsArch } from '@/utils/common/tableColumn'
  18. import { sizestr } from '@/utils/utils'
  19. import i18n from '@/locales'
  20. const storageType = {
  21. rotate: i18n.t('compute.text_47'),
  22. ssd: i18n.t('compute.text_48'),
  23. hybrid: i18n.t('compute.text_578'),
  24. }
  25. export default {
  26. name: 'HostDetail',
  27. mixins: [WindowsMixin],
  28. props: {
  29. data: {
  30. type: Object,
  31. required: true,
  32. },
  33. onManager: {
  34. type: Function,
  35. required: true,
  36. },
  37. columns: {
  38. type: Array,
  39. required: true,
  40. },
  41. refresh: {
  42. type: Function,
  43. required: true,
  44. },
  45. },
  46. data () {
  47. const baseInfo = [
  48. {
  49. field: 'hostname',
  50. title: this.$t('common_388'),
  51. },
  52. getUserTagColumn({
  53. onManager: this.onManager,
  54. resource: 'host',
  55. columns: () => this.columns,
  56. tipName: this.$t('dictionary.host'),
  57. editCheck: (row) => (row.provider || '').toLowerCase() !== 'bingocloud',
  58. }),
  59. getExtTagColumn({
  60. onManager: this.onManager,
  61. resource: 'host',
  62. columns: () => this.columns,
  63. tipName: this.$t('dictionary.host'),
  64. editCheck: (row) => (row.provider || '').toLowerCase() !== 'bingocloud',
  65. }),
  66. getPublicScopeTableColumn({ vm: this, resource: 'hosts' }),
  67. getBrandTableColumn(),
  68. getEnabledTableColumn(),
  69. {
  70. field: 'access_ip',
  71. title: 'IP',
  72. slots: {
  73. default: ({ row, cellValue }) => {
  74. const ret = [
  75. <list-body-cell-wrap copy row={ row } field="access_ip" title={ cellValue } />,
  76. ]
  77. if (row.public_ip) {
  78. ret.push(
  79. <list-body-cell-wrap copy row={ row } field="public_ip" title={ cellValue } />,
  80. )
  81. }
  82. return ret
  83. },
  84. },
  85. },
  86. {
  87. field: 'access_mac',
  88. title: this.$t('compute.text_385'),
  89. slots: {
  90. default: ({ row, cellValue }) => {
  91. return [
  92. <list-body-cell-wrap copy row={ row } field="access_mac" title={ cellValue } />,
  93. ]
  94. },
  95. },
  96. },
  97. getStatusTableColumn({ field: 'host_status', statusModule: 'host_status', title: this.$t('compute.text_502') }),
  98. {
  99. field: 'alert_data',
  100. title: (h) => [
  101. <span style="margin-right:5px">{this.$t('compute.alert_status')}</span>,
  102. <help-tooltip name="alertDataTimeRange" />,
  103. ],
  104. slots: {
  105. default: () => {
  106. const state = this.alertData?.alert_state
  107. if (state) {
  108. return [<status status={state} statusModule="monitorresources" />]
  109. }
  110. return '-'
  111. },
  112. },
  113. hidden: () => this.$isScopedPolicyMenuHidden('host_hidden_columns.alert_data'),
  114. },
  115. {
  116. field: 'nonsystem_guests',
  117. title: '#VM',
  118. width: 60,
  119. slots: {
  120. default: ({ row }, h) => {
  121. if (row.nonsystem_guests <= 0) return row.nonsystem_guests
  122. const ret = [
  123. <a onClick={ () => this.$emit('tab-change', 'vminstance-list') }>{row.nonsystem_guests}</a>,
  124. ]
  125. return ret
  126. },
  127. },
  128. },
  129. {
  130. field: 'nonsystem_guests',
  131. title: '#' + this.$t('compute.host.host_type.container.title'),
  132. width: 60,
  133. slots: {
  134. default: ({ row }, h) => {
  135. if (row.nonsystem_guests <= 0) return row.nonsystem_guests
  136. const ret = [
  137. <a onClick={ () => this.$emit('tab-change', 'vminstance-list') }>{row.nonsystem_guests}</a>,
  138. ]
  139. return ret
  140. },
  141. },
  142. },
  143. {
  144. field: 'schedtags',
  145. title: this.$t('compute.text_541'),
  146. formatter: ({ cellValue, row }) => {
  147. if (row.schedtags && row.schedtags.length > 0) {
  148. const schedtags = row.schedtags.map(v => v.name)
  149. return schedtags.join(',')
  150. }
  151. return '-'
  152. },
  153. },
  154. {
  155. field: 'version',
  156. title: this.$t('compute.text_585'),
  157. slots: {
  158. default: ({ row, cellValue }) => {
  159. return [
  160. <div class='text-truncate'>
  161. <list-body-cell-wrap copy row={ row } field="version" title={ cellValue } />
  162. </div>,
  163. ]
  164. },
  165. },
  166. },
  167. {
  168. field: 'kernel_version',
  169. title: this.$t('compute.host.kernel_version.title'),
  170. formatter: ({ cellValue, row }) => {
  171. let text = '-'
  172. if (row.metadata && row.metadata.kernel_version) {
  173. text = row.metadata.kernel_version
  174. }
  175. return text
  176. },
  177. },
  178. {
  179. field: 'os_distribution',
  180. title: this.$t('compute.host.os_distribution.title'),
  181. formatter: ({ cellValue, row }) => {
  182. let text = '-'
  183. if (row.metadata && row.metadata.os_distribution) {
  184. text = row.metadata.os_distribution
  185. if (row.metadata.os_version) {
  186. text += '(' + row.metadata.os_version + ')'
  187. }
  188. }
  189. return text
  190. },
  191. },
  192. {
  193. field: 'ovs_version',
  194. title: this.$t('compute.host.ovs_version.title'),
  195. formatter: ({ cellValue, row }) => {
  196. let text = '-'
  197. if (row.metadata && row.metadata.ovs_version) {
  198. text = row.metadata.ovs_version
  199. }
  200. return text
  201. },
  202. },
  203. {
  204. field: 'ovs_kmod_version',
  205. title: this.$t('compute.host.kernel_ovs_version.title'),
  206. formatter: ({ cellValue, row }) => {
  207. let text = '-'
  208. if (row.metadata && row.metadata.ovs_kmod_version) {
  209. text = row.metadata.ovs_kmod_version
  210. }
  211. return text
  212. },
  213. },
  214. {
  215. field: 'kvm_module',
  216. title: this.$t('compute.text_586'),
  217. formatter: ({ cellData, row }) => {
  218. const kvmModuleMap = {
  219. 'kvm-intel': 'Intel',
  220. 'kvm-amd': 'AMD',
  221. buildin: 'Buildin',
  222. unsupport: this.$t('compute.text_587'),
  223. }
  224. if (!row.sys_info) return '-'
  225. return kvmModuleMap[row.sys_info.kvm_module] || '-'
  226. },
  227. },
  228. {
  229. field: 'cdrom_boot',
  230. title: this.$t('compute.text_588'),
  231. formatter: ({ cellData, row }) => {
  232. let ret = '-'
  233. if (row.ipmi_info && row.ipmi_info.cdrom_boot === 'true') {
  234. ret = this.$t('compute.text_589')
  235. } else {
  236. ret = this.$t('compute.text_587')
  237. }
  238. return ret
  239. },
  240. },
  241. {
  242. field: 'isolated_device_count',
  243. title: this.$t('compute.passthrough_device_count'),
  244. slots: {
  245. default: ({ row }, h) => {
  246. return [
  247. <a onClick={ () => this.$emit('tab-change', 'gpu-list') }>{row.isolated_device_count || 0}</a>,
  248. ]
  249. },
  250. },
  251. },
  252. {
  253. field: 'host_type',
  254. title: this.$t('compute.host.host_type.title'),
  255. formatter: ({ cellData, row }) => {
  256. let ret = '-'
  257. if (row.host_type === 'container') {
  258. ret = this.$t('compute.host.host_type.container.title')
  259. } else if (row.host_type === 'kvm' || row.host_type === 'hypervisor') {
  260. ret = this.$t('compute.host.host_type.kvm.title')
  261. } else if (row.host_type === 'baremetal') {
  262. ret = this.$t('compute.host.host_type.baremetal.title')
  263. } else if (row.host_type) {
  264. ret = row.host_type
  265. }
  266. return ret
  267. },
  268. },
  269. {
  270. field: 'enable_numa_allocate',
  271. title: this.$t('compute.host.host_enable_numa_allocate.title'),
  272. formatter: ({ cellData, row }) => {
  273. if (row.enable_numa_allocate) {
  274. return this.$t('compute.sched_numa')
  275. } else if (row.sys_info?.host_agent_cpu_numa_allocate) {
  276. return this.$t('compute.host_agent_numa')
  277. }
  278. return this.$t('table.title.off')
  279. },
  280. },
  281. {
  282. field: 'auto_migrate_on_host_down',
  283. title: this.$t('compute.host.auto_migrate_on_host.title'),
  284. formatter: ({ cellData, row }) => {
  285. let ret = this.$t('table.title.off')
  286. if (row && row.metadata) {
  287. const autoList = []
  288. if (row.metadata.auto_migrate_on_host_down) {
  289. autoList.push(this.$t('compute.host.auto_migrate_on_host_down.title'))
  290. }
  291. if (row.metadata.auto_migrate_on_host_shutdown) {
  292. autoList.push(this.$t('compute.host.auto_migrate_on_host_shutdown.title'))
  293. }
  294. if (autoList.length > 0) {
  295. ret = autoList.join(',')
  296. }
  297. }
  298. return ret
  299. },
  300. },
  301. ]
  302. return {
  303. alertData: null,
  304. // itemData: {
  305. // status: 'ready',
  306. // host_status: 'ready',
  307. // enabled: true,
  308. // },
  309. storageColumns: [
  310. {
  311. field: 'adapter',
  312. title: this.$t('compute.text_579'),
  313. width: 70,
  314. },
  315. {
  316. field: 'driver',
  317. title: this.$t('compute.text_378'),
  318. width: 140,
  319. },
  320. {
  321. field: 'model',
  322. title: this.$t('compute.text_580'),
  323. showOverflow: 'ellipsis',
  324. minWidth: 200,
  325. },
  326. {
  327. field: 'rotate',
  328. title: this.$t('compute.text_175'),
  329. width: 80,
  330. formatter: ({ cellValue, row }) => {
  331. if (cellValue === true) return this.$t('compute.text_581')
  332. return 'SSD'
  333. },
  334. },
  335. {
  336. field: 'size',
  337. title: this.$t('compute.text_397'),
  338. formatter: ({ cellValue, row }) => {
  339. return sizestr(cellValue, 'M', 1024)
  340. },
  341. },
  342. {
  343. field: 'slot',
  344. title: this.$t('compute.text_582'),
  345. },
  346. ],
  347. hostColumns: [
  348. {
  349. field: 'ip_addr',
  350. title: 'IP',
  351. width: 160,
  352. },
  353. {
  354. field: 'mac',
  355. title: this.$t('compute.text_385'),
  356. showOverflow: 'ellipsis',
  357. minWidth: 100,
  358. },
  359. {
  360. field: 'masklen',
  361. title: this.$t('compute.text_583'),
  362. },
  363. {
  364. field: 'nic_type',
  365. title: this.$t('compute.text_175'),
  366. },
  367. {
  368. field: 'rate',
  369. title: this.$t('compute.text_584'),
  370. },
  371. ],
  372. baseInfo: baseInfo,
  373. extraInfo: [
  374. {
  375. title: this.$t('compute.text_590'),
  376. items: [
  377. {
  378. field: 'manufacture',
  379. title: this.$t('compute.text_228'),
  380. formatter: ({ cellValue, row }) => {
  381. return ((row.sys_info || {}).manufacture) || '-'
  382. },
  383. },
  384. {
  385. field: 'model',
  386. title: this.$t('compute.text_580'),
  387. formatter: ({ cellValue, row }) => {
  388. return ((row.sys_info || {}).model) || '-'
  389. },
  390. },
  391. getCopyWithContentTableColumn({
  392. field: 'sn',
  393. title: this.$t('compute.text_591'),
  394. }),
  395. ],
  396. },
  397. {
  398. title: 'CPU',
  399. items: [
  400. {
  401. field: 'cpu_count',
  402. title: this.$t('compute.text_563'),
  403. formatter: ({ cellValue }) => {
  404. return cellValue + this.$t('compute.text_167')
  405. },
  406. },
  407. {
  408. field: 'node_count',
  409. title: this.$t('compute.text_593'),
  410. formatter: ({ cellValue }) => {
  411. return cellValue + this.$t('compute.text_200')
  412. },
  413. },
  414. {
  415. field: 'cpu_commit_bound',
  416. title: this.$t('compute.text_594'),
  417. slots: {
  418. default: ({ row }, h) => {
  419. if (row.cpu_commit_bound) {
  420. return [
  421. <a class="mem-edit-item" onClick={this.openHostAdjustOversoldRatioDialog}>{row.cpu_commit_bound} <a class="edit-icon"><a-icon type='edit' /></a></a>,
  422. ]
  423. }
  424. return '-'
  425. },
  426. },
  427. },
  428. {
  429. field: 'cpu_virtual',
  430. title: this.$t('compute.text_1326'),
  431. formatter: ({ row }) => {
  432. if (!row.cpu_count || !row.cpu_commit_bound) {
  433. return '-'
  434. }
  435. return row.cpu_count * row.cpu_commit_bound + this.$t('compute.text_167')
  436. },
  437. },
  438. {
  439. field: 'cpu_commit_rate',
  440. title: this.$t('compute.text_595'),
  441. },
  442. {
  443. field: 'cpu_desc',
  444. title: this.$t('compute.text_596'),
  445. },
  446. getOsArch({ field: 'cpu_architecture' }),
  447. {
  448. field: 'reserved_cpu',
  449. title: this.$t('compute.passthrough_reserved'),
  450. slots: {
  451. default: ({ row }, h) => {
  452. const cpu = row.reserved_resource_for_gpu && row.reserved_resource_for_gpu.reserved_cpu
  453. if (cpu) {
  454. return [
  455. <a onClick={ () => this.$emit('tab-change', 'gpu-list') }>{this.$t('compute.text_120', [cpu])}</a>,
  456. ]
  457. }
  458. return '-'
  459. },
  460. },
  461. },
  462. {
  463. field: 'reserved_cpus_info',
  464. title: this.$t('compute.system_reserve_resource'),
  465. slots: {
  466. default: ({ row }, h) => {
  467. if (row.metadata?.reserved_cpus_info) {
  468. const ret = []
  469. const reserved_cpus_info = row.metadata?.reserved_cpus_info || '{}'
  470. const cpusInfo = JSON.parse(reserved_cpus_info).cpus || ''
  471. const processes_prefix = JSON.parse(reserved_cpus_info).processes_prefix || []
  472. const mems = JSON.parse(reserved_cpus_info).mems || ''
  473. ret.push(<div>{this.$t('compute.text_1058')}: {cpusInfo.split(',').sort().join('、')}</div>)
  474. ret.push(<div>Numa Node: {mems.split(',').sort().join('、')}</div>)
  475. ret.push(<div>{this.$t('compute.executable_file_name')}: {processes_prefix.join(', ')}</div>)
  476. return ret
  477. }
  478. return '-'
  479. },
  480. },
  481. },
  482. // {
  483. // field: 'resource_reserved_info',
  484. // title: this.$t('compute.resource_reserved_info'),
  485. // },
  486. ],
  487. },
  488. {
  489. title: this.$t('compute.text_369'),
  490. items: [
  491. {
  492. field: 'mem_size',
  493. title: this.$t('compute.text_1327'),
  494. formatter: ({ cellValue, row }) => {
  495. const allowedBrands = ['OneCloud', 'VMware']
  496. if (!allowedBrands.includes(row.brand)) return sizestr(cellValue, 'M', 1024)
  497. return this.$t('compute.text_1330') + sizestr(row.mem_commit, 'M', 1024) + this.$t('compute.text_1331') + sizestr(cellValue, 'M', 1024)
  498. },
  499. },
  500. {
  501. field: 'mem_commit_bound',
  502. title: this.$t('compute.text_594'),
  503. slots: {
  504. default: ({ row }, h) => {
  505. if (row.mem_commit_bound) return [<a class="mem-edit-item" onClick={this.openHostAdjustOversoldRatioDialog}>{row.mem_commit_bound}<a class="edit-icon"><a-icon type='edit' /></a></a>]
  506. return '-'
  507. },
  508. },
  509. },
  510. {
  511. field: 'mem_commit_rate',
  512. title: this.$t('compute.text_595'),
  513. },
  514. {
  515. field: 'mem_reserved',
  516. title: this.$t('compute.text_598'),
  517. formatter: ({ cellValue, row }) => {
  518. return sizestr(cellValue, 'M', 1024)
  519. },
  520. },
  521. {
  522. field: 'reserved_memory',
  523. title: this.$t('compute.passthrough_reserved'),
  524. slots: {
  525. default: ({ row }, h) => {
  526. const memory = row.reserved_resource_for_gpu && row.reserved_resource_for_gpu.reserved_memory
  527. if (memory) {
  528. return [
  529. <a onClick={ () => this.$emit('tab-change', 'gpu-list') }>{ sizestr(memory, 'M', 1024) }</a>,
  530. ]
  531. }
  532. return '-'
  533. },
  534. },
  535. },
  536. {
  537. field: 'page_size_kb',
  538. title: this.$t('compute.host.hugepage_config.title'),
  539. formatter: ({ cellValue, row }) => {
  540. let ret = this.$t('table.title.off')
  541. if (row && row.metadata && row.metadata.hugepages_option) {
  542. ret = row.metadata.hugepages_option
  543. }
  544. if (row.page_size_kb > 4) {
  545. ret += '(' + sizestr(row.page_size_kb, 'K', 1024) + ')'
  546. }
  547. return ret
  548. },
  549. },
  550. {
  551. field: 'enable_ksm',
  552. title: 'KSM',
  553. formatter: ({ cellValue, row }) => {
  554. let ret = this.$t('table.title.off')
  555. if (row && row.metadata && row.metadata.enable_ksm === 'true') {
  556. ret = this.$t('table.title.on')
  557. }
  558. return ret
  559. },
  560. },
  561. ],
  562. },
  563. {
  564. title: this.$t('compute.text_99'),
  565. items: [
  566. {
  567. field: 'metadata',
  568. title: this.$t('compute.text_1329'),
  569. formatter: ({ cellValue, row }) => {
  570. if (row.brand.toLowerCase() !== 'onecloud') return '-'
  571. const total = sizestr(row.metadata.root_partition_total_capacity_mb, 'M', 1024)
  572. const used = sizestr(row.root_partition_used_capacity_mb, 'M', 1024)
  573. return this.$t('compute.text_1330') + used + this.$t('compute.text_1331') + total
  574. },
  575. },
  576. {
  577. field: 'storage',
  578. title: this.$t('compute.text_1332'),
  579. formatter: ({ cellValue, row }) => {
  580. const total = sizestr(row.storage, 'M', 1024)
  581. const used = sizestr(row.actual_storage_used, 'M', 1024)
  582. const allowedBrands = ['OneCloud', 'VMware']
  583. if (!allowedBrands.includes(row.brand)) return sizestr(cellValue, 'M', 1024)
  584. return this.$t('compute.text_1330') + used + this.$t('compute.text_1331') + total
  585. },
  586. },
  587. {
  588. field: 'storage_type',
  589. title: this.$t('compute.text_175'),
  590. formatter: ({ cellValue, row }) => {
  591. return storageType[cellValue]
  592. },
  593. },
  594. {
  595. field: 'storage_commit_rate',
  596. title: this.$t('compute.text_595'),
  597. },
  598. {
  599. field: 'storage_waste',
  600. title: h => {
  601. return [
  602. <span class="mr-1">{this.$t('compute.text_599')}</span>,
  603. <a-tooltip title={ this.$t('compute.text_1376') }>
  604. <a-icon type="question-circle-o" />
  605. </a-tooltip>,
  606. ]
  607. },
  608. formatter: ({ cellValue, row }) => {
  609. return sizestr(cellValue || 0, 'M', 1024)
  610. },
  611. },
  612. {
  613. field: 'reserved_storage',
  614. title: this.$t('compute.passthrough_reserved'),
  615. slots: {
  616. default: ({ row }, h) => {
  617. const storage = row.reserved_resource_for_gpu && row.reserved_resource_for_gpu.reserved_storage
  618. if (storage) {
  619. return [
  620. <a onClick={ () => this.$emit('tab-change', 'gpu-list') }>{ sizestr(storage, 'M', 1024) }</a>,
  621. ]
  622. }
  623. return '-'
  624. },
  625. },
  626. },
  627. {
  628. title: '',
  629. field: 'storage_info',
  630. slots: {
  631. default: ({ row }, h) => {
  632. return [
  633. <vxe-grid class="mb-2" data={ row.storage_info } columns={ this.storageColumns } />,
  634. ]
  635. },
  636. },
  637. },
  638. ],
  639. },
  640. {
  641. title: this.$t('compute.text_600'),
  642. field: 'nic_info',
  643. slots: {
  644. default: ({ row }, h) => {
  645. const { nic_info = [] } = row
  646. return [
  647. <vxe-grid class="mb-2" data={ nic_info.filter(nic => !nic.mac.startsWith('ff:')) } columns={ this.hostColumns } />,
  648. ]
  649. },
  650. },
  651. },
  652. ],
  653. }
  654. },
  655. watch: {
  656. 'data.id': {
  657. handler () {
  658. this.fetchAlertData()
  659. },
  660. immediate: true,
  661. },
  662. },
  663. // created () {
  664. // this.updateDetailData()
  665. // },
  666. methods: {
  667. async fetchAlertData () {
  668. this.alertData = null
  669. const id = this.data?.id
  670. if (!id) return
  671. if (this.$isScopedPolicyMenuHidden('host_hidden_columns.alert_data')) return
  672. try {
  673. const monitorManager = new this.$Manager('unifiedmonitors/resource-metrics', 'v1')
  674. const res = await monitorManager.create({
  675. data: {
  676. res_ids: [id],
  677. res_type: 'host',
  678. },
  679. })
  680. const metrics = res.data?.resource_metrics?.[id]
  681. this.alertData = metrics || null
  682. } catch (e) {
  683. this.alertData = null
  684. }
  685. },
  686. // updateDetailData () {
  687. // const hostManager = new this.$Manager('hosts')
  688. // hostManager.get({ id: this.data.id })
  689. // .then((res) => {
  690. // this.itemData = res.data
  691. // })
  692. // },
  693. openHostAdjustOversoldRatioDialog () {
  694. this.createDialog('HostAdjustOversoldRatioDialog', {
  695. data: [this.data],
  696. columns: this.columns,
  697. onManager: this.onManager,
  698. name: this.$t('dictionary.host'),
  699. refresh: this.refresh,
  700. })
  701. },
  702. },
  703. }
  704. </script>
  705. <style lang="less">
  706. .mem-edit-item {
  707. .edit-icon {
  708. display: none;
  709. }
  710. &:hover .edit-icon {
  711. display: inline;
  712. }
  713. }
  714. </style>