feat(webui): WebUI 管理后台新增 AII 管理栏目

在 WebUI NyaHome 管理后台中实现 AII 管理栏目,用于在线修改模型设置。
同时在后端补全了两个路由端点。
This commit is contained in:
2026-06-01 20:42:16 +08:00
parent 45e255856a
commit 7df66bbc61
13 changed files with 571 additions and 109 deletions
@@ -0,0 +1,186 @@
<script setup lang="ts">
import { type SelectOption, useMessage } from 'naive-ui'
import { computed, onMounted, ref, watch } from 'vue'
import AiiProviderAddModal from '@/components/aii/AiiProviderAddModal.vue'
import { aiiModelRules, check_remote_model } from '@/tools/avaliable-check.ts'
import { api } from '@/tools/web.js'
import type { AiiModelPublic, AiiProviderPublicWithoutKey } from '@/types/aii.js'
import type { ReturnDto } from '@/types/response.js'
const MESSAGE = useMessage()
const showModal = defineModel<boolean>('showModal', { required: true })
const { reload } = defineProps<{
noAddProvider?: boolean
reload?: () => void
}>()
const showAddProviderModal = ref(false)
const selectProvider = ref<number | null>(null)
const providers = ref<AiiProviderPublicWithoutKey[]>([])
const remoteModels = ref<string[]>([])
const addModelForm = ref({
id: 0,
model_name: '',
max_context_length: 0,
reasonable: false,
aii_provider_id: selectProvider.value,
})
watch(selectProvider, (newValue) => {
addModelForm.value.aii_provider_id = newValue
})
function loadProviders() {
api
.get('/aii/provider/')
.then((res) => res.data as AiiProviderPublicWithoutKey[])
.then((result) => {
providers.value = result
MESSAGE.success(`成功加载了 ${result.length} 个模型提供商。`)
})
.catch((err) => {
MESSAGE.error(`获取模型提供商列表失败:${err}`)
})
}
const providerOptions = computed<SelectOption[]>(() => {
const options = [] as SelectOption[]
for (const ap of providers.value) {
options.push({
label: `[${ap.id}] [ ${ap.name} ] ( ${ap.base_url} )`,
value: ap.id,
})
}
return options
})
onMounted(() => {
loadProviders()
})
function onGetRemoteModels() {
api
.get(`/aii/provider/${selectProvider.value}/remote/models/`)
.then((res) => res.data as ReturnDto)
.then((data) => {
if (data.success) {
return data.result as string[]
} else {
throw TypeError('由于未知原因,后端业务错误。')
}
})
.then((models) => {
remoteModels.value = models
MESSAGE.success(`成功获取模型提供商 ${selectProvider.value}${models.length} 个模型。`)
})
.catch((err) => {
MESSAGE.error(`获取提供商的模型列表失败:${err}`)
})
}
async function onCheck() {
if (selectProvider.value) {
if (await check_remote_model(selectProvider.value, addModelForm.value.model_name)) {
MESSAGE.success(`提供商的模型 ${addModelForm.value.model_name} 可用。`)
} else {
MESSAGE.warning(`提供商的模型 ${addModelForm.value.model_name} 不可用。`)
}
} else {
MESSAGE.warning('请选择模型提供商。')
}
}
function onConfirm() {
api
.post('/aii/model/', JSON.stringify(addModelForm.value))
.then((res) => res.data as AiiModelPublic)
.then(() => {
MESSAGE.success(`模型 ${addModelForm.value.model_name} 成功添加。`)
showModal.value = false
if (reload) reload()
})
.catch((err) => {
MESSAGE.error(`添加模型失败:${err}`)
})
}
</script>
<template>
<n-modal v-model:show="showModal" preset="card" title="添加模型">
<n-form
:model="addModelForm"
label-placement="left"
label-width="auto"
label-align="right"
:rules="aiiModelRules"
>
<n-form-item label="模型提供商" path="aii_provider_id">
<n-flex style="width: 100%" justify="right" align="center">
<n-select v-model:value="selectProvider" :options="providerOptions" />
<n-tag round type="info" v-if="!noAddProvider">修改已添加的提供商请前往管理中心</n-tag>
<n-button
secondary
type="success"
size="small"
round
@click="loadProviders()"
v-if="!noAddProvider"
>
刷新
</n-button>
<n-button
secondary
type="warning"
size="small"
round
@click="showAddProviderModal = true"
v-if="!noAddProvider"
>
添加
</n-button>
</n-flex>
</n-form-item>
<n-form-item label="模型名称" path="model_name">
<n-flex style="width: 100%" justify="right" align="center">
<n-input v-model:value="addModelForm.model_name" />
<n-flex style="overflow: auto">
<n-button
secondary
type="info"
size="small"
round
v-for="m in remoteModels"
v-bind:key="m"
@click="addModelForm.model_name = m"
>{{ m }}
</n-button>
</n-flex>
<n-button secondary type="success" size="small" round @click="onGetRemoteModels()"
>获取模型列表
</n-button>
</n-flex>
</n-form-item>
<n-form-item label="最大上下文" path="max_context_length">
<n-input-number v-model:value="addModelForm.max_context_length">
<template #suffix>K</template>
</n-input-number>
</n-form-item>
<n-form-item label="支持思考">
<n-switch v-model:value="addModelForm.reasonable" />
</n-form-item>
<n-form-item label="添加完成">
<n-flex>
<n-button secondary type="info" @click="onCheck()">检测</n-button>
<n-button secondary type="primary" @click="onConfirm()">确认</n-button>
</n-flex>
</n-form-item>
</n-form>
<aii-provider-add-modal v-model:show-modal="showAddProviderModal" />
</n-modal>
</template>
<style scoped></style>