bfg-common 1.3.393 → 1.3.394

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 (85) hide show
  1. package/assets/img/icons/icons-sprite-dark-1.svg +1 -360
  2. package/assets/img/icons/icons-sprite-dark-2.svg +1 -296
  3. package/assets/img/icons/icons-sprite-dark-3.svg +1 -221
  4. package/assets/img/icons/icons-sprite-dark-4.svg +1 -247
  5. package/assets/img/icons/icons-sprite-light-1.svg +1 -360
  6. package/assets/img/icons/icons-sprite-light-2.svg +1 -296
  7. package/assets/img/icons/icons-sprite-light-3.svg +1 -221
  8. package/assets/img/icons/icons-sprite-light-4.svg +1 -247
  9. package/assets/localization/local_be.json +5 -1
  10. package/assets/localization/local_en.json +5 -1
  11. package/assets/localization/local_hy.json +5 -1
  12. package/assets/localization/local_kk.json +5 -1
  13. package/assets/localization/local_ru.json +5 -1
  14. package/assets/localization/local_zh.json +5 -1
  15. package/assets/scss/common/icons/icons.scss +0 -1
  16. package/components/atoms/loader/Loader.vue +30 -30
  17. package/components/atoms/loader/PreLoader.vue +38 -38
  18. package/components/atoms/switch/Switch.vue +111 -111
  19. package/components/atoms/table/compact/Compact.vue +526 -526
  20. package/components/common/adapterManager/ui/table/Header.vue +93 -93
  21. package/components/common/adapterManager/ui/table/Table.vue +162 -162
  22. package/components/common/context/Context.vue +99 -99
  23. package/components/common/lib/config/states.ts +126 -126
  24. package/components/common/resource/progressBlock/ProgressBlock.vue +142 -142
  25. package/components/common/split/horizontal/Horizontal.vue +70 -307
  26. package/components/common/split/horizontal/HorizontalOld.vue +307 -0
  27. package/components/common/vm/actions/add/Add.vue +602 -602
  28. package/components/common/vm/actions/clone/Clone.vue +518 -518
  29. package/components/common/vm/actions/common/customizeHardware/CustomizeHardware.vue +273 -273
  30. package/components/common/vm/actions/common/customizeHardware/virtualHardware/VirtualHardware.vue +697 -697
  31. package/components/common/vm/actions/common/customizeHardware/virtualHardware/cdDvdDrive/CdDvdDrive.vue +256 -256
  32. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newHardDisk/NewHardDisk.vue +458 -458
  33. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newNetwork/NewNetwork.vue +304 -304
  34. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newPciDevice/NewPciDevice.vue +167 -167
  35. package/components/common/vm/actions/common/customizeHardware/virtualHardware/newUsbController/NewUsbController.vue +82 -82
  36. package/components/common/vm/actions/common/customizeHardware/virtualHardware/other/Other.vue +32 -32
  37. package/components/common/vm/actions/common/customizeHardware/virtualHardware/videoCard/VideoCard.vue +182 -182
  38. package/components/common/vm/actions/common/customizeHardware/vmoptions/bootOptions/BootOptions.vue +114 -114
  39. package/components/common/vm/actions/common/customizeHardware/vmoptions/generalOptions/GeneralOptions.vue +188 -188
  40. package/components/common/vm/actions/common/customizeHardware/vmoptions/tools/Tools.vue +72 -72
  41. package/components/common/vm/actions/common/select/computeResource/ComputeResource.vue +164 -164
  42. package/components/common/vm/actions/common/select/name/Name.vue +235 -235
  43. package/components/common/vm/actions/common/select/storage/Storage.vue +198 -198
  44. package/components/common/vm/actions/editSettings/EditSettings.vue +316 -316
  45. package/components/common/weekSelect/lib/config/options.ts +10 -10
  46. package/components/common/wizards/datastore/add/Add.vue +436 -436
  47. package/components/common/wizards/datastore/add/lib/config/stepItems.ts +191 -191
  48. package/components/common/wizards/datastore/add/lib/models/interfaces.ts +38 -38
  49. package/components/common/wizards/datastore/add/lib/utils.ts +70 -70
  50. package/components/common/wizards/datastore/add/local/createName/CreateName.vue +160 -160
  51. package/components/common/wizards/datastore/add/nfs/Nfs.vue +112 -112
  52. package/components/common/wizards/datastore/add/nfs/accessibility/Accessibility.vue +104 -104
  53. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/compatibleTable.ts +62 -62
  54. package/components/common/wizards/datastore/add/nfs/accessibility/tablesView/lib/config/hostsTableItems.ts +53 -53
  55. package/components/common/wizards/datastore/add/readyComplete/ReadyComplete.vue +86 -86
  56. package/components/common/wizards/datastore/add/readyComplete/lib/config/propertiesDetails.ts +184 -184
  57. package/components/common/wizards/datastore/add/sharedStorm/deviceSelection/DeviceSelection.vue +459 -459
  58. package/components/common/wizards/datastore/add/types/Types.vue +80 -80
  59. package/components/common/wizards/datastore/add/types/lib/config/typeOptions.ts +49 -49
  60. package/composables/useAppVersion.ts +21 -21
  61. package/composables/useEnvLanguage.ts +20 -20
  62. package/lib/utils/sendTask.ts +72 -72
  63. package/minify.js +146 -146
  64. package/package.json +1 -1
  65. package/plugins/spice-console/spice.console.ts +147 -147
  66. package/public/spice-console/application/codec.js +263 -263
  67. package/public/spice-console/lib/rasterEngine.js +907 -907
  68. package/public/spice-console/network/spicechannel.js +375 -375
  69. package/store/main/lib/interfaces.ts +9 -9
  70. package/store/tasks/actions.ts +133 -133
  71. package/store/tasks/getters.ts +25 -25
  72. package/store/tasks/lib/models/interfaces.ts +18 -18
  73. package/store/tasks/mutations.ts +58 -58
  74. package/store/tasks/state.ts +16 -16
  75. package/assets/img/icons/icons-sprite-dark-1-old.svg +0 -1
  76. package/assets/img/icons/icons-sprite-dark-2-old.svg +0 -1
  77. package/assets/img/icons/icons-sprite-dark-3-old.svg +0 -1
  78. package/assets/img/icons/icons-sprite-dark-4-old.svg +0 -1
  79. package/assets/img/icons/icons-sprite-dark-7.svg +0 -8
  80. package/assets/img/icons/icons-sprite-light-1-old.svg +0 -1
  81. package/assets/img/icons/icons-sprite-light-2-old.svg +0 -1
  82. package/assets/img/icons/icons-sprite-light-3-old.svg +0 -1
  83. package/assets/img/icons/icons-sprite-light-4-old.svg +0 -1
  84. package/assets/img/icons/icons-sprite-light-7.svg +0 -8
  85. package/assets/scss/common/icons/icons-7.scss +0 -29
