发布 0.1.0:通知渠道、告警规则与多项体验修复
CI / test (push) Successful in 27s
Release / release (push) Successful in 26s

This commit is contained in:
Wang Defa
2026-07-10 17:31:19 +08:00
parent 9d0c116ce3
commit 5464d2dfea
68 changed files with 1604 additions and 359 deletions
+3 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NInput, NSelect, useMessage, type SelectOption } from 'naive-ui'
import { NInput, NSelect, type SelectOption } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { attachVnic } from '@/api/instances'
@@ -9,6 +9,7 @@ import FormModal from '@/components/FormModal.vue'
import ToggleCard from '@/components/ToggleCard.vue'
import { useAsync } from '@/composables/useAsync'
import { shortAd } from '@/composables/useFormat'
import { useToast } from '@/composables/useToast'
const props = defineProps<{
show: boolean
@@ -21,7 +22,7 @@ const props = defineProps<{
const emit = defineEmits<{ 'update:show': [boolean]; attached: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const subnetId = ref<string | null>(null)
const displayName = ref('')
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NCheckbox, NSelect, useMessage, type SelectGroupOption } from 'naive-ui'
import { NCheckbox, NSelect, type SelectGroupOption } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { listBootVolumes } from '@/api/bootVolumes'
@@ -7,6 +7,7 @@ import { attachVolume, listVolumes } from '@/api/instances'
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
@@ -19,7 +20,7 @@ const props = defineProps<{
const emit = defineEmits<{ 'update:show': [boolean]; attached: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const volumeId = ref<string | null>(null)
const readOnly = ref(false)
+3 -2
View File
@@ -1,15 +1,16 @@
<script setup lang="ts">
import { NInput, useMessage } from 'naive-ui'
import { NInput } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { createConsoleConnection } from '@/api/instances'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import { useToast } from '@/composables/useToast'
const props = defineProps<{ show: boolean; cfgId: number; instanceId: string; region?: string }>()
const emit = defineEmits<{ 'update:show': [boolean]; created: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const publicKey = ref('')
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NInput, useMessage } from 'naive-ui'
import { NInput } from 'naive-ui'
import { computed, reactive, ref, watch } from 'vue'
import { createInstances } from '@/api/instances'
@@ -8,6 +8,7 @@ import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import InstanceSpecForm from '@/components/instance/InstanceSpecForm.vue'
import { defaultResourceName } from '@/composables/useFormat'
import { useToast } from '@/composables/useToast'
/** 租户 / 区间 / 区域直接使用父页面当前值,不在表单中出现 */
const props = defineProps<{
@@ -18,7 +19,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{ 'update:show': [boolean]; created: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const specForm = ref<InstanceType<typeof InstanceSpecForm> | null>(null)
@@ -1,6 +1,4 @@
<script setup lang="ts">
import { useMessage } from 'naive-ui'
import AppInputNumber from '@/components/AppInputNumber.vue'
import { reactive, ref, watch } from 'vue'
@@ -8,6 +6,7 @@ import { updateBootVolume } from '@/api/bootVolumes'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import type { BootVolume } from '@/types/api'
import { useToast } from '@/composables/useToast'
const props = defineProps<{
show: boolean
@@ -17,7 +16,7 @@ const props = defineProps<{
}>()
const emit = defineEmits<{ 'update:show': [boolean]; updated: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const form = reactive({ sizeInGBs: 50, vpusPerGB: 10 })
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NSelect, useMessage } from 'naive-ui'
import { NSelect } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { listBootVolumes } from '@/api/bootVolumes'
@@ -7,6 +7,7 @@ import { replaceBootVolume } from '@/api/instances'
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
@@ -18,7 +19,7 @@ const props = defineProps<{
const emit = defineEmits<{ 'update:show': [boolean]; replaced: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const bootVolumeId = ref<string | null>(null)
+3 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NSelect, useMessage } from 'naive-ui'
import { NSelect } from 'naive-ui'
import { computed, reactive, ref, watch } from 'vue'
import { listShapes, updateInstance } from '@/api/instances'
@@ -7,6 +7,7 @@ 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
@@ -20,7 +21,7 @@ const props = defineProps<{
const emit = defineEmits<{ 'update:show': [boolean]; resized: [] }>()
const message = useMessage()
const message = useToast()
const submitting = ref(false)
const form = reactive({ shape: props.shape, ocpus: props.ocpus, memoryInGBs: props.memoryInGBs })
+5 -12
View File
@@ -2,11 +2,12 @@
import { FitAddon } from '@xterm/addon-fit'
import { Terminal } from '@xterm/xterm'
import '@xterm/xterm/css/xterm.css'
import { NSpin, useMessage } from 'naive-ui'
import { NSpin } from 'naive-ui'
import { nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { consoleSessionWsUrl, createConsoleSession, deleteConsoleSession } from '@/api/instances'
import { useAuthStore } from '@/stores/auth'
import { useToast } from '@/composables/useToast'
const props = defineProps<{
show: boolean
@@ -14,12 +15,11 @@ const props = defineProps<{
instanceId: string
region?: string
instanceName?: string
rootPassword?: string
}>()
const emit = defineEmits<{ 'update:show': [boolean] }>()
const message = useMessage()
const message = useToast()
const auth = useAuthStore()
const termEl = ref<HTMLDivElement | null>(null)
@@ -153,12 +153,6 @@ function syncWinsize() {
term.focus()
}
async function copyRootPassword() {
await navigator.clipboard.writeText(props.rootPassword ?? '')
message.success('已复制 root 密码')
term?.focus()
}
function reconnect() {
teardown()
void open()
@@ -250,7 +244,6 @@ onBeforeUnmount(teardown)
</span>
<span v-if="phase === 'creating'" class="text-xs">准备连接中</span>
<div class="flex-1" />
<button v-if="rootPassword" class="ser-btn" @click="copyRootPassword">复制 root 密码</button>
<button class="ser-btn" :disabled="phase !== 'connected'" @click="pasteFromClipboard">
粘贴
</button>
@@ -299,8 +292,8 @@ onBeforeUnmount(teardown)
<template v-else-if="phase === 'closed'">
<div class="max-w-[80%] break-all text-center">{{ error || '连接已关闭' }}</div>
<div class="max-w-[80%] text-center text-xs leading-relaxed">
串口登录需实例本地账户密码SSH 公钥无效创建实例时若启用随机 root
密码可从顶栏复制无密码也可查看启动日志或经 GRUB 单用户模式救援
串口登录需实例本地账户密码SSH 公钥无效随机 root 密码可在实例详情
验证身份后查看无密码也可查看启动日志或经 GRUB 单用户模式救援
</div>
<button class="ser-btn !border !border-[#3a3835] !px-3 !py-1" @click="reconnect">
重新连接
+3 -2
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { NButton, NPopconfirm, NSpin, useMessage } from 'naive-ui'
import { NButton, NPopconfirm, NSpin } from 'naive-ui'
import { computed, ref } from 'vue'
@@ -10,6 +10,7 @@ import LifecycleBadge from '@/components/LifecycleBadge.vue'
import OcidText from '@/components/OcidText.vue'
import { useAsync } from '@/composables/useAsync'
import { ATTACHMENT_TRANSIENT_STATES, useTransientPoll } from '@/composables/useTransientPoll'
import { useToast } from '@/composables/useToast'
/** 网卡(VNIC)区块:每卡一主行 + IPv6 副行;IPv6 增删按卡操作。
* 删除复用实例级接口(后端按地址遍历全部网卡),添加走 per-VNIC 接口。 */
@@ -23,7 +24,7 @@ const props = defineProps<{
const emit = defineEmits<{ changed: [] }>()
const message = useMessage()
const message = useToast()
const vnics = useAsync(() => listVnics(props.cfgId, props.instanceId, props.region))
useTransientPoll(