bfg-common 1.3.322 → 1.3.324

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,694 +1,697 @@
1
- <template>
2
- <div class="virtual-hardware">
3
- <div class="clr-row clr-justify-content-end">
4
- <atoms-dropdown-tree
5
- :title="localization.addNewDevice"
6
- :items="dropdownItems"
7
- test-id="add-device-dropdown"
8
- @select="onAddDevice"
9
- />
10
- </div>
11
-
12
- <div class="clr-component">
13
- <div class="stack-view">
14
- <common-vm-actions-common-customize-hardware-virtual-hardware-cpu
15
- :max-cpus="props.maxCpus"
16
- :cpu-models="props.cpuModels"
17
- :cpu="props.cpu"
18
- :is-edit="props.isEdit"
19
- :state="props.state"
20
- :error-validation-fields="props.errorValidationFields"
21
- :vm-cpu-help-text-second="props.vmCpuHelpTextSecond"
22
- @send-data="onSendDataCpuMethod"
23
- @remove-error-by-title="emits('remove-error-by-title', $event)"
24
- @invalid="cpuInvalid = $event"
25
- />
26
- <common-vm-actions-common-customize-hardware-virtual-hardware-memory
27
- :max-memory="props.maxMemory"
28
- :is-edit="props.isEdit"
29
- :memory="props.memory"
30
- :state="props.state"
31
- :error-validation-fields="props.errorValidationFields"
32
- @send-data="onSendDataMemoryMethod"
33
- @invalid="memoryInvalid = $event"
34
- @remove-error-by-title="emits('remove-error-by-title', $event)"
35
- />
36
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk
37
- v-for="(item, key) in hardDisks"
38
- :key="hardDisksIndex[key]"
39
- :index="hardDisksIndex[key]"
40
- :type="hardDisksType[key]"
41
- :storage="props.storage"
42
- :hard-disk="item"
43
- :error-validation-fields="props.errorValidationFields"
44
- :get-datastore-table-func="props.getDatastoreTableFunc"
45
- :datastore="props.datastore"
46
- :is-edit="props.isEdit"
47
- :state="props.state"
48
- @remove="onRemoveHardDisk(hardDisksIndex[key], item)"
49
- @roll-back="onRollBackHardDisk(hardDisksIndex[key])"
50
- @send-data="onSendDataNewHardDiskMethod($event, hardDisksIndex[key])"
51
- @invalid="onSetInvalidHardDisk($event, hardDisksIndex[key])"
52
- @remove-error-by-title="emits('remove-error-by-title', $event)"
53
- />
54
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-network
55
- v-for="(item, key) in networks"
56
- :key="networksIndex[key]"
57
- :index="networksIndex[key]"
58
- :type="networksType[key]"
59
- :network="item"
60
- :networks-table="props.networksTable"
61
- :error-validation-fields="props.errorValidationFields"
62
- :is-edit="props.isEdit"
63
- :state="props.state"
64
- @remove="onRemoveNetwork(networksIndex[key])"
65
- @send-data="sendDataNewNetworkMethod($event, networksIndex[key])"
66
- @invalid="onSetInvalidNetwork($event, networksIndex[key])"
67
- @remove-error-by-title="emits('remove-error-by-title', $event)"
68
- @get-networks-table="emits('get-networks-table', $event)"
69
- />
70
- <common-vm-actions-common-customize-hardware-virtual-hardware-cd-dvd-drive
71
- v-for="(item, key) in cdDvdDrives"
72
- :key="cdDvdDrivesIndex[key]"
73
- :index="cdDvdDrivesIndex[key]"
74
- :hard-disks-count="hardDisks.length"
75
- :type="cdDvdDrivesType[key]"
76
- :cd-dvd-drive="item"
77
- :nodes="props.nodes"
78
- :files="props.files"
79
- :error-validation-fields="props.errorValidationFields"
80
- :is-edit="props.isEdit"
81
- :state="props.state"
82
- @remove-error-by-title="emits('remove-error-by-title', $event)"
83
- @get-storage="emits('get-storage', $event)"
84
- @get-folders-or-files="emits('get-folders-or-files', $event)"
85
- @get-active-device-child="emits('get-active-device-child', $event)"
86
- @show-datastore-child="emits('show-datastore-child', $event)"
87
- @remove="onRemoveCdDvdDrive(cdDvdDrivesIndex[key], item)"
88
- @roll-back="onRollBackCdDvdDrive(cdDvdDrivesIndex[key])"
89
- @send-data="
90
- sendDataNewCdDvdDriveMethod($event, cdDvdDrivesIndex[key])
91
- "
92
- />
93
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-usb-controller
94
- :usb-controller="props.usbController"
95
- :is-edit="props.isEdit"
96
- :state="props.state"
97
- @send-data="onSendDataNewUsbControllerMethod"
98
- />
99
- <template v-if="props.passthroughDevices">
100
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-pci-device
101
- v-for="(item, key) in pciDevices"
102
- :key="pciDevicesIndex[key]"
103
- :index="pciDevicesIndex[key]"
104
- :pci-device="item"
105
- :error-validation-fields="props.errorValidationFields"
106
- :passthrough-devices="props.passthroughDevices"
107
- :type="pciDevicesType[key]"
108
- :is-edit="props.isEdit"
109
- :state="props.state"
110
- @remove="onRemovePciDevice(pciDevicesIndex[key])"
111
- @send-data="
112
- onSendDataPciDevicesMethod($event, pciDevicesIndex[key])
113
- "
114
- @invalid="onSetInvalidPciDevice($event, pciDevicesIndex[key])"
115
- @remove-error-by-title="emits('remove-error-by-title', $event)"
116
- />
117
- </template>
118
- <common-vm-actions-common-customize-hardware-virtual-hardware-video-card
119
- :is-edit="props.isEdit"
120
- :video-card="props.videoCard"
121
- :error-validation-fields="props.errorValidationFields"
122
- :state="props.state"
123
- @send-data="onSendDataVideoCardMethod"
124
- @invalid="videoCardInvalid = $event"
125
- @remove-error-by-title="emits('remove-error-by-title', $event)"
126
- />
127
- <common-vm-actions-common-customize-hardware-virtual-hardware-other />
128
- </div>
129
- </div>
130
-
131
- <Teleport to="body">
132
- <common-vm-actions-common-customize-hardware-virtual-hardware-browse-view
133
- v-if="isShowFileModal"
134
- :show="isShowFileModal"
135
- :nodes="props.nodes"
136
- :files="props.files"
137
- @submit="onAddExistHardDisk"
138
- @get-storage="emits('get-storage', $event)"
139
- @get-folders-or-files="emits('get-folders-or-files', $event)"
140
- @get-active-device-child="emits('get-active-device-child', $event)"
141
- @show-datastore-child="emits('show-datastore-child', $event)"
142
- @hide="isShowFileModal = false"
143
- />
144
- </Teleport>
145
- </div>
146
- </template>
147
-
148
- <script setup lang="ts">
149
- import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
150
- import { UI_I_NetworkTableItem } from '~/lib/models/store/network/interfaces'
151
- import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
152
- import { UI_I_FolderOrFileTreePayload } from '~/lib/models/store/storage/interfaces'
153
- import { UI_I_FileTreeNode } from '~/components/lib/models/interfaces'
154
- import {
155
- UI_I_SendDataCpu,
156
- UI_I_SendDataMemory,
157
- UI_I_SendDataNewHardDisk,
158
- UI_I_SendDataNewNetwork,
159
- UI_I_SendDataVideoCard,
160
- UI_I_InvalidKeys,
161
- UI_I_SendDataVirtualHardware,
162
- UI_I_SendDataNewCdDvdDrive,
163
- // UI_I_HardDisk,
164
- UI_I_SendDataNewPciDevice,
165
- } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
166
- import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
167
- import {
168
- UI_T_HardDiskType,
169
- UI_T_NetworkType,
170
- UI_T_CdDvdType,
171
- } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
172
- import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
173
- import {
174
- UI_I_DropdownTreeItem,
175
- UI_I_DropdownTreeItemChild,
176
- } from '~/components/atoms/dropdown/tree/lib/models/interfaces'
177
- import { UI_I_Localization } from '~/lib/models/interfaces'
178
- import {
179
- API_UI_I_VmEditCpu,
180
- API_UI_I_VmEditMemory,
181
- UI_I_PciDevice,
182
- } from '~/lib/models/store/vm/interfaces'
183
- import { dropdownItemsFunc } from './lib/config/dropdownItems'
184
-
185
- const props = defineProps<{
186
- storage: UI_I_DatastoreTableItem | null
187
- cpuModels: UI_I_OptionItem[]
188
- vmName: string
189
- maxCpus: number
190
- maxMemory: number
191
- isEdit: boolean
192
- isClone: boolean
193
- errorValidationFields: UI_I_ErrorValidationField<string>[]
194
- nodes: UI_I_FileTreeNode[]
195
- files: UI_I_FileTreeNode[]
196
- networksTable: UI_I_NetworkTableItem[]
197
- getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
198
- datastore: UI_I_DatastoreTableItem[]
199
- state?: string | number
200
- cpu?: API_UI_I_VmEditCpu
201
- memory?: API_UI_I_VmEditMemory
202
- vmCpuHelpTextSecond?: string
203
- hardDisks?: UI_I_SendDataNewHardDisk[] | null
204
- cdDvdDrives?: UI_I_SendDataNewCdDvdDrive[] | null
205
- networks?: UI_I_SendDataNewNetwork[] | null
206
- videoCard?: UI_I_SendDataVideoCard
207
- usbController?: string
208
- pciDevices?: UI_I_SendDataNewPciDevice[]
209
- passthroughDevices?: UI_I_PciDevice[]
210
- }>()
211
- const emits = defineEmits<{
212
- (event: 'send-data', value: UI_I_SendDataVirtualHardware): void
213
- (event: 'invalid', value: string[]): void
214
- (event: 'get-storage', value: UI_I_TablePayload): void
215
- (event: 'get-folders-or-files', value: UI_I_FolderOrFileTreePayload): void
216
- (event: 'get-active-device-child', value: UI_I_FileTreeNode): void
217
- (event: 'show-datastore-child', value: UI_I_FileTreeNode): void
218
- (event: 'remove-error-by-title', value: string): void
219
- (event: 'get-networks-table', value: UI_I_TablePayload): void
220
- (event: 'get-pci-devices'): void
221
- }>()
222
-
223
- const localization = computed<UI_I_Localization>(() => useLocal())
224
- const { $binary } = useNuxtApp()
225
-
226
- const dropdownItems = computed<UI_I_DropdownTreeItem[]>(() =>
227
- dropdownItemsFunc(localization.value, props.state, props.passthroughDevices)
228
- )
229
-
230
- const hardDisksIndex = ref<number[]>([0])
231
- const hardDisksType = ref<UI_T_HardDiskType[]>(['new'])
232
- const hardDisks = ref<UI_I_SendDataNewHardDisk[]>([
233
- {
234
- create: true,
235
- size: 90,
236
- source: '',
237
- boot_order: -1,
238
- },
239
- ])
240
- watch(
241
- () => props.hardDisks,
242
- (newValue) => {
243
- if ((!props.isEdit && !props.isClone) || !newValue) return
244
-
245
- const count = newValue?.length || 0
246
- hardDisksIndex.value = [...Array(count).keys()]
247
- hardDisks.value = newValue
248
- if (props.isEdit) {
249
- hardDisksType.value = Array(count).fill('edit')
250
- } else if (props.isClone) {
251
- hardDisksType.value = Array(count).fill('new')
252
- hardDisks.value = newValue.map((hardDisk) => {
253
- hardDisk.create = true
254
- return hardDisk
255
- })
256
- }
257
- },
258
- { immediate: true }
259
- )
260
-
261
- const addHardDisk = (
262
- create = true,
263
- size = 90,
264
- source = '',
265
- type: UI_T_HardDiskType = 'new',
266
- provisioned_type?: number // При добавлении существующей
267
- ): void => {
268
- const newIndex = (hardDisksIndex.value.at(-1) ?? -1) + 1
269
- hardDisksIndex.value.push(newIndex)
270
- hardDisksType.value.push(type)
271
-
272
- let provisionType = 'thick'
273
- if (provisioned_type === 1) provisionType = 'thin'
274
-
275
- hardDisks.value.push({
276
- create,
277
- size,
278
- source,
279
- attach: true,
280
- boot_order: 0, // Убираем галочку
281
- provision_type: provisionType,
282
- })
283
- sendData()
284
- }
285
- const onAddExistHardDisk = (file: UI_I_FileTreeNode): void => {
286
- addHardDisk(
287
- false,
288
- $binary.bToGb(file.size),
289
- file.path,
290
- 'exist',
291
- file.provisioned_type
292
- )
293
- }
294
- const onRemoveHardDisk = (
295
- index: number,
296
- hardDisk: UI_I_SendDataNewHardDisk
297
- ): void => {
298
- const removeIndex = hardDisksIndex.value.indexOf(index)
299
-
300
- if (!hardDisk.create && !hardDisk.attach) {
301
- hardDisksType.value = hardDisksType.value.map((item, key) => {
302
- if (key === removeIndex) return 'removed'
303
- return item
304
- })
305
- sendData()
306
- return
307
- }
308
-
309
- hardDisksIndex.value = hardDisksIndex.value.filter((item) => item !== index)
310
- hardDisksType.value = hardDisksType.value.filter(
311
- (_item, key) => key !== removeIndex
312
- )
313
-
314
- hardDisks.value = hardDisks.value.filter((_hardDisk, key: number) => {
315
- return removeIndex !== key
316
- })
317
-
318
- delete newHardDiskInvalidKeys.value[index]
319
- delete sendDataNewHardDisk.value[index]
320
- sendData()
321
- }
322
- const onRollBackHardDisk = (index: number): void => {
323
- const removeIndex = hardDisksIndex.value.indexOf(index)
324
- hardDisksType.value = hardDisksType.value.map((item, key) => {
325
- if (key === removeIndex) return 'edit'
326
- return item
327
- })
328
- }
329
-
330
- const defaultNetwork = {
331
- network: '',
332
- net_bridge: '',
333
- mac: '',
334
- target: '',
335
- model: '',
336
- boot_order: -1,
337
- }
338
- const networks = ref<UI_I_SendDataNewNetwork[]>([useDeepCopy(defaultNetwork)])
339
- const networksType = ref<UI_T_NetworkType[]>(['new'])
340
- const networksIndex = ref<number[]>([0])
341
- watch(
342
- () => props.networks,
343
- (newValue) => {
344
- if ((!props.isEdit && !props.isClone) || !newValue) return
345
-
346
- // networks.value = newValue
347
- // networksType.value = Array(newValue.length).fill('edit')
348
- const count = newValue?.length || 0
349
- networksIndex.value = [...Array(count).keys()]
350
- networks.value = newValue
351
- if (props.isEdit) {
352
- networksType.value = Array(count).fill('edit')
353
- } else if (props.isClone) {
354
- networksType.value = Array(count).fill('new')
355
- }
356
- },
357
- { immediate: true }
358
- )
359
- const addNetwork = (): void => {
360
- const index = (networksIndex.value.at(-1) || 0) + 1
361
- networksIndex.value.push(index)
362
- networksType.value.push('new')
363
-
364
- networks.value.push({
365
- ...useDeepCopy(defaultNetwork),
366
- boot_order: 0, // Убираем галочку
367
- })
368
- }
369
- const onRemoveNetwork = (index: number): void => {
370
- const removeIndex = networksIndex.value.indexOf(index)
371
- networksIndex.value = networksIndex.value.filter((item) => item !== index)
372
- networksType.value = networksType.value.filter(
373
- (_item, key) => key !== removeIndex
374
- )
375
- networks.value = networks.value.filter(
376
- (_network, key: number) => removeIndex !== key
377
- )
378
-
379
- delete newNetworkInvalidKeys.value[index]
380
- delete sendDataNewNetwork.value[index]
381
- sendData()
382
- }
383
-
384
- const defaultCdDvdDriver: UI_I_SendDataNewCdDvdDrive = {
385
- create: false,
386
- attach: false,
387
- source: '',
388
- bus: '',
389
- device_type: 'cdrom',
390
- boot_order: -1,
391
- }
392
- const cdDvdDrives = ref<UI_I_SendDataNewCdDvdDrive[]>([
393
- useDeepCopy(defaultCdDvdDriver),
394
- ])
395
- const cdDvdDrivesIndex = ref<number[]>([0])
396
- const cdDvdDrivesType = ref<UI_T_CdDvdType[]>(['new'])
397
- watch(
398
- () => props.cdDvdDrives,
399
- (newValue) => {
400
- if ((!props.isEdit && !props.isClone) || !newValue) return
401
-
402
- // cdDvdDrives.value = newValue
403
- // cdDvdDrivesType.value = Array(newValue.length).fill('edit')
404
- const count = newValue?.length || 0
405
- cdDvdDrivesIndex.value = [...Array(count).keys()]
406
- cdDvdDrives.value = newValue
407
- if (props.isEdit) {
408
- cdDvdDrivesType.value = Array(count).fill('edit')
409
- } else if (props.isClone) {
410
- cdDvdDrivesType.value = Array(count).fill('new')
411
- }
412
- },
413
- { immediate: true }
414
- )
415
- const addCdDvdDrive = (): void => {
416
- const value = (cdDvdDrivesIndex.value.at(-1) || 0) + 1
417
- cdDvdDrivesIndex.value.push(value)
418
- cdDvdDrivesType.value.push('new')
419
- cdDvdDrives.value.push({
420
- ...useDeepCopy(defaultCdDvdDriver),
421
- attach: true,
422
- boot_order: 0, // Убираем галочку
423
- })
424
- }
425
- const onRemoveCdDvdDrive = (
426
- index: number,
427
- cdDvdDrive: UI_I_SendDataNewCdDvdDrive
428
- ): void => {
429
- const removeIndex = cdDvdDrivesIndex.value.indexOf(index)
430
-
431
- // if (!cdDvdDrive.create && !cdDvdDrive.attach) {
432
- if (cdDvdDrivesType.value[index] === 'edit' && !cdDvdDrive.attach) {
433
- cdDvdDrivesType.value = cdDvdDrivesType.value.map((item, key) => {
434
- if (key === removeIndex) return 'removed'
435
- return item
436
- })
437
- sendData()
438
- return
439
- }
440
-
441
- cdDvdDrivesIndex.value = cdDvdDrivesIndex.value.filter(
442
- (cdDvdDrive) => cdDvdDrive !== index
443
- )
444
- cdDvdDrivesType.value = cdDvdDrivesType.value.filter(
445
- (_item, key) => key !== index
446
- )
447
- cdDvdDrives.value = cdDvdDrives.value.filter(
448
- (_cdDvdDrive, key: number) => removeIndex !== key
449
- )
450
-
451
- delete sendDataNewCdDvdDrive.value[index]
452
- sendData()
453
- }
454
- const onRollBackCdDvdDrive = (index: number): void => {
455
- const removeIndex = cdDvdDrivesIndex.value.indexOf(index)
456
- cdDvdDrivesType.value = cdDvdDrivesType.value.map((item, key) => {
457
- if (key === removeIndex) return 'edit'
458
- return item
459
- })
460
- }
461
-
462
- const defaultPciDevice: UI_I_SendDataNewPciDevice = {
463
- address: '',
464
- vendor_name: '',
465
- class_name: 'cdrom',
466
- device_name: 'cdrom',
467
- }
468
- const pciDevicesIndex = ref<number[]>([0])
469
- const pciDevicesType = ref<('new' | 'edit' | 'removed')[]>(['new'])
470
- const pciDevices = ref<UI_I_SendDataNewPciDevice[]>([])
471
- watch(
472
- () => props.pciDevices,
473
- (newValue) => {
474
- if ((!props.isEdit && !props.isClone) || !newValue) return
475
-
476
- const count = newValue?.length || 0
477
- pciDevicesIndex.value = [...Array(count).keys()]
478
- pciDevices.value = newValue || []
479
- if (props.isEdit) {
480
- pciDevicesType.value = Array(count).fill('edit')
481
- } else if (props.isClone) {
482
- pciDevicesType.value = Array(count).fill('new')
483
- }
484
- },
485
- { immediate: true }
486
- )
487
-
488
- const addPciDevice = (): void => {
489
- const newIndex = (pciDevicesIndex.value.at(-1) ?? -1) + 1
490
- pciDevicesIndex.value.push(newIndex)
491
- pciDevicesType.value.push('new')
492
-
493
- pciDevices.value.push(useDeepCopy(defaultPciDevice))
494
- }
495
- const onRemovePciDevice = (index: number): void => {
496
- const removeIndex = pciDevicesIndex.value.indexOf(index)
497
- pciDevicesIndex.value = pciDevicesIndex.value.filter((item) => item !== index)
498
- pciDevicesType.value = pciDevicesType.value.filter(
499
- (_item, key) => key !== removeIndex
500
- )
501
-
502
- pciDevices.value = pciDevices.value.filter((_pciDevice, key: number) => {
503
- return removeIndex !== key
504
- })
505
-
506
- delete newPciDeviceInvalidKeys.value[index]
507
- delete sendDataNewPciDevices.value[index]
508
- sendData()
509
- }
510
-
511
- const isShowFileModal = ref<boolean>(false)
512
- const onAddDevice = (item: UI_I_DropdownTreeItemChild): void => {
513
- switch (item.value) {
514
- case 1:
515
- addHardDisk()
516
- break
517
- case 2:
518
- isShowFileModal.value = true
519
- break
520
- case 5:
521
- addCdDvdDrive()
522
- break
523
- case 10:
524
- addPciDevice()
525
- break
526
- case 14:
527
- addNetwork()
528
- break
529
- }
530
- }
531
-
532
- const cpuInvalid = ref<boolean>(false)
533
- const sendDataCpu = ref<UI_I_SendDataCpu | null>(null)
534
- const onSendDataCpuMethod = (data: UI_I_SendDataCpu): void => {
535
- sendDataCpu.value = data
536
- sendData()
537
- }
538
- const sendDataMemory = ref<UI_I_SendDataMemory | null>(null)
539
- const onSendDataMemoryMethod = (data: UI_I_SendDataMemory): void => {
540
- sendDataMemory.value = data
541
- sendData()
542
- }
543
- const sendDataNewHardDisk = ref<UI_I_SendDataNewHardDisk[]>([])
544
- const onSendDataNewHardDiskMethod = (
545
- data: UI_I_SendDataNewHardDisk,
546
- index: number
547
- ): void => {
548
- sendDataNewHardDisk.value[index] = data
549
- sendData()
550
- }
551
- const sendDataNewNetwork = ref<UI_I_SendDataNewNetwork[]>([])
552
- const sendDataNewNetworkMethod = (
553
- data: UI_I_SendDataNewNetwork,
554
- index: number
555
- ): void => {
556
- sendDataNewNetwork.value[index] = data
557
- sendData()
558
- }
559
- const sendDataNewCdDvdDrive = ref<UI_I_SendDataNewCdDvdDrive[]>([])
560
- const sendDataNewCdDvdDriveMethod = (
561
- data: UI_I_SendDataNewCdDvdDrive,
562
- index: number
563
- ): void => {
564
- sendDataNewCdDvdDrive.value[index] = data
565
- sendData()
566
- }
567
- const sendDataNewUsbController = ref<string | null>(null)
568
- const onSendDataNewUsbControllerMethod = (data: string): void => {
569
- sendDataNewUsbController.value = data
570
- sendData()
571
- }
572
- const sendDataNewPciDevices = ref<UI_I_SendDataNewPciDevice[]>([])
573
- const onSendDataPciDevicesMethod = (
574
- data: UI_I_SendDataNewPciDevice,
575
- index: number
576
- ): void => {
577
- sendDataNewPciDevices.value[index] = data
578
- sendData()
579
- }
580
- const sendDataVideoCard = ref<UI_I_SendDataVideoCard | null>(null)
581
- const onSendDataVideoCardMethod = (data: UI_I_SendDataVideoCard): void => {
582
- sendDataVideoCard.value = data
583
- sendData()
584
- }
585
-
586
- const sendData = (): void => {
587
- const cpu = sendDataCpu.value
588
- const memory = sendDataMemory.value
589
- const hardDisks = sendDataNewHardDisk.value.flat(2)
590
- const networks = sendDataNewNetwork.value.flat(2)
591
- const cdDvdDrives = sendDataNewCdDvdDrive.value.flat(2)
592
- const pciDevices = sendDataNewPciDevices.value.flat()
593
- const usbController = sendDataNewUsbController.value
594
- const videoCard = sendDataVideoCard.value
595
-
596
- const sendData: UI_I_SendDataVirtualHardware = {
597
- cpu,
598
- memory,
599
- usbController,
600
- pciDevices,
601
- networks,
602
- hardDisks,
603
- cdDvdDrives,
604
- videoCard,
605
- }
606
- emits('send-data', sendData)
607
- }
608
-
609
- const onSetInvalidHardDisk = (invalid: boolean, index: number): void => {
610
- newHardDiskInvalidKeys.value[index] = invalid
611
- }
612
-
613
- const onSetInvalidNetwork = (invalid: boolean, key: number): void => {
614
- newNetworkInvalidKeys.value[key] = invalid
615
- }
616
-
617
- const onSetInvalidPciDevice = (invalid: boolean, key: number): void => {
618
- newPciDeviceInvalidKeys.value[key] = invalid
619
- }
620
-
621
- const memoryInvalid = ref<boolean>(false)
622
- const videoCardInvalid = ref<boolean>(false)
623
- const newHardDiskInvalidKeys = ref<UI_I_InvalidKeys>({})
624
- const newNetworkInvalidKeys = ref<UI_I_InvalidKeys>({})
625
- const newPciDeviceInvalidKeys = ref<UI_I_InvalidKeys>({})
626
-
627
- watch(
628
- [
629
- cpuInvalid,
630
- memoryInvalid,
631
- newHardDiskInvalidKeys,
632
- newNetworkInvalidKeys,
633
- newPciDeviceInvalidKeys,
634
- videoCardInvalid,
635
- ],
636
- () => {
637
- const cpu = cpuInvalid.value ? localization.value.cpu : ''
638
- const memory = memoryInvalid.value ? localization.value.memory : ''
639
- const videoCard = videoCardInvalid.value ? localization.value.videoCard : ''
640
- let newHardDisk = ''
641
- Object.keys(newHardDiskInvalidKeys.value).forEach((key) => {
642
- if (!newHardDiskInvalidKeys.value[+key]) {
643
- return
644
- }
645
-
646
- newHardDisk +=
647
- (newHardDisk ? ', ' : '') +
648
- localization.value.newHardDisk +
649
- ' ' +
650
- (+key + 1)
651
- })
652
- let newNetwork = ''
653
- Object.keys(newNetworkInvalidKeys.value).forEach((key) => {
654
- if (!newNetworkInvalidKeys.value[+key]) {
655
- return
656
- }
657
-
658
- newNetwork +=
659
- (newNetwork ? ', ' : '') +
660
- localization.value.newNetwork +
661
- ' ' +
662
- (+key + 1)
663
- })
664
- // let pciDevice = ''
665
- // Object.keys(newPciDeviceInvalidKeys.value).forEach((key) => {
666
- // if (!newPciDeviceInvalidKeys.value[+key]) {
667
- // return
668
- // }
669
- //
670
- // pciDevice +=
671
- // (pciDevice ? ', ' : '') +
672
- // localization.value.pciDevice +
673
- // ' ' +
674
- // (+key + 1)
675
- // })
676
-
677
- emits('invalid', [cpu, memory, newHardDisk, newNetwork, videoCard])
678
- },
679
- { deep: true }
680
- )
681
-
682
- emits('get-pci-devices')
683
- </script>
684
-
685
- <style scoped lang="scss">
686
- .virtual-hardware {
687
- .content-dropdown {
688
- width: 200px;
689
- }
690
- }
691
- .stack-view {
692
- background-color: var(--block-view-bg-color);
693
- }
694
- </style>
1
+ <template>
2
+ <div class="virtual-hardware">
3
+ <div class="clr-row clr-justify-content-end">
4
+ <atoms-dropdown-tree
5
+ :title="localization.addNewDevice"
6
+ :items="dropdownItems"
7
+ test-id="add-device-dropdown"
8
+ @select="onAddDevice"
9
+ />
10
+ </div>
11
+
12
+ <div class="clr-component">
13
+ <div class="stack-view">
14
+ <common-vm-actions-common-customize-hardware-virtual-hardware-cpu
15
+ :max-cpus="props.maxCpus"
16
+ :cpu-models="props.cpuModels"
17
+ :cpu="props.cpu"
18
+ :is-edit="props.isEdit"
19
+ :state="props.state"
20
+ :error-validation-fields="props.errorValidationFields"
21
+ :vm-cpu-help-text-second="props.vmCpuHelpTextSecond"
22
+ @send-data="onSendDataCpuMethod"
23
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
24
+ @invalid="cpuInvalid = $event"
25
+ />
26
+ <common-vm-actions-common-customize-hardware-virtual-hardware-memory
27
+ :max-memory="props.maxMemory"
28
+ :is-edit="props.isEdit"
29
+ :memory="props.memory"
30
+ :state="props.state"
31
+ :error-validation-fields="props.errorValidationFields"
32
+ @send-data="onSendDataMemoryMethod"
33
+ @invalid="memoryInvalid = $event"
34
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
35
+ />
36
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk
37
+ v-for="(item, key) in hardDisks"
38
+ :key="hardDisksIndex[key]"
39
+ :index="hardDisksIndex[key]"
40
+ :type="hardDisksType[key]"
41
+ :storage="props.storage"
42
+ :hard-disk="item"
43
+ :error-validation-fields="props.errorValidationFields"
44
+ :get-datastore-table-func="props.getDatastoreTableFunc"
45
+ :datastore="props.datastore"
46
+ :is-edit="props.isEdit"
47
+ :state="props.state"
48
+ @remove="onRemoveHardDisk(hardDisksIndex[key], item)"
49
+ @roll-back="onRollBackHardDisk(hardDisksIndex[key])"
50
+ @send-data="onSendDataNewHardDiskMethod($event, hardDisksIndex[key])"
51
+ @invalid="onSetInvalidHardDisk($event, hardDisksIndex[key])"
52
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
53
+ />
54
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-network
55
+ v-for="(item, key) in networks"
56
+ :key="networksIndex[key]"
57
+ :index="networksIndex[key]"
58
+ :type="networksType[key]"
59
+ :network="item"
60
+ :networks-table="props.networksTable"
61
+ :error-validation-fields="props.errorValidationFields"
62
+ :is-edit="props.isEdit"
63
+ :state="props.state"
64
+ :project="props.project"
65
+ @remove="onRemoveNetwork(networksIndex[key])"
66
+ @send-data="sendDataNewNetworkMethod($event, networksIndex[key])"
67
+ @invalid="onSetInvalidNetwork($event, networksIndex[key])"
68
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
69
+ @get-networks-table="emits('get-networks-table', $event)"
70
+ />
71
+ <common-vm-actions-common-customize-hardware-virtual-hardware-cd-dvd-drive
72
+ v-for="(item, key) in cdDvdDrives"
73
+ :key="cdDvdDrivesIndex[key]"
74
+ :index="cdDvdDrivesIndex[key]"
75
+ :hard-disks-count="hardDisks.length"
76
+ :type="cdDvdDrivesType[key]"
77
+ :cd-dvd-drive="item"
78
+ :nodes="props.nodes"
79
+ :files="props.files"
80
+ :error-validation-fields="props.errorValidationFields"
81
+ :is-edit="props.isEdit"
82
+ :state="props.state"
83
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
84
+ @get-storage="emits('get-storage', $event)"
85
+ @get-folders-or-files="emits('get-folders-or-files', $event)"
86
+ @get-active-device-child="emits('get-active-device-child', $event)"
87
+ @show-datastore-child="emits('show-datastore-child', $event)"
88
+ @remove="onRemoveCdDvdDrive(cdDvdDrivesIndex[key], item)"
89
+ @roll-back="onRollBackCdDvdDrive(cdDvdDrivesIndex[key])"
90
+ @send-data="
91
+ sendDataNewCdDvdDriveMethod($event, cdDvdDrivesIndex[key])
92
+ "
93
+ />
94
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-usb-controller
95
+ :usb-controller="props.usbController"
96
+ :is-edit="props.isEdit"
97
+ :state="props.state"
98
+ @send-data="onSendDataNewUsbControllerMethod"
99
+ />
100
+ <template v-if="props.passthroughDevices">
101
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-pci-device
102
+ v-for="(item, key) in pciDevices"
103
+ :key="pciDevicesIndex[key]"
104
+ :index="pciDevicesIndex[key]"
105
+ :pci-device="item"
106
+ :error-validation-fields="props.errorValidationFields"
107
+ :passthrough-devices="props.passthroughDevices"
108
+ :type="pciDevicesType[key]"
109
+ :is-edit="props.isEdit"
110
+ :state="props.state"
111
+ @remove="onRemovePciDevice(pciDevicesIndex[key])"
112
+ @send-data="
113
+ onSendDataPciDevicesMethod($event, pciDevicesIndex[key])
114
+ "
115
+ @invalid="onSetInvalidPciDevice($event, pciDevicesIndex[key])"
116
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
117
+ />
118
+ </template>
119
+ <common-vm-actions-common-customize-hardware-virtual-hardware-video-card
120
+ :is-edit="props.isEdit"
121
+ :video-card="props.videoCard"
122
+ :error-validation-fields="props.errorValidationFields"
123
+ :state="props.state"
124
+ @send-data="onSendDataVideoCardMethod"
125
+ @invalid="videoCardInvalid = $event"
126
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
127
+ />
128
+ <common-vm-actions-common-customize-hardware-virtual-hardware-other />
129
+ </div>
130
+ </div>
131
+
132
+ <Teleport to="body">
133
+ <common-vm-actions-common-customize-hardware-virtual-hardware-browse-view
134
+ v-if="isShowFileModal"
135
+ :show="isShowFileModal"
136
+ :nodes="props.nodes"
137
+ :files="props.files"
138
+ @submit="onAddExistHardDisk"
139
+ @get-storage="emits('get-storage', $event)"
140
+ @get-folders-or-files="emits('get-folders-or-files', $event)"
141
+ @get-active-device-child="emits('get-active-device-child', $event)"
142
+ @show-datastore-child="emits('show-datastore-child', $event)"
143
+ @hide="isShowFileModal = false"
144
+ />
145
+ </Teleport>
146
+ </div>
147
+ </template>
148
+
149
+ <script setup lang="ts">
150
+ import { UI_T_Project } from '~/lib/models/types'
151
+ import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
152
+ import { UI_I_NetworkTableItem } from '~/lib/models/store/network/interfaces'
153
+ import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
154
+ import { UI_I_FolderOrFileTreePayload } from '~/lib/models/store/storage/interfaces'
155
+ import { UI_I_FileTreeNode } from '~/components/lib/models/interfaces'
156
+ import {
157
+ UI_I_SendDataCpu,
158
+ UI_I_SendDataMemory,
159
+ UI_I_SendDataNewHardDisk,
160
+ UI_I_SendDataNewNetwork,
161
+ UI_I_SendDataVideoCard,
162
+ UI_I_InvalidKeys,
163
+ UI_I_SendDataVirtualHardware,
164
+ UI_I_SendDataNewCdDvdDrive,
165
+ // UI_I_HardDisk,
166
+ UI_I_SendDataNewPciDevice,
167
+ } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
168
+ import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
169
+ import {
170
+ UI_T_HardDiskType,
171
+ UI_T_NetworkType,
172
+ UI_T_CdDvdType,
173
+ } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
174
+ import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
175
+ import {
176
+ UI_I_DropdownTreeItem,
177
+ UI_I_DropdownTreeItemChild,
178
+ } from '~/components/atoms/dropdown/tree/lib/models/interfaces'
179
+ import { UI_I_Localization } from '~/lib/models/interfaces'
180
+ import {
181
+ API_UI_I_VmEditCpu,
182
+ API_UI_I_VmEditMemory,
183
+ UI_I_PciDevice,
184
+ } from '~/lib/models/store/vm/interfaces'
185
+ import { dropdownItemsFunc } from './lib/config/dropdownItems'
186
+
187
+ const props = defineProps<{
188
+ storage: UI_I_DatastoreTableItem | null
189
+ cpuModels: UI_I_OptionItem[]
190
+ vmName: string
191
+ maxCpus: number
192
+ maxMemory: number
193
+ isEdit: boolean
194
+ isClone: boolean
195
+ errorValidationFields: UI_I_ErrorValidationField<string>[]
196
+ nodes: UI_I_FileTreeNode[]
197
+ files: UI_I_FileTreeNode[]
198
+ networksTable: UI_I_NetworkTableItem[]
199
+ getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
200
+ datastore: UI_I_DatastoreTableItem[]
201
+ state?: string | number
202
+ cpu?: API_UI_I_VmEditCpu
203
+ memory?: API_UI_I_VmEditMemory
204
+ vmCpuHelpTextSecond?: string
205
+ hardDisks?: UI_I_SendDataNewHardDisk[] | null
206
+ cdDvdDrives?: UI_I_SendDataNewCdDvdDrive[] | null
207
+ networks?: UI_I_SendDataNewNetwork[] | null
208
+ videoCard?: UI_I_SendDataVideoCard
209
+ usbController?: string
210
+ pciDevices?: UI_I_SendDataNewPciDevice[]
211
+ passthroughDevices?: UI_I_PciDevice[]
212
+ project: UI_T_Project
213
+ }>()
214
+ const emits = defineEmits<{
215
+ (event: 'send-data', value: UI_I_SendDataVirtualHardware): void
216
+ (event: 'invalid', value: string[]): void
217
+ (event: 'get-storage', value: UI_I_TablePayload): void
218
+ (event: 'get-folders-or-files', value: UI_I_FolderOrFileTreePayload): void
219
+ (event: 'get-active-device-child', value: UI_I_FileTreeNode): void
220
+ (event: 'show-datastore-child', value: UI_I_FileTreeNode): void
221
+ (event: 'remove-error-by-title', value: string): void
222
+ (event: 'get-networks-table', value: UI_I_TablePayload): void
223
+ (event: 'get-pci-devices'): void
224
+ }>()
225
+
226
+ const localization = computed<UI_I_Localization>(() => useLocal())
227
+ const { $binary } = useNuxtApp()
228
+
229
+ const dropdownItems = computed<UI_I_DropdownTreeItem[]>(() =>
230
+ dropdownItemsFunc(localization.value, props.state, props.passthroughDevices)
231
+ )
232
+
233
+ const hardDisksIndex = ref<number[]>([0])
234
+ const hardDisksType = ref<UI_T_HardDiskType[]>(['new'])
235
+ const hardDisks = ref<UI_I_SendDataNewHardDisk[]>([
236
+ {
237
+ create: true,
238
+ size: 90,
239
+ source: '',
240
+ boot_order: -1,
241
+ },
242
+ ])
243
+ watch(
244
+ () => props.hardDisks,
245
+ (newValue) => {
246
+ if ((!props.isEdit && !props.isClone) || !newValue) return
247
+
248
+ const count = newValue?.length || 0
249
+ hardDisksIndex.value = [...Array(count).keys()]
250
+ hardDisks.value = newValue
251
+ if (props.isEdit) {
252
+ hardDisksType.value = Array(count).fill('edit')
253
+ } else if (props.isClone) {
254
+ hardDisksType.value = Array(count).fill('new')
255
+ hardDisks.value = newValue.map((hardDisk) => {
256
+ hardDisk.create = true
257
+ return hardDisk
258
+ })
259
+ }
260
+ },
261
+ { immediate: true }
262
+ )
263
+
264
+ const addHardDisk = (
265
+ create = true,
266
+ size = 90,
267
+ source = '',
268
+ type: UI_T_HardDiskType = 'new',
269
+ provisioned_type?: number // При добавлении существующей
270
+ ): void => {
271
+ const newIndex = (hardDisksIndex.value.at(-1) ?? -1) + 1
272
+ hardDisksIndex.value.push(newIndex)
273
+ hardDisksType.value.push(type)
274
+
275
+ let provisionType = 'thick'
276
+ if (provisioned_type === 1) provisionType = 'thin'
277
+
278
+ hardDisks.value.push({
279
+ create,
280
+ size,
281
+ source,
282
+ attach: true,
283
+ boot_order: 0, // Убираем галочку
284
+ provision_type: provisionType,
285
+ })
286
+ sendData()
287
+ }
288
+ const onAddExistHardDisk = (file: UI_I_FileTreeNode): void => {
289
+ addHardDisk(
290
+ false,
291
+ $binary.bToGb(file.size),
292
+ file.path,
293
+ 'exist',
294
+ file.provisioned_type
295
+ )
296
+ }
297
+ const onRemoveHardDisk = (
298
+ index: number,
299
+ hardDisk: UI_I_SendDataNewHardDisk
300
+ ): void => {
301
+ const removeIndex = hardDisksIndex.value.indexOf(index)
302
+
303
+ if (!hardDisk.create && !hardDisk.attach) {
304
+ hardDisksType.value = hardDisksType.value.map((item, key) => {
305
+ if (key === removeIndex) return 'removed'
306
+ return item
307
+ })
308
+ sendData()
309
+ return
310
+ }
311
+
312
+ hardDisksIndex.value = hardDisksIndex.value.filter((item) => item !== index)
313
+ hardDisksType.value = hardDisksType.value.filter(
314
+ (_item, key) => key !== removeIndex
315
+ )
316
+
317
+ hardDisks.value = hardDisks.value.filter((_hardDisk, key: number) => {
318
+ return removeIndex !== key
319
+ })
320
+
321
+ delete newHardDiskInvalidKeys.value[index]
322
+ delete sendDataNewHardDisk.value[index]
323
+ sendData()
324
+ }
325
+ const onRollBackHardDisk = (index: number): void => {
326
+ const removeIndex = hardDisksIndex.value.indexOf(index)
327
+ hardDisksType.value = hardDisksType.value.map((item, key) => {
328
+ if (key === removeIndex) return 'edit'
329
+ return item
330
+ })
331
+ }
332
+
333
+ const defaultNetwork = {
334
+ network: '',
335
+ net_bridge: '',
336
+ mac: '',
337
+ target: '',
338
+ model: '',
339
+ boot_order: -1,
340
+ }
341
+ const networks = ref<UI_I_SendDataNewNetwork[]>([useDeepCopy(defaultNetwork)])
342
+ const networksType = ref<UI_T_NetworkType[]>(['new'])
343
+ const networksIndex = ref<number[]>([0])
344
+ watch(
345
+ () => props.networks,
346
+ (newValue) => {
347
+ if ((!props.isEdit && !props.isClone) || !newValue) return
348
+
349
+ // networks.value = newValue
350
+ // networksType.value = Array(newValue.length).fill('edit')
351
+ const count = newValue?.length || 0
352
+ networksIndex.value = [...Array(count).keys()]
353
+ networks.value = newValue
354
+ if (props.isEdit) {
355
+ networksType.value = Array(count).fill('edit')
356
+ } else if (props.isClone) {
357
+ networksType.value = Array(count).fill('new')
358
+ }
359
+ },
360
+ { immediate: true }
361
+ )
362
+ const addNetwork = (): void => {
363
+ const index = (networksIndex.value.at(-1) || 0) + 1
364
+ networksIndex.value.push(index)
365
+ networksType.value.push('new')
366
+
367
+ networks.value.push({
368
+ ...useDeepCopy(defaultNetwork),
369
+ boot_order: 0, // Убираем галочку
370
+ })
371
+ }
372
+ const onRemoveNetwork = (index: number): void => {
373
+ const removeIndex = networksIndex.value.indexOf(index)
374
+ networksIndex.value = networksIndex.value.filter((item) => item !== index)
375
+ networksType.value = networksType.value.filter(
376
+ (_item, key) => key !== removeIndex
377
+ )
378
+ networks.value = networks.value.filter(
379
+ (_network, key: number) => removeIndex !== key
380
+ )
381
+
382
+ delete newNetworkInvalidKeys.value[index]
383
+ delete sendDataNewNetwork.value[index]
384
+ sendData()
385
+ }
386
+
387
+ const defaultCdDvdDriver: UI_I_SendDataNewCdDvdDrive = {
388
+ create: false,
389
+ attach: false,
390
+ source: '',
391
+ bus: '',
392
+ device_type: 'cdrom',
393
+ boot_order: -1,
394
+ }
395
+ const cdDvdDrives = ref<UI_I_SendDataNewCdDvdDrive[]>([
396
+ useDeepCopy(defaultCdDvdDriver),
397
+ ])
398
+ const cdDvdDrivesIndex = ref<number[]>([0])
399
+ const cdDvdDrivesType = ref<UI_T_CdDvdType[]>(['new'])
400
+ watch(
401
+ () => props.cdDvdDrives,
402
+ (newValue) => {
403
+ if ((!props.isEdit && !props.isClone) || !newValue) return
404
+
405
+ // cdDvdDrives.value = newValue
406
+ // cdDvdDrivesType.value = Array(newValue.length).fill('edit')
407
+ const count = newValue?.length || 0
408
+ cdDvdDrivesIndex.value = [...Array(count).keys()]
409
+ cdDvdDrives.value = newValue
410
+ if (props.isEdit) {
411
+ cdDvdDrivesType.value = Array(count).fill('edit')
412
+ } else if (props.isClone) {
413
+ cdDvdDrivesType.value = Array(count).fill('new')
414
+ }
415
+ },
416
+ { immediate: true }
417
+ )
418
+ const addCdDvdDrive = (): void => {
419
+ const value = (cdDvdDrivesIndex.value.at(-1) || 0) + 1
420
+ cdDvdDrivesIndex.value.push(value)
421
+ cdDvdDrivesType.value.push('new')
422
+ cdDvdDrives.value.push({
423
+ ...useDeepCopy(defaultCdDvdDriver),
424
+ attach: true,
425
+ boot_order: 0, // Убираем галочку
426
+ })
427
+ }
428
+ const onRemoveCdDvdDrive = (
429
+ index: number,
430
+ cdDvdDrive: UI_I_SendDataNewCdDvdDrive
431
+ ): void => {
432
+ const removeIndex = cdDvdDrivesIndex.value.indexOf(index)
433
+
434
+ // if (!cdDvdDrive.create && !cdDvdDrive.attach) {
435
+ if (cdDvdDrivesType.value[index] === 'edit' && !cdDvdDrive.attach) {
436
+ cdDvdDrivesType.value = cdDvdDrivesType.value.map((item, key) => {
437
+ if (key === removeIndex) return 'removed'
438
+ return item
439
+ })
440
+ sendData()
441
+ return
442
+ }
443
+
444
+ cdDvdDrivesIndex.value = cdDvdDrivesIndex.value.filter(
445
+ (cdDvdDrive) => cdDvdDrive !== index
446
+ )
447
+ cdDvdDrivesType.value = cdDvdDrivesType.value.filter(
448
+ (_item, key) => key !== index
449
+ )
450
+ cdDvdDrives.value = cdDvdDrives.value.filter(
451
+ (_cdDvdDrive, key: number) => removeIndex !== key
452
+ )
453
+
454
+ delete sendDataNewCdDvdDrive.value[index]
455
+ sendData()
456
+ }
457
+ const onRollBackCdDvdDrive = (index: number): void => {
458
+ const removeIndex = cdDvdDrivesIndex.value.indexOf(index)
459
+ cdDvdDrivesType.value = cdDvdDrivesType.value.map((item, key) => {
460
+ if (key === removeIndex) return 'edit'
461
+ return item
462
+ })
463
+ }
464
+
465
+ const defaultPciDevice: UI_I_SendDataNewPciDevice = {
466
+ address: '',
467
+ vendor_name: '',
468
+ class_name: 'cdrom',
469
+ device_name: 'cdrom',
470
+ }
471
+ const pciDevicesIndex = ref<number[]>([0])
472
+ const pciDevicesType = ref<('new' | 'edit' | 'removed')[]>(['new'])
473
+ const pciDevices = ref<UI_I_SendDataNewPciDevice[]>([])
474
+ watch(
475
+ () => props.pciDevices,
476
+ (newValue) => {
477
+ if ((!props.isEdit && !props.isClone) || !newValue) return
478
+
479
+ const count = newValue?.length || 0
480
+ pciDevicesIndex.value = [...Array(count).keys()]
481
+ pciDevices.value = newValue || []
482
+ if (props.isEdit) {
483
+ pciDevicesType.value = Array(count).fill('edit')
484
+ } else if (props.isClone) {
485
+ pciDevicesType.value = Array(count).fill('new')
486
+ }
487
+ },
488
+ { immediate: true }
489
+ )
490
+
491
+ const addPciDevice = (): void => {
492
+ const newIndex = (pciDevicesIndex.value.at(-1) ?? -1) + 1
493
+ pciDevicesIndex.value.push(newIndex)
494
+ pciDevicesType.value.push('new')
495
+
496
+ pciDevices.value.push(useDeepCopy(defaultPciDevice))
497
+ }
498
+ const onRemovePciDevice = (index: number): void => {
499
+ const removeIndex = pciDevicesIndex.value.indexOf(index)
500
+ pciDevicesIndex.value = pciDevicesIndex.value.filter((item) => item !== index)
501
+ pciDevicesType.value = pciDevicesType.value.filter(
502
+ (_item, key) => key !== removeIndex
503
+ )
504
+
505
+ pciDevices.value = pciDevices.value.filter((_pciDevice, key: number) => {
506
+ return removeIndex !== key
507
+ })
508
+
509
+ delete newPciDeviceInvalidKeys.value[index]
510
+ delete sendDataNewPciDevices.value[index]
511
+ sendData()
512
+ }
513
+
514
+ const isShowFileModal = ref<boolean>(false)
515
+ const onAddDevice = (item: UI_I_DropdownTreeItemChild): void => {
516
+ switch (item.value) {
517
+ case 1:
518
+ addHardDisk()
519
+ break
520
+ case 2:
521
+ isShowFileModal.value = true
522
+ break
523
+ case 5:
524
+ addCdDvdDrive()
525
+ break
526
+ case 10:
527
+ addPciDevice()
528
+ break
529
+ case 14:
530
+ addNetwork()
531
+ break
532
+ }
533
+ }
534
+
535
+ const cpuInvalid = ref<boolean>(false)
536
+ const sendDataCpu = ref<UI_I_SendDataCpu | null>(null)
537
+ const onSendDataCpuMethod = (data: UI_I_SendDataCpu): void => {
538
+ sendDataCpu.value = data
539
+ sendData()
540
+ }
541
+ const sendDataMemory = ref<UI_I_SendDataMemory | null>(null)
542
+ const onSendDataMemoryMethod = (data: UI_I_SendDataMemory): void => {
543
+ sendDataMemory.value = data
544
+ sendData()
545
+ }
546
+ const sendDataNewHardDisk = ref<UI_I_SendDataNewHardDisk[]>([])
547
+ const onSendDataNewHardDiskMethod = (
548
+ data: UI_I_SendDataNewHardDisk,
549
+ index: number
550
+ ): void => {
551
+ sendDataNewHardDisk.value[index] = data
552
+ sendData()
553
+ }
554
+ const sendDataNewNetwork = ref<UI_I_SendDataNewNetwork[]>([])
555
+ const sendDataNewNetworkMethod = (
556
+ data: UI_I_SendDataNewNetwork,
557
+ index: number
558
+ ): void => {
559
+ sendDataNewNetwork.value[index] = data
560
+ sendData()
561
+ }
562
+ const sendDataNewCdDvdDrive = ref<UI_I_SendDataNewCdDvdDrive[]>([])
563
+ const sendDataNewCdDvdDriveMethod = (
564
+ data: UI_I_SendDataNewCdDvdDrive,
565
+ index: number
566
+ ): void => {
567
+ sendDataNewCdDvdDrive.value[index] = data
568
+ sendData()
569
+ }
570
+ const sendDataNewUsbController = ref<string | null>(null)
571
+ const onSendDataNewUsbControllerMethod = (data: string): void => {
572
+ sendDataNewUsbController.value = data
573
+ sendData()
574
+ }
575
+ const sendDataNewPciDevices = ref<UI_I_SendDataNewPciDevice[]>([])
576
+ const onSendDataPciDevicesMethod = (
577
+ data: UI_I_SendDataNewPciDevice,
578
+ index: number
579
+ ): void => {
580
+ sendDataNewPciDevices.value[index] = data
581
+ sendData()
582
+ }
583
+ const sendDataVideoCard = ref<UI_I_SendDataVideoCard | null>(null)
584
+ const onSendDataVideoCardMethod = (data: UI_I_SendDataVideoCard): void => {
585
+ sendDataVideoCard.value = data
586
+ sendData()
587
+ }
588
+
589
+ const sendData = (): void => {
590
+ const cpu = sendDataCpu.value
591
+ const memory = sendDataMemory.value
592
+ const hardDisks = sendDataNewHardDisk.value.flat(2)
593
+ const networks = sendDataNewNetwork.value.flat(2)
594
+ const cdDvdDrives = sendDataNewCdDvdDrive.value.flat(2)
595
+ const pciDevices = sendDataNewPciDevices.value.flat()
596
+ const usbController = sendDataNewUsbController.value
597
+ const videoCard = sendDataVideoCard.value
598
+
599
+ const sendData: UI_I_SendDataVirtualHardware = {
600
+ cpu,
601
+ memory,
602
+ usbController,
603
+ pciDevices,
604
+ networks,
605
+ hardDisks,
606
+ cdDvdDrives,
607
+ videoCard,
608
+ }
609
+ emits('send-data', sendData)
610
+ }
611
+
612
+ const onSetInvalidHardDisk = (invalid: boolean, index: number): void => {
613
+ newHardDiskInvalidKeys.value[index] = invalid
614
+ }
615
+
616
+ const onSetInvalidNetwork = (invalid: boolean, key: number): void => {
617
+ newNetworkInvalidKeys.value[key] = invalid
618
+ }
619
+
620
+ const onSetInvalidPciDevice = (invalid: boolean, key: number): void => {
621
+ newPciDeviceInvalidKeys.value[key] = invalid
622
+ }
623
+
624
+ const memoryInvalid = ref<boolean>(false)
625
+ const videoCardInvalid = ref<boolean>(false)
626
+ const newHardDiskInvalidKeys = ref<UI_I_InvalidKeys>({})
627
+ const newNetworkInvalidKeys = ref<UI_I_InvalidKeys>({})
628
+ const newPciDeviceInvalidKeys = ref<UI_I_InvalidKeys>({})
629
+
630
+ watch(
631
+ [
632
+ cpuInvalid,
633
+ memoryInvalid,
634
+ newHardDiskInvalidKeys,
635
+ newNetworkInvalidKeys,
636
+ newPciDeviceInvalidKeys,
637
+ videoCardInvalid,
638
+ ],
639
+ () => {
640
+ const cpu = cpuInvalid.value ? localization.value.cpu : ''
641
+ const memory = memoryInvalid.value ? localization.value.memory : ''
642
+ const videoCard = videoCardInvalid.value ? localization.value.videoCard : ''
643
+ let newHardDisk = ''
644
+ Object.keys(newHardDiskInvalidKeys.value).forEach((key) => {
645
+ if (!newHardDiskInvalidKeys.value[+key]) {
646
+ return
647
+ }
648
+
649
+ newHardDisk +=
650
+ (newHardDisk ? ', ' : '') +
651
+ localization.value.newHardDisk +
652
+ ' ' +
653
+ (+key + 1)
654
+ })
655
+ let newNetwork = ''
656
+ Object.keys(newNetworkInvalidKeys.value).forEach((key) => {
657
+ if (!newNetworkInvalidKeys.value[+key]) {
658
+ return
659
+ }
660
+
661
+ newNetwork +=
662
+ (newNetwork ? ', ' : '') +
663
+ localization.value.newNetwork +
664
+ ' ' +
665
+ (+key + 1)
666
+ })
667
+ // let pciDevice = ''
668
+ // Object.keys(newPciDeviceInvalidKeys.value).forEach((key) => {
669
+ // if (!newPciDeviceInvalidKeys.value[+key]) {
670
+ // return
671
+ // }
672
+ //
673
+ // pciDevice +=
674
+ // (pciDevice ? ', ' : '') +
675
+ // localization.value.pciDevice +
676
+ // ' ' +
677
+ // (+key + 1)
678
+ // })
679
+
680
+ emits('invalid', [cpu, memory, newHardDisk, newNetwork, videoCard])
681
+ },
682
+ { deep: true }
683
+ )
684
+
685
+ emits('get-pci-devices')
686
+ </script>
687
+
688
+ <style scoped lang="scss">
689
+ .virtual-hardware {
690
+ .content-dropdown {
691
+ width: 200px;
692
+ }
693
+ }
694
+ .stack-view {
695
+ background-color: var(--block-view-bg-color);
696
+ }
697
+ </style>