安全页重构:免密登录与活跃会话;按钮规范统一
CI / test (push) Successful in 53s

This commit is contained in:
2026-07-30 12:23:16 +08:00
parent d0fbecbd43
commit d675b950ee
43 changed files with 1944 additions and 475 deletions
+28 -16
View File
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { NButton, NDataTable, NInput, NSelect, NTooltip, useDialog, type DataTableColumns } from 'naive-ui'
import { NButton, NDataTable, NInput, NSelect, NTooltip, type DataTableColumns } from 'naive-ui'
import { computed, h, reactive, ref } from 'vue'
import { createAiChannel, deleteAiChannel, probeAiChannel, updateAiChannel } from '@/api/aigateway'
import { listCachedRegions } from '@/api/configs'
import AiChannelModelsModal from '@/components/ai/AiChannelModelsModal.vue'
import ConfirmPop from '@/components/ConfirmPop.vue'
import GroupChips from '@/components/ai/GroupChips.vue'
import PanelHeader from '@/components/PanelHeader.vue'
import AppInputNumber from '@/components/AppInputNumber.vue'
@@ -25,7 +26,6 @@ const emit = defineEmits<{ changed: [] }>()
const groupHints = computed(() => [...new Set(props.channels.map((c) => c.group).filter(Boolean))])
const toast = useToast()
const dialog = useDialog()
const scope = useScopeStore()
/** 名称列:渠道名关联租户,悬停出统一租户卡(别名/名称/主区域/前往租户);
* 名称本身不承载跳转,单元格内截断 */
@@ -135,28 +135,31 @@ function openModels(ch: AiChannel) {
showModels.value = true
}
/** 正在切换启用状态的渠道 id 集合,按行 loading 防连点 */
const toggling = ref(new Set<number>())
async function toggle(ch: AiChannel) {
if (toggling.value.has(ch.id)) return
toggling.value.add(ch.id)
try {
await updateAiChannel(ch.id, { enabled: !ch.enabled })
toast.success(ch.enabled ? '已停用' : '已启用')
emit('changed')
} catch (e) {
toast.error(e instanceof Error ? e.message : '操作失败')
} finally {
toggling.value.delete(ch.id)
}
}
function confirmRemove(ch: AiChannel) {
dialog.warning({
title: '删除渠道',
content: `删除渠道「${ch.name}」及其模型缓存?`,
positiveText: '删除',
negativeText: '取消',
onPositiveClick: async () => {
await deleteAiChannel(ch.id)
toast.success('已删除')
emit('changed')
},
})
async function doRemove(ch: AiChannel) {
try {
await deleteAiChannel(ch.id)
toast.success('已删除')
emit('changed')
} catch (e) {
toast.error(e instanceof Error ? e.message : '删除失败')
}
}
function probeCell(r: AiChannel) {
@@ -204,9 +207,18 @@ const columns = computed<DataTableColumns<AiChannel>>(() => [
render: (r) => h('div', { class: 'flex items-center gap-0.5' }, [
h(NButton, { size: 'tiny', quaternary: true, type: 'primary', loading: probing.value.has(r.id), onClick: () => doProbe(r.id) }, { default: () => '探测' }),
h(NButton, { size: 'tiny', quaternary: true, type: 'primary', onClick: () => openModels(r) }, { default: () => '模型列表' }),
h(NButton, { size: 'tiny', quaternary: true, onClick: () => toggle(r) }, { default: () => (r.enabled ? '停用' : '启用') }),
h(NButton, { size: 'tiny', quaternary: true, loading: toggling.value.has(r.id), onClick: () => toggle(r) }, { default: () => (r.enabled ? '停用' : '启用') }),
h(NButton, { size: 'tiny', quaternary: true, type: 'primary', onClick: () => openEdit(r) }, { default: () => '编辑' }),
h(NButton, { size: 'tiny', quaternary: true, type: 'error', onClick: () => confirmRemove(r) }, { default: () => '删除' }),
h(
ConfirmPop,
{
title: `删除渠道「${r.name}」?`,
content: '渠道及其模型缓存将一并删除。',
confirmText: '删除',
onConfirm: () => doRemove(r),
},
{ trigger: () => h(NButton, { size: 'tiny', quaternary: true, type: 'error' }, { default: () => '删除' }) },
),
]),
},
])