@@ -1,458 +1,458 @@
1
- <template>
2
- <div class="hardware-hard-disk">
3
- <atoms-stack-block
4
- :has-children="true"
5
- :is-roll-back="props.type === 'removed'"
6
- :removable="isRemovable"
7
- @remove="onRemoveHardDisk"
8
- @roll-back="onRollBackHardDisk"
9
- >
10
- <template #stackBlockKey>
11
- <div
12
- :id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
13
- :data-id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
14
- class="vm-wizard-toggle-block-hard-disk flex-align-center"
15
- >
16
- <span>{{ label }}</span>
17
- <atoms-the-icon
18
- v-show="hardDiskInvalid"
19
- width="24px"
20
- height="24px"
21
- class="is-error tooltip-trigger"
22
- name="info"
23
- />
24
- </div>
25
- </template>
26
- <template #stackBlockContent>
27
- <div class="flex-align-center flex-space-between mr-1">
28
- <template v-if="props.type === 'removed'">
29
- <span>{{ localization.deviceWillRemoved }}</span>
30
- <div @click.stop class="flex-align-center">
31
- <input
32
- :id="`hard-disk-delete-files-from-datastore-${props.index}`"
33
- v-model="deleteFilesFromDatastore"
34
- :data-id="`hard-disk-delete-files-from-datastore-${props.index}`"
35
- :disabled="props.disabled"
36
- type="checkbox"
37
- class="pointer"
38
- @click.stop
39
- />
40
- <label
41
- :for="`hard-disk-delete-files-from-datastore-${props.index}`"
42
- :class="[
43
- 'label-text-normal pointer',
44
- props.disabled && 'div-disabled',
45
- ]"
46
- >{{ localization.deleteFilesFromDatastore }}</label
47
- >
48
- </div>
49
- </template>
50
- <template v-else>
51
- <atoms-tooltip-error
52
- :has-error="!!hardDiskLocalAndApiErrorsTexts"
53
- :selector="`#vm-wizard-hard-disk-${props.index}`"
54
- @remove="
55
- emits(
56
- 'remove-error-by-title',
57
- `disk_devices[${props.index}].size`
58
- )
59
- "
60
- >
61
- <template #elem>
62
- <input
63
- :id="`vm-wizard-hard-disk-${props.index}`"
64
- v-model.number="size"
65
- :data-id="`vm-wizard-hard-disk-${props.index}`"
66
- type="number"
67
- :class="[
68
- 'spinfree edit-setting-input input-text-color',
69
- { 'has-error': hardDiskTypeErrorLocalText },
70
- ]"
71
- :disabled="isDisabledSize"
72
- @blur="onValidateSize"
73
- @click.stop
74
- />
75
- </template>
76
- <template #content>
77
- {{ hardDiskLocalAndApiErrorsTexts }}
78
- </template>
79
- </atoms-tooltip-error>
80
- <div class="select ml-1">
81
- <select
82
- :id="`hard-disk-type-select-${props.index}`"
83
- v-model="hardDiskType"
84
- :data-id="`hard-disk-type-select-${props.index}`"
85
- :disabled="props.type === 'exist'"
86
- class="input-text-color"
87
- @change="onValidateSize"
88
- @click.stop
89
- >
90
- <option
91
- v-for="(item, key) in hardDiskTypeOptions"
92
- :key="key"
93
- :value="item.value"
94
- >
95
- {{ item.text }}
96
- </option>
97
- </select>
98
- </div>
99
- </template>
100
- </div>
101
- </template>
102
- <template #stackChildren>
103
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-maximum-size
104
- :free-mb="maxHardDisk"
105
- />
106
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-location
107
- v-if="props.type === 'new'"
108
- :location="location"
109
- :index="props.index"
110
- :error-validation-fields="props.errorValidationFields"
111
- :get-datastore-table-func="props.getDatastoreTableFunc"
112
- :datastore="props.datastore"
113
- @change="onChangeStorage"
114
- @remove-error-by-title="emits('remove-error-by-title', $event)"
115
- />
116
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-file
117
- v-else
118
- :path="props.hardDisk?.source"
119
- />
120
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-provisioning
121
- v-model:disk-provisioning="diskProvisioning"
122
- :index="props.index"
123
- :error-validation-fields="props.errorValidationFields"
124
- :disabled="(props.isEdit && props.type !== 'new') || isRunning"
125
- @remove-error-by-title="emits('remove-error-by-title', $event)"
126
- />
127
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-sharing
128
- v-model:sharing="sharing"
129
- :index="props.index"
130
- :error-validation-fields="props.errorValidationFields"
131
- :disabled="isRunning && props.type !== 'new'"
132
- @remove-error-by-title="emits('remove-error-by-title', $event)"
133
- />
134
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-limit-iops
135
- v-model:limit-iops="limitIops"
136
- v-model:limit-iops-type="limitIopsType"
137
- :index="props.index"
138
- :error-validation-fields="props.errorValidationFields"
139
- @valid="limitIopsInvalid = $event"
140
- @remove-error-by-title="emits('remove-error-by-title', $event)"
141
- />
142
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-mode
143
- v-model:diskMode="diskMode"
144
- :index="props.index"
145
- :error-validation-fields="props.errorValidationFields"
146
- :disabled="isRunning && props.type !== 'new'"
147
- @remove-error-by-title="emits('remove-error-by-title', $event)"
148
- />
149
- <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-cache
150
- v-model:cache="cache"
151
- :index="props.index"
152
- :error-validation-fields="props.errorValidationFields"
153
- :disabled="(props.isEdit && props.type !== 'new') || isRunning"
154
- @remove-error-by-title="emits('remove-error-by-title', $event)"
155
- />
156
- <common-vm-actions-common-customize-hardware-virtual-hardware-bus
157
- v-model:bus="bus"
158
- :index="props.index"
159
- :error-validation-fields="props.errorValidationFields"
160
- :disabled="isRunning"
161
- :help-text="localization.busVirtioHelpDesc"
162
- component-type="disk_devices"
163
- @remove-error-by-title="emits('remove-error-by-title', $event)"
164
- />
165
- </template>
166
- </atoms-stack-block>
167
- </div>
168
- </template>
169
-
170
- <script setup lang="ts">
171
- import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
172
- import {
173
- UI_I_SendDataNewHardDisk,
174
- UI_I_SendDataNewHardDiskStorage,
175
- } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
176
- import { UI_T_HardDiskType } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
177
- import { UI_I_Localization } from '~/lib/models/interfaces'
178
- import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
179
- import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
180
- import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
181
- import { binaryOptionsFunc } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/config/binaryOptions'
182
-
183
- const props = defineProps<{
184
- storage: UI_I_DatastoreTableItem | null
185
- hardDisk: UI_I_SendDataNewHardDisk
186
- type: UI_T_HardDiskType
187
- index: number
188
- errorValidationFields: UI_I_ErrorValidationField<string>[]
189
- getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
190
- datastore: UI_I_DatastoreTableItem[]
191
- isEdit: boolean
192
- state?: string | number
193
- }>()
194
- const emits = defineEmits<{
195
- (event: 'send-data', value: UI_I_SendDataNewHardDisk): void
196
- (event: 'invalid', value: boolean): void
197
- (event: 'remove'): void
198
- (event: 'roll-back'): void
199
- (event: 'remove-error-by-title', value: string): void
200
- }>()
201
-
202
- const localization = computed<UI_I_Localization>(() => useLocal())
203
-
204
- const isRunning = computed<boolean>(() => {
205
- return props.state === 2
206
- })
207
-
208
- const isRemovable = computed<boolean>(() => {
209
- return (
210
- !props.isEdit ||
211
- !isRunning.value ||
212
- (isRunning.value && bus.value === 'virtio')
213
- )
214
- })
215
-
216
- const typeError = computed<string>(() => `disk_devices[${props.index}].size`)
217
-
218
- const apiErrorLocal = computed<string>(() => {
219
- return (
220
- props.errorValidationFields?.find(
221
- (message) => message.field === typeError.value
222
- )?.error_message || ''
223
- )
224
- })
225
-
226
- const { $binary } = useNuxtApp()
227
-
228
- const label = computed<string>(() => {
229
- if (props.type === 'edit' || props.type === 'removed')
230
- return `${localization.value.hardDisk} ${props.index + 1}`
231
-
232
- return `${localization.value.newHardDisk} *`
233
- })
234
-
235
- const maxHardDisk = computed<number>(() => {
236
- if (props.type === 'exist') return $binary.gbToMb(props.hardDisk.size)
237
-
238
- if (!storage.value) return 0
239
-
240
- if (props.isEdit)
241
- return storage.value.capacity.free_mb + $binary.gbToMb(props.hardDisk.size)
242
-
243
- return storage.value.capacity.free_mb
244
- })
245
- const hardDiskTypeErrorLocalText = computed<string>(() => {
246
- if (+sizeInMb.value > maxHardDisk.value) {
247
- return localization.value.diskCapacitySpecifiedGreater
248
- }
249
-
250
- if (+sizeInMb.value <= 0) {
251
- return localization.value.diskCapacityCannotZero
252
- }
253
-
254
- return ''
255
- })
256
-
257
- const hardDiskLocalAndApiErrorsTexts = computed<string>(() => {
258
- const localError = hardDiskTypeErrorLocalText.value
259
- const apiError = apiErrorLocal.value
260
-
261
- let result = ''
262
- if (localError && !apiError) result = localError
263
- if (!localError && apiError) result = apiError
264
- if (localError && apiError) result = localError + ', ' + apiError
265
- if (!localError && apiError) result = apiError
266
-
267
- return result
268
- })
269
-
270
- const deleteFilesFromDatastore = ref<boolean>(false)
271
-
272
- const hardDiskType = ref<string>('gb')
273
- const hardDiskTypeOptions = ref<UI_I_OptionItem[]>(
274
- binaryOptionsFunc(localization.value)
275
- )
276
-
277
- const size = ref<number>(props.hardDisk.size) // Изначально всегда приходит в gb
278
- const sizeInMb = computed<number>(() => {
279
- if (hardDiskType.value === 'mb') {
280
- return size.value
281
- }
282
-
283
- return $binary.universalFromTo(size.value, hardDiskType.value, 'mb')
284
- })
285
- const onValidateSize = (): void => {
286
- if (props.type !== 'edit') return
287
- if (sizeInMb.value < $binary.gbToMb(props.hardDisk.size)) {
288
- size.value = $binary.universalFromTo(
289
- props.hardDisk.size,
290
- 'gb',
291
- hardDiskType.value
292
- )
293
- }
294
- }
295
-
296
- const isDisabledSize = computed<boolean>(() => {
297
- return props.type === 'exist' || (props.type === 'edit' && readOnly.value)
298
- })
299
-
300
- const onRemoveHardDisk = (): void => {
301
- emits('remove')
302
- }
303
- const onRollBackHardDisk = (): void => {
304
- emits('roll-back')
305
- }
306
-
307
- const location = ref<UI_I_DatastoreTableItem | null>(props.storage || null)
308
- watch(
309
- () => props.storage,
310
- (newValue) => {
311
- location.value = newValue
312
- storage.value = newValue
313
- }
314
- )
315
- const storage = ref<UI_I_DatastoreTableItem | null>(props.storage)
316
- const onChangeStorage = (item: UI_I_DatastoreTableItem): void => {
317
- storage.value = item
318
- }
319
-
320
- const diskProvisioning = ref<string>('thick')
321
- const sharing = ref<string>('')
322
- const shares = ref<number>(0)
323
-
324
- const limitIops = ref<number>(16)
325
- const limitIopsType = ref<string>('unlimited')
326
- const limitIopsInvalid = ref<boolean>(false)
327
-
328
- const diskMode = ref<string>('dependent')
329
-
330
- const cache = ref<string>('none')
331
-
332
- const bus = ref<string>('ide')
333
-
334
- const readOnly = ref<boolean>(false)
335
-
336
- const discard = ref<string>('')
337
-
338
- const io = ref<string>('')
339
-
340
- const target = ref<string>('')
341
-
342
- watch(
343
- [
344
- sizeInMb,
345
- storage,
346
- diskProvisioning,
347
- sharing,
348
- limitIops,
349
- limitIopsType,
350
- diskMode,
351
- cache,
352
- bus,
353
- deleteFilesFromDatastore,
354
- () => props.type,
355
- ],
356
- () => {
357
- const limitIopsLocal =
358
- limitIopsType.value === 'unlimited' ? 0 : limitIops.value
359
- const storageLocal: UI_I_SendDataNewHardDiskStorage = {
360
- id: storage.value?.id || '',
361
- name: storage.value?.name || '',
362
- type: storage.value?.type_text || '',
363
- pool: storage.value?.pool_name || '',
364
- user: '',
365
- endpoint: '',
366
- password: '',
367
- protocol: '',
368
- options: '',
369
- }
370
-
371
- emits('send-data', {
372
- create: props.hardDisk.create,
373
- attach: props.hardDisk.attach,
374
- source: props.hardDisk.source,
375
- size: Math.ceil(sizeInMb.value),
376
- bus: bus.value,
377
- target: target.value,
378
- storage: storageLocal,
379
- device_type: 'disk',
380
- provision_type: diskProvisioning.value,
381
- disk_mode: diskMode.value,
382
- sharing: sharing.value === 'yes',
383
- read_only: readOnly.value,
384
- shares: shares.value,
385
- cache: cache.value,
386
- io: io.value,
387
- limit_iops: limitIopsLocal,
388
- discard: discard.value,
389
- capacity: Math.ceil(sizeInMb.value),
390
- boot_order: props.hardDisk.boot_order,
391
- detach: props.type === 'removed',
392
- remove: props.type === 'removed' && deleteFilesFromDatastore.value, // Проверяем если удалили диск и указали Delete files from datastore
393
- })
394
- },
395
- { immediate: true }
396
- )
397
-
398
- // Добавляем данные для редактирования
399
- watch(
400
- () => props.hardDisk,
401
- (newValue) => {
402
- // TODO refactoring
403
- if (props.type === 'exist') {
404
- diskProvisioning.value = newValue.provision_type || 'thick'
405
- return
406
- }
407
- if (props.type !== 'edit') return
408
-
409
- diskProvisioning.value = newValue.provision_type || 'thick'
410
- sharing.value = newValue.sharing ? 'yes' : ''
411
- shares.value = newValue.shares || 0
412
- // limitIopsType.value = newValue.io === 'native' ? 'unlimited' : 'custom'
413
- limitIopsType.value = newValue.limit_iops === 0 ? 'unlimited' : 'custom'
414
- limitIops.value = newValue.limit_iops || 16
415
- diskMode.value = newValue.disk_mode || 'dependent'
416
- cache.value = newValue.cache || 'none'
417
- bus.value = newValue.bus || 'ide'
418
- readOnly.value = newValue.read_only || false
419
- discard.value = newValue.discard || ''
420
- io.value = newValue.io || ''
421
- target.value = newValue.target || ''
422
- },
423
- { immediate: true }
424
- )
425
- watch(
426
- isRunning,
427
- (newValue) => {
428
- if (newValue && props.type === 'new') {
429
- bus.value = 'virtio'
430
- }
431
- },
432
- { immediate: true }
433
- )
434
-
435
- const hardDiskInvalid = computed<boolean>(() => {
436
- return !!hardDiskTypeErrorLocalText.value || limitIopsInvalid.value
437
- })
438
- watch(
439
- hardDiskInvalid,
440
- (newValue) => {
441
- emits('invalid', newValue)
442
- },
443
- { immediate: true }
444
- )
445
- </script>
446
-
447
- <style scoped lang="scss">
448
- .hardware-cpu {
449
- #hard-disk {
450
- width: 100px;
451
- -webkit-appearance: none;
452
-
453
- &.has-error {
454
- border: 1px solid #db2100;
455
- }
456
- }
457
- }
458
- </style>
1
+ <template>
2
+ <div class="hardware-hard-disk">
3
+ <atoms-stack-block
4
+ :has-children="true"
5
+ :is-roll-back="props.type === 'removed'"
6
+ :removable="isRemovable"
7
+ @remove="onRemoveHardDisk"
8
+ @roll-back="onRollBackHardDisk"
9
+ >
10
+ <template #stackBlockKey>
11
+ <div
12
+ :id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
13
+ :data-id="`vm-wizard-toggle-block-hard-disk-${props.index}`"
14
+ class="vm-wizard-toggle-block-hard-disk flex-align-center"
15
+ >
16
+ <span>{{ label }}</span>
17
+ <atoms-the-icon
18
+ v-show="hardDiskInvalid"
19
+ width="24px"
20
+ height="24px"
21
+ class="is-error tooltip-trigger"
22
+ name="info"
23
+ />
24
+ </div>
25
+ </template>
26
+ <template #stackBlockContent>
27
+ <div class="flex-align-center flex-space-between mr-1">
28
+ <template v-if="props.type === 'removed'">
29
+ <span>{{ localization.deviceWillRemoved }}</span>
30
+ <div @click.stop class="flex-align-center">
31
+ <input
32
+ :id="`hard-disk-delete-files-from-datastore-${props.index}`"
33
+ v-model="deleteFilesFromDatastore"
34
+ :data-id="`hard-disk-delete-files-from-datastore-${props.index}`"
35
+ :disabled="props.disabled"
36
+ type="checkbox"
37
+ class="pointer"
38
+ @click.stop
39
+ />
40
+ <label
41
+ :for="`hard-disk-delete-files-from-datastore-${props.index}`"
42
+ :class="[
43
+ 'label-text-normal pointer',
44
+ props.disabled && 'div-disabled',
45
+ ]"
46
+ >{{ localization.deleteFilesFromDatastore }}</label
47
+ >
48
+ </div>
49
+ </template>
50
+ <template v-else>
51
+ <atoms-tooltip-error
52
+ :has-error="!!hardDiskLocalAndApiErrorsTexts"
53
+ :selector="`#vm-wizard-hard-disk-${props.index}`"
54
+ @remove="
55
+ emits(
56
+ 'remove-error-by-title',
57
+ `disk_devices[${props.index}].size`
58
+ )
59
+ "
60
+ >
61
+ <template #elem>
62
+ <input
63
+ :id="`vm-wizard-hard-disk-${props.index}`"
64
+ v-model.number="size"
65
+ :data-id="`vm-wizard-hard-disk-${props.index}`"
66
+ type="number"
67
+ :class="[
68
+ 'spinfree edit-setting-input input-text-color',
69
+ { 'has-error': hardDiskTypeErrorLocalText },
70
+ ]"
71
+ :disabled="isDisabledSize"
72
+ @blur="onValidateSize"
73
+ @click.stop
74
+ />
75
+ </template>
76
+ <template #content>
77
+ {{ hardDiskLocalAndApiErrorsTexts }}
78
+ </template>
79
+ </atoms-tooltip-error>
80
+ <div class="select ml-1">
81
+ <select
82
+ :id="`hard-disk-type-select-${props.index}`"
83
+ v-model="hardDiskType"
84
+ :data-id="`hard-disk-type-select-${props.index}`"
85
+ :disabled="props.type === 'exist'"
86
+ class="input-text-color"
87
+ @change="onValidateSize"
88
+ @click.stop
89
+ >
90
+ <option
91
+ v-for="(item, key) in hardDiskTypeOptions"
92
+ :key="key"
93
+ :value="item.value"
94
+ >
95
+ {{ item.text }}
96
+ </option>
97
+ </select>
98
+ </div>
99
+ </template>
100
+ </div>
101
+ </template>
102
+ <template #stackChildren>
103
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-maximum-size
104
+ :free-mb="maxHardDisk"
105
+ />
106
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-location
107
+ v-if="props.type === 'new'"
108
+ :location="location"
109
+ :index="props.index"
110
+ :error-validation-fields="props.errorValidationFields"
111
+ :get-datastore-table-func="props.getDatastoreTableFunc"
112
+ :datastore="props.datastore"
113
+ @change="onChangeStorage"
114
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
115
+ />
116
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-file
117
+ v-else
118
+ :path="props.hardDisk?.source"
119
+ />
120
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-provisioning
121
+ v-model:disk-provisioning="diskProvisioning"
122
+ :index="props.index"
123
+ :error-validation-fields="props.errorValidationFields"
124
+ :disabled="(props.isEdit && props.type !== 'new') || isRunning"
125
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
126
+ />
127
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-sharing
128
+ v-model:sharing="sharing"
129
+ :index="props.index"
130
+ :error-validation-fields="props.errorValidationFields"
131
+ :disabled="isRunning && props.type !== 'new'"
132
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
133
+ />
134
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-limit-iops
135
+ v-model:limit-iops="limitIops"
136
+ v-model:limit-iops-type="limitIopsType"
137
+ :index="props.index"
138
+ :error-validation-fields="props.errorValidationFields"
139
+ @valid="limitIopsInvalid = $event"
140
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
141
+ />
142
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-mode
143
+ v-model:diskMode="diskMode"
144
+ :index="props.index"
145
+ :error-validation-fields="props.errorValidationFields"
146
+ :disabled="isRunning && props.type !== 'new'"
147
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
148
+ />
149
+ <common-vm-actions-common-customize-hardware-virtual-hardware-new-hard-disk-cache
150
+ v-model:cache="cache"
151
+ :index="props.index"
152
+ :error-validation-fields="props.errorValidationFields"
153
+ :disabled="(props.isEdit && props.type !== 'new') || isRunning"
154
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
155
+ />
156
+ <common-vm-actions-common-customize-hardware-virtual-hardware-bus
157
+ v-model:bus="bus"
158
+ :index="props.index"
159
+ :error-validation-fields="props.errorValidationFields"
160
+ :disabled="isRunning"
161
+ :help-text="localization.busVirtioHelpDesc"
162
+ component-type="disk_devices"
163
+ @remove-error-by-title="emits('remove-error-by-title', $event)"
164
+ />
165
+ </template>
166
+ </atoms-stack-block>
167
+ </div>
168
+ </template>
169
+
170
+ <script setup lang="ts">
171
+ import { UI_I_DatastoreTableItem } from '~/lib/models/store/storage/interfaces'
172
+ import {
173
+ UI_I_SendDataNewHardDisk,
174
+ UI_I_SendDataNewHardDiskStorage,
175
+ } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/interfaces'
176
+ import { UI_T_HardDiskType } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/models/types'
177
+ import { UI_I_Localization } from '~/lib/models/interfaces'
178
+ import { UI_I_ErrorValidationField } from '~/lib/models/store/interfaces'
179
+ import { UI_I_TablePayload } from '~/lib/models/table/interfaces'
180
+ import { UI_I_OptionItem } from '~/components/atoms/lib/models/interfaces'
181
+ import { binaryOptionsFunc } from '~/components/common/vm/actions/common/customizeHardware/virtualHardware/lib/config/binaryOptions'
182
+
183
+ const props = defineProps<{
184
+ storage: UI_I_DatastoreTableItem | null
185
+ hardDisk: UI_I_SendDataNewHardDisk
186
+ type: UI_T_HardDiskType
187
+ index: number
188
+ errorValidationFields: UI_I_ErrorValidationField<string>[]
189
+ getDatastoreTableFunc: (payload: UI_I_TablePayload) => Promise<void>
190
+ datastore: UI_I_DatastoreTableItem[]
191
+ isEdit: boolean
192
+ state?: string | number
193
+ }>()
194
+ const emits = defineEmits<{
195
+ (event: 'send-data', value: UI_I_SendDataNewHardDisk): void
196
+ (event: 'invalid', value: boolean): void
197
+ (event: 'remove'): void
198
+ (event: 'roll-back'): void
199
+ (event: 'remove-error-by-title', value: string): void
200
+ }>()
201
+
202
+ const localization = computed<UI_I_Localization>(() => useLocal())
203
+
204
+ const isRunning = computed<boolean>(() => {
205
+ return props.state === 2
206
+ })
207
+
208
+ const isRemovable = computed<boolean>(() => {
209
+ return (
210
+ !props.isEdit ||
211
+ !isRunning.value ||
212
+ (isRunning.value && bus.value === 'virtio')
213
+ )
214
+ })
215
+
216
+ const typeError = computed<string>(() => `disk_devices[${props.index}].size`)
217
+
218
+ const apiErrorLocal = computed<string>(() => {
219
+ return (
220
+ props.errorValidationFields?.find(
221
+ (message) => message.field === typeError.value
222
+ )?.error_message || ''
223
+ )
224
+ })
225
+
226
+ const { $binary } = useNuxtApp()
227
+
228
+ const label = computed<string>(() => {
229
+ if (props.type === 'edit' || props.type === 'removed')
230
+ return `${localization.value.hardDisk} ${props.index + 1}`
231
+
232
+ return `${localization.value.newHardDisk} *`
233
+ })
234
+
235
+ const maxHardDisk = computed<number>(() => {
236
+ if (props.type === 'exist') return $binary.gbToMb(props.hardDisk.size)
237
+
238
+ if (!storage.value) return 0
239
+
240
+ if (props.isEdit)
241
+ return storage.value.capacity.free_mb + $binary.gbToMb(props.hardDisk.size)
242
+
243
+ return storage.value.capacity.free_mb
244
+ })
245
+ const hardDiskTypeErrorLocalText = computed<string>(() => {
246
+ if (+sizeInMb.value > maxHardDisk.value) {
247
+ return localization.value.diskCapacitySpecifiedGreater
248
+ }
249
+
250
+ if (+sizeInMb.value <= 0) {
251
+ return localization.value.diskCapacityCannotZero
252
+ }
253
+
254
+ return ''
255
+ })
256
+
257
+ const hardDiskLocalAndApiErrorsTexts = computed<string>(() => {
258
+ const localError = hardDiskTypeErrorLocalText.value
259
+ const apiError = apiErrorLocal.value
260
+
261
+ let result = ''
262
+ if (localError && !apiError) result = localError
263
+ if (!localError && apiError) result = apiError
264
+ if (localError && apiError) result = localError + ', ' + apiError
265
+ if (!localError && apiError) result = apiError
266
+
267
+ return result
268
+ })
269
+
270
+ const deleteFilesFromDatastore = ref<boolean>(false)
271
+
272
+ const hardDiskType = ref<string>('gb')
273
+ const hardDiskTypeOptions = ref<UI_I_OptionItem[]>(
274
+ binaryOptionsFunc(localization.value)
275
+ )
276
+
277
+ const size = ref<number>(props.hardDisk.size) // Изначально всегда приходит в gb
278
+ const sizeInMb = computed<number>(() => {
279
+ if (hardDiskType.value === 'mb') {
280
+ return size.value
281
+ }
282
+
283
+ return $binary.universalFromTo(size.value, hardDiskType.value, 'mb')
284
+ })
285
+ const onValidateSize = (): void => {
286
+ if (props.type !== 'edit') return
287
+ if (sizeInMb.value < $binary.gbToMb(props.hardDisk.size)) {
288
+ size.value = $binary.universalFromTo(
289
+ props.hardDisk.size,
290
+ 'gb',
291
+ hardDiskType.value
292
+ )
293
+ }
294
+ }
295
+
296
+ const isDisabledSize = computed<boolean>(() => {
297
+ return props.type === 'exist' || (props.type === 'edit' && readOnly.value)
298
+ })
299
+
300
+ const onRemoveHardDisk = (): void => {
301
+ emits('remove')
302
+ }
303
+ const onRollBackHardDisk = (): void => {
304
+ emits('roll-back')
305
+ }
306
+
307
+ const location = ref<UI_I_DatastoreTableItem | null>(props.storage || null)
308
+ watch(
309
+ () => props.storage,
310
+ (newValue) => {
311
+ location.value = newValue
312
+ storage.value = newValue
313
+ }
314
+ )
315
+ const storage = ref<UI_I_DatastoreTableItem | null>(props.storage)
316
+ const onChangeStorage = (item: UI_I_DatastoreTableItem): void => {
317
+ storage.value = item
318
+ }
319
+
320
+ const diskProvisioning = ref<string>('thick')
321
+ const sharing = ref<string>('')
322
+ const shares = ref<number>(0)
323
+
324
+ const limitIops = ref<number>(16)
325
+ const limitIopsType = ref<string>('unlimited')
326
+ const limitIopsInvalid = ref<boolean>(false)
327
+
328
+ const diskMode = ref<string>('dependent')
329
+
330
+ const cache = ref<string>('none')
331
+
332
+ const bus = ref<string>('ide')
333
+
334
+ const readOnly = ref<boolean>(false)
335
+
336
+ const discard = ref<string>('')
337
+
338
+ const io = ref<string>('')
339
+
340
+ const target = ref<string>('')
341
+
342
+ watch(
343
+ [
344
+ sizeInMb,
345
+ storage,
346
+ diskProvisioning,
347
+ sharing,
348
+ limitIops,
349
+ limitIopsType,
350
+ diskMode,
351
+ cache,
352
+ bus,
353
+ deleteFilesFromDatastore,
354
+ () => props.type,
355
+ ],
356
+ () => {
357
+ const limitIopsLocal =
358
+ limitIopsType.value === 'unlimited' ? 0 : limitIops.value
359
+ const storageLocal: UI_I_SendDataNewHardDiskStorage = {
360
+ id: storage.value?.id || '',
361
+ name: storage.value?.name || '',
362
+ type: storage.value?.type_text || '',
363
+ pool: storage.value?.pool_name || '',
364
+ user: '',
365
+ endpoint: '',
366
+ password: '',
367
+ protocol: '',
368
+ options: '',
369
+ }
370
+
371
+ emits('send-data', {
372
+ create: props.hardDisk.create,
373
+ attach: props.hardDisk.attach,
374
+ source: props.hardDisk.source,
375
+ size: Math.ceil(sizeInMb.value),
376
+ bus: bus.value,
377
+ target: target.value,
378
+ storage: storageLocal,
379
+ device_type: 'disk',
380
+ provision_type: diskProvisioning.value,
381
+ disk_mode: diskMode.value,
382
+ sharing: sharing.value === 'yes',
383
+ read_only: readOnly.value,
384
+ shares: shares.value,
385
+ cache: cache.value,
386
+ io: io.value,
387
+ limit_iops: limitIopsLocal,
388
+ discard: discard.value,
389
+ capacity: Math.ceil(sizeInMb.value),
390
+ boot_order: props.hardDisk.boot_order,
391
+ detach: props.type === 'removed',
392
+ remove: props.type === 'removed' && deleteFilesFromDatastore.value, // Проверяем если удалили диск и указали Delete files from datastore
393
+ })
394
+ },
395
+ { immediate: true }
396
+ )
397
+
398
+ // Добавляем данные для редактирования
399
+ watch(
400
+ () => props.hardDisk,
401
+ (newValue) => {
402
+ // TODO refactoring
403
+ if (props.type === 'exist') {
404
+ diskProvisioning.value = newValue.provision_type || 'thick'
405
+ return
406
+ }
407
+ if (props.type !== 'edit') return
408
+
409
+ diskProvisioning.value = newValue.provision_type || 'thick'
410
+ sharing.value = newValue.sharing ? 'yes' : ''
411
+ shares.value = newValue.shares || 0
412
+ // limitIopsType.value = newValue.io === 'native' ? 'unlimited' : 'custom'
413
+ limitIopsType.value = newValue.limit_iops === 0 ? 'unlimited' : 'custom'
414
+ limitIops.value = newValue.limit_iops || 16
415
+ diskMode.value = newValue.disk_mode || 'dependent'
416
+ cache.value = newValue.cache || 'none'
417
+ bus.value = newValue.bus || 'ide'
418
+ readOnly.value = newValue.read_only || false
419
+ discard.value = newValue.discard || ''
420
+ io.value = newValue.io || ''
421
+ target.value = newValue.target || ''
422
+ },
423
+ { immediate: true }
424
+ )
425
+ watch(
426
+ isRunning,
427
+ (newValue) => {
428
+ if (newValue && props.type === 'new') {
429
+ bus.value = 'virtio'
430
+ }
431
+ },
432
+ { immediate: true }
433
+ )
434
+
435
+ const hardDiskInvalid = computed<boolean>(() => {
436
+ return !!hardDiskTypeErrorLocalText.value || limitIopsInvalid.value
437
+ })
438
+ watch(
439
+ hardDiskInvalid,
440
+ (newValue) => {
441
+ emits('invalid', newValue)
442
+ },
443
+ { immediate: true }
444
+ )
445
+ </script>
446
+
447
+ <style scoped lang="scss">
448
+ .hardware-cpu {
449
+ #hard-disk {
450
+ width: 100px;
451
+ -webkit-appearance: none;
452
+
453
+ &.has-error {
454
+ border: 1px solid #db2100;
455
+ }
456
+ }
457
+ }
458
+ </style>