初始提交:OCI 面板前端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<script setup lang="ts">
|
||||
import { NSelect, useMessage } 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'
|
||||
|
||||
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 = useMessage()
|
||||
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>
|
||||
Reference in New Issue
Block a user