Files
oci-portal-dash/src/components/instance/ResizeModal.vue
T
Wang Defa 5464d2dfea
CI / test (push) Successful in 27s
Release / release (push) Successful in 26s
发布 0.1.0:通知渠道、告警规则与多项体验修复
2026-07-10 17:31:19 +08:00

148 lines
4.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { NSelect } from 'naive-ui'
import { computed, reactive, ref, watch } from 'vue'
import { listShapes, updateInstance } from '@/api/instances'
import AppInputNumber from '@/components/AppInputNumber.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import { useAsync } from '@/composables/useAsync'
import { useToast } from '@/composables/useToast'
const props = defineProps<{
show: boolean
cfgId: number
instanceId: string
region?: string
shape: string
ocpus: number
memoryInGBs: number
}>()
const emit = defineEmits<{ 'update:show': [boolean]; resized: [] }>()
const message = useToast()
const submitting = ref(false)
const form = reactive({ shape: props.shape, ocpus: props.ocpus, memoryInGBs: props.memoryInGBs })
const shapes = useAsync(() => listShapes(props.cfgId, props.region).catch(() => []), false)
watch(
() => props.show,
(v) => {
if (v) {
Object.assign(form, { shape: props.shape, ocpus: props.ocpus, memoryInGBs: props.memoryInGBs })
void shapes.run()
}
},
)
const BILLING_LABEL: Record<string, string> = {
ALWAYS_FREE: '永久免费',
LIMITED_FREE: '免费额度',
}
const shapeOptions = computed(() => {
const list = shapes.data.value ?? []
if (!list.length) return [{ label: form.shape, value: form.shape }]
return list.map((s) => {
const free = BILLING_LABEL[s.billingType]
return { label: s.name + (free ? `${free}` : ''), value: s.name }
})
})
const selectedShape = computed(
() => shapes.data.value?.find((s) => s.name === form.shape) ?? null,
)
const isFlex = computed(() => selectedShape.value?.isFlexible ?? form.shape.endsWith('.Flex'))
const ocpuRange = computed(() => ({
min: selectedShape.value?.ocpusMin || 1,
max: selectedShape.value?.ocpusMax || 128,
}))
const memRange = computed(() => ({
min: selectedShape.value?.memoryMinGBs || 1,
max: selectedShape.value?.memoryMaxGBs || 1024,
}))
// 换 shape 后把规格收敛到该 shape 的合法范围
watch(selectedShape, (s) => {
if (!s?.isFlexible) return
form.ocpus = Math.min(Math.max(form.ocpus, ocpuRange.value.min), ocpuRange.value.max)
form.memoryInGBs = Math.min(Math.max(form.memoryInGBs, memRange.value.min), memRange.value.max)
})
const changed = computed(
() =>
form.shape !== props.shape ||
(isFlex.value && (form.ocpus !== props.ocpus || form.memoryInGBs !== props.memoryInGBs)),
)
async function submit() {
submitting.value = true
try {
await updateInstance(
props.cfgId,
props.instanceId,
{
shape: form.shape !== props.shape ? form.shape : undefined,
ocpus: isFlex.value ? form.ocpus : undefined,
memoryInGBs: isFlex.value ? form.memoryInGBs : undefined,
},
props.region,
)
message.success('规格调整已提交,运行中实例将自动重启')
emit('update:show', false)
emit('resized')
} catch (e) {
message.error(e instanceof Error ? e.message : '调整失败')
} finally {
submitting.value = false
}
}
</script>
<template>
<FormModal
:show="show"
title="调整规格"
:submitting="submitting"
:submit-disabled="!changed"
submit-text="提交调整"
@update:show="emit('update:show', $event)"
@submit="submit"
>
<FormField label="Shape" required hint="可更换为租户当前区域提供的其他 shape,需目标容量可用">
<NSelect
v-model:value="form.shape"
filterable
tag
:options="shapeOptions"
:loading="shapes.loading.value"
/>
</FormField>
<div v-if="isFlex" class="grid grid-cols-2 gap-3 max-md:grid-cols-1">
<FormField label="OCPU" required :hint="`可选 ${ocpuRange.min}${ocpuRange.max}`">
<AppInputNumber
v-model:value="form.ocpus"
:min="ocpuRange.min"
:max="ocpuRange.max"
class="w-full"
/>
</FormField>
<FormField label="内存(GB" required :hint="`可选 ${memRange.min}${memRange.max} GB`">
<AppInputNumber
v-model:value="form.memoryInGBs"
:min="memRange.min"
:max="memRange.max"
class="w-full"
/>
</FormField>
</div>
<div class="text-xs leading-relaxed text-ink-3">
运行中实例 OCI 会自动重启完成变更更换 shape 要求镜像与目标 shape 架构兼容 aarch64
镜像不能换到 x86 shape)。A1 免费额度上限 4 OCPU / 24 GB
</div>
</FormModal>
</template>