bfg-common 1.3.241 → 1.3.243

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