|
|
@@ -1,37 +1,66 @@
|
|
|
<template>
|
|
|
<div class="ai-page ai-model">
|
|
|
- <!-- 页签栏即顶部条:页签居左,摘要与操作经 rightExtra 落在同一行右侧 -->
|
|
|
- <a-tabs v-model:active-key="activeTab" class="ai-model__tabs" size="small" destroy-inactive-tab-pane>
|
|
|
- <template #rightExtra>
|
|
|
- <div class="ai-topbar__actions">
|
|
|
- <a-button :loading="loading" @click="load">
|
|
|
- <template #icon><Icon icon="mdi:refresh" :size="14" /></template>
|
|
|
- 刷新
|
|
|
- </a-button>
|
|
|
- <a-button type="primary" @click="openCreate">
|
|
|
- <template #icon><Icon icon="mdi:plus" :size="14" /></template>
|
|
|
- 新增{{ activeMeta.label }}
|
|
|
- </a-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
-
|
|
|
- <a-tab-pane v-for="tab in TABS" :key="tab.key">
|
|
|
- <template #tab>
|
|
|
+ <!-- 顶部工具卡:页签切换 + 摘要 + 当前页签操作(与插件与技能页同一套卡片规范) -->
|
|
|
+ <section class="ai-model__toolbar">
|
|
|
+ <div class="ai-model__card-title">模型管理</div>
|
|
|
+ <div class="ai-model__tabs" role="tablist" aria-label="模型分类">
|
|
|
+ <button
|
|
|
+ v-for="tab in TABS"
|
|
|
+ :key="tab.key"
|
|
|
+ type="button"
|
|
|
+ role="tab"
|
|
|
+ class="ai-model__tab"
|
|
|
+ :class="{ 'is-on': activeTab === tab.key }"
|
|
|
+ :aria-selected="activeTab === tab.key"
|
|
|
+ @click="activeTab = tab.key"
|
|
|
+ >
|
|
|
+ <Icon :icon="tab.icon" :size="14" />
|
|
|
<span>{{ tab.label }}</span>
|
|
|
<span class="ai-model__tab-count">{{ listOf(tab.key).length }}</span>
|
|
|
- </template>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <span class="ai-model__summary">{{ summary }}</span>
|
|
|
+ <span class="ai-model__spacer"></span>
|
|
|
|
|
|
+ <div class="ai-model__actions">
|
|
|
+ <a-button :loading="loading" @click="load">
|
|
|
+ <template #icon><Icon icon="mdi:refresh" :size="14" /></template>
|
|
|
+ 刷新
|
|
|
+ </a-button>
|
|
|
+ <a-button type="primary" @click="openCreate">
|
|
|
+ <template #icon><Icon icon="mdi:plus" :size="14" /></template>
|
|
|
+ 新增{{ activeMeta.label }}
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <!-- 内容卡:标题/说明 + 模型卡片网格 -->
|
|
|
+ <section class="ai-model__panel">
|
|
|
+ <div class="ai-model__panel-head">
|
|
|
+ <span class="ai-model__card-title">{{ activeMeta.label }}</span>
|
|
|
+ <span class="ai-model__panel-desc">{{ activeMeta.desc }}</span>
|
|
|
+ <span class="ai-model__spacer"></span>
|
|
|
+ <span class="ai-model__panel-count">共 {{ currentModels.length }} 个</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="ai-model__panel-body">
|
|
|
<a-spin :spinning="loading">
|
|
|
- <div v-if="listOf(tab.key).length" class="ai-model__grid">
|
|
|
- <article v-for="record in listOf(tab.key)" :key="record.id" class="ai-model__card">
|
|
|
+ <div v-if="currentModels.length" class="ai-model__grid">
|
|
|
+ <article v-for="record in currentModels" :key="record.id" class="ai-model__card">
|
|
|
<div class="ai-model__card-head">
|
|
|
- <div class="ai-model__name">
|
|
|
- <span class="ai-model__name-text ai-model__ellipsis" :title="record.name || record.modelId">
|
|
|
- {{ record.name || record.modelId }}
|
|
|
- </span>
|
|
|
- <span v-if="record.defaultModel" class="ai-model__badge">
|
|
|
- 默认{{ tab.key === 'embedding' ? '向量' : '对话' }}
|
|
|
- </span>
|
|
|
+ <span class="ai-model__card-icon" :class="activeTab === 'embedding' ? 'is-embedding' : 'is-llm'">
|
|
|
+ <Icon :icon="activeMeta.icon" :size="18" />
|
|
|
+ </span>
|
|
|
+ <div class="ai-model__card-heading">
|
|
|
+ <div class="ai-model__name-row">
|
|
|
+ <span class="ai-model__name" :title="record.name || record.modelId">
|
|
|
+ {{ record.name || record.modelId }}
|
|
|
+ </span>
|
|
|
+ <span v-if="record.defaultModel" class="ai-model__chip is-primary">
|
|
|
+ 默认{{ activeTab === 'embedding' ? '向量' : '对话' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <span class="ai-model__card-sub">{{ record.provider || '未指定厂商' }}</span>
|
|
|
</div>
|
|
|
<span class="ai-model__status" :class="statusMeta(record.status).cls">
|
|
|
{{ statusMeta(record.status).label }}
|
|
|
@@ -39,40 +68,59 @@
|
|
|
</div>
|
|
|
|
|
|
<div class="ai-model__card-meta">
|
|
|
- <span class="ai-model__chip">{{ record.provider || '—' }}</span>
|
|
|
- <span class="ai-model__mono">{{ record.modelId || '—' }}</span>
|
|
|
+ <span class="ai-model__meta-label">模型 ID</span>
|
|
|
+ <span class="ai-model__mono ai-model__ellipsis" :title="record.modelId">{{
|
|
|
+ record.modelId || '—'
|
|
|
+ }}</span>
|
|
|
</div>
|
|
|
|
|
|
<div class="ai-model__card-meta">
|
|
|
- <span class="ai-model__mono ai-model__ellipsis" :title="record.baseUrl">
|
|
|
- {{ record.baseUrl || '—' }}
|
|
|
- </span>
|
|
|
+ <span class="ai-model__meta-label">接口地址</span>
|
|
|
+ <span class="ai-model__mono ai-model__ellipsis" :title="record.baseUrl">{{
|
|
|
+ record.baseUrl || '—'
|
|
|
+ }}</span>
|
|
|
</div>
|
|
|
|
|
|
- <div class="ai-model__sub">更新:{{ record.updatedAt || record.createdAt || '—' }}</div>
|
|
|
+ <div class="ai-model__card-sub">更新:{{ record.updatedAt || record.createdAt || '—' }}</div>
|
|
|
|
|
|
<div class="ai-model__card-ops">
|
|
|
- <a-button type="link" size="small" :loading="!!testing[record.id]" @click="onTestConnection(record)">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="ai-model__op"
|
|
|
+ :disabled="!!testing[record.id]"
|
|
|
+ @click="onTestConnection(record)"
|
|
|
+ >
|
|
|
+ <Icon v-if="testing[record.id]" icon="mdi:loading" :size="12" spin />
|
|
|
测试连接
|
|
|
- </a-button>
|
|
|
- <a-button type="link" size="small" @click="openEdit(record)">编辑</a-button>
|
|
|
- <a-button type="link" size="small" :disabled="record.defaultModel" @click="onSetDefault(record, tab)">
|
|
|
+ </button>
|
|
|
+ <button type="button" class="ai-model__op" @click="openEdit(record)">编辑</button>
|
|
|
+ <span class="ai-model__ops-sp"></span>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ class="ai-model__op"
|
|
|
+ :disabled="record.defaultModel"
|
|
|
+ @click="onSetDefault(record, activeMeta)"
|
|
|
+ >
|
|
|
{{ record.defaultModel ? '已是默认' : '设为默认' }}
|
|
|
- </a-button>
|
|
|
- <a-button type="link" size="small" danger @click="onDelete(record)">删除</a-button>
|
|
|
+ </button>
|
|
|
+ <button type="button" class="ai-model__op is-danger" @click="onDelete(record)">删除</button>
|
|
|
</div>
|
|
|
</article>
|
|
|
</div>
|
|
|
- <div v-else-if="!loading" class="ai-model__empty">
|
|
|
- {{
|
|
|
- tab.key === 'embedding'
|
|
|
- ? '还没有向量模型。向量模型用于表结构语义检索,点击右上角「新增向量模型」添加。'
|
|
|
- : '还没有对话模型。点击右上角「新增对话模型」添加一个,AI 对话才能开始工作。'
|
|
|
- }}
|
|
|
- </div>
|
|
|
+ <EmptyState
|
|
|
+ v-else-if="!loading"
|
|
|
+ :title="activeTab === 'embedding' ? '还没有向量模型' : '还没有对话模型'"
|
|
|
+ :description="
|
|
|
+ activeTab === 'embedding'
|
|
|
+ ? '向量模型用于表结构语义检索,点击右上角「新增向量模型」添加。'
|
|
|
+ : '点击右上角「新增对话模型」添加一个,AI 对话才能开始工作。'
|
|
|
+ "
|
|
|
+ :min-height="300"
|
|
|
+ description-width="min(100%, 520px)"
|
|
|
+ />
|
|
|
</a-spin>
|
|
|
- </a-tab-pane>
|
|
|
- </a-tabs>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
|
|
|
<ModelFormModal
|
|
|
v-model:open="formOpen"
|
|
|
@@ -94,7 +142,8 @@
|
|
|
* 模型数量为个位数级,故不做服务端分页,列表一次拉全量。
|
|
|
*/
|
|
|
import { computed, onMounted, ref } from 'vue';
|
|
|
- import { Button, Modal, Spin, Tabs } from 'ant-design-vue';
|
|
|
+ import { Button, Modal, Spin } from 'ant-design-vue';
|
|
|
+ import { EmptyState } from '@/core/components/EmptyState';
|
|
|
import Icon from '@/core/components/Icon/src/Icon.vue';
|
|
|
import { useMessage } from '@/core/hooks/web/useMessage';
|
|
|
import * as modelApi from '../../api/modelApi';
|
|
|
@@ -103,8 +152,6 @@
|
|
|
|
|
|
const AButton = Button;
|
|
|
const ASpin = Spin;
|
|
|
- const ATabs = Tabs;
|
|
|
- const ATabPane = Tabs.TabPane;
|
|
|
|
|
|
const { showMessage } = useMessage();
|
|
|
|
|
|
@@ -114,11 +161,23 @@
|
|
|
interface ModelTab {
|
|
|
key: ModelTypeKey;
|
|
|
label: string;
|
|
|
+ icon: string;
|
|
|
+ desc: string;
|
|
|
}
|
|
|
|
|
|
const TABS: ModelTab[] = [
|
|
|
- { key: 'llm', label: '对话模型' },
|
|
|
- { key: 'embedding', label: '向量模型' },
|
|
|
+ {
|
|
|
+ key: 'llm',
|
|
|
+ label: '对话模型',
|
|
|
+ icon: 'mdi:message-text-outline',
|
|
|
+ desc: '对话与工具调用使用的大语言模型,每个类型各保留一个默认模型。',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ key: 'embedding',
|
|
|
+ label: '向量模型',
|
|
|
+ icon: 'mdi:database-search-outline',
|
|
|
+ desc: '用于表结构语义检索的向量模型,与对话模型分别维护默认项。',
|
|
|
+ },
|
|
|
];
|
|
|
|
|
|
const models = ref<AiModel[]>([]);
|
|
|
@@ -138,6 +197,10 @@
|
|
|
return key === 'embedding' ? embeddingModels.value : llmModels.value;
|
|
|
}
|
|
|
|
|
|
+ const currentModels = computed(() => listOf(activeTab.value));
|
|
|
+
|
|
|
+ const summary = computed(() => `${llmModels.value.length} 个对话模型 · ${embeddingModels.value.length} 个向量模型`);
|
|
|
+
|
|
|
/**
|
|
|
* 状态展示:后端 StatusEnum 为 ACTIVE / SUSPENDED / ARCHIVED / DISABLED / DELETED
|
|
|
* (DELETED 已被列表接口过滤,不会出现)
|
|
|
@@ -445,4 +508,529 @@
|
|
|
border-top: 1px solid var(--ai-border);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /* ======================================================================
|
|
|
+ * 卡片化改版:对齐自主模式「持续联系」页与插件与技能页的视觉规范
|
|
|
+ * 页面底 #f4f6fa · 白卡圆角 14 · 描边 #e7ebf3 · 标题蓝条 · 主按钮 #2a50ec
|
|
|
+ * ==================================================================== */
|
|
|
+ .ai-page.ai-model {
|
|
|
+ gap: 8px;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 0;
|
|
|
+ margin: 0;
|
|
|
+ padding: 8px 10px;
|
|
|
+ background: #f4f6fa;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model {
|
|
|
+ &__toolbar {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ min-height: 52px;
|
|
|
+ padding: 10px 14px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 14px;
|
|
|
+ box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-title {
|
|
|
+ position: relative;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ padding-left: 11px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 22px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ top: 4px;
|
|
|
+ bottom: 4px;
|
|
|
+ left: 0;
|
|
|
+ width: 3px;
|
|
|
+ background: #2a50ec;
|
|
|
+ border-radius: 2px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__tabs {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 2px;
|
|
|
+ padding: 3px;
|
|
|
+ background: #f4f6fa;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__tab {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 10px;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12.5px;
|
|
|
+ font-weight: 500;
|
|
|
+ font-family: inherit;
|
|
|
+ line-height: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ background: transparent;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ color 0.15s ease,
|
|
|
+ background-color 0.15s ease,
|
|
|
+ box-shadow 0.15s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #2a50ec;
|
|
|
+ background: rgb(42 80 236 / 6%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-on {
|
|
|
+ color: #2a50ec;
|
|
|
+ font-weight: 600;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0 1px 3px rgb(16 24 40 / 10%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__tab-count {
|
|
|
+ display: inline-block;
|
|
|
+ min-width: 18px;
|
|
|
+ margin-left: 0;
|
|
|
+ padding: 0 5px;
|
|
|
+ color: inherit;
|
|
|
+ font-size: 11px;
|
|
|
+ line-height: 16px;
|
|
|
+ text-align: center;
|
|
|
+ background: rgb(42 80 236 / 8%);
|
|
|
+ border: 0;
|
|
|
+ border-radius: 999px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__summary {
|
|
|
+ max-width: 220px;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__spacer {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__actions {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+
|
|
|
+ :deep(.ant-btn) {
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 14px;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: none;
|
|
|
+
|
|
|
+ &:hover,
|
|
|
+ &:focus {
|
|
|
+ color: #2a50ec;
|
|
|
+ background: #f8faff;
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ant-btn-primary) {
|
|
|
+ color: #fff;
|
|
|
+ background: #2a50ec;
|
|
|
+ border-color: #2a50ec;
|
|
|
+
|
|
|
+ &:hover,
|
|
|
+ &:focus {
|
|
|
+ color: #fff;
|
|
|
+ background: #4b6bf0;
|
|
|
+ border-color: #4b6bf0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ background: #1e3ed0;
|
|
|
+ border-color: #1e3ed0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ant-btn[disabled]) {
|
|
|
+ color: rgb(0 0 0 / 25%);
|
|
|
+ background: #f5f5f5;
|
|
|
+ border-color: #e7ebf3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__panel {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ min-height: 0;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 14px;
|
|
|
+ box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__panel-head {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ min-height: 48px;
|
|
|
+ padding: 8px 14px;
|
|
|
+ border-bottom: 1px solid #eff2f7;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__panel-desc {
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__panel-count {
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ font-variant-numeric: tabular-nums;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__panel-body {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 14px;
|
|
|
+ overflow-y: auto;
|
|
|
+ overscroll-behavior: contain;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card {
|
|
|
+ gap: 10px;
|
|
|
+ padding: 14px;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ transition:
|
|
|
+ border-color 0.15s ease,
|
|
|
+ box-shadow 0.15s ease;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ box-shadow: 0 4px 14px rgb(16 24 40 / 8%);
|
|
|
+ transform: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-head {
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: flex-start;
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-icon {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 34px;
|
|
|
+ height: 34px;
|
|
|
+ color: #2a50ec;
|
|
|
+ background: rgb(42 80 236 / 8%);
|
|
|
+ border-radius: 9px;
|
|
|
+
|
|
|
+ &.is-embedding {
|
|
|
+ color: #7c3aed;
|
|
|
+ background: rgb(124 58 237 / 8%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-heading {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__name-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__name {
|
|
|
+ display: block;
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-sub {
|
|
|
+ overflow: hidden;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ font-family: inherit;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ font-variant-numeric: tabular-nums;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__chip {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ max-width: 220px;
|
|
|
+ height: 20px;
|
|
|
+ padding: 0 7px;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 18px;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ background: #f4f6fa;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &.is-primary {
|
|
|
+ color: #2a50ec;
|
|
|
+ background: rgb(42 80 236 / 7%);
|
|
|
+ border-color: rgb(42 80 236 / 18%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__status {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 5px;
|
|
|
+ height: 22px;
|
|
|
+ padding: 0 8px;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 11px;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+ background: #f4f6fa;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 6px;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ width: 6px;
|
|
|
+ height: 6px;
|
|
|
+ background: currentColor;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-on {
|
|
|
+ color: #12805c;
|
|
|
+ background: rgb(18 128 92 / 8%);
|
|
|
+ border-color: rgb(18 128 92 / 20%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-warn {
|
|
|
+ color: #b7791f;
|
|
|
+ background: rgb(183 121 31 / 8%);
|
|
|
+ border-color: rgb(183 121 31 / 20%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-off {
|
|
|
+ color: #5b6577;
|
|
|
+ background: #f4f6fa;
|
|
|
+ border-color: #e7ebf3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-meta {
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__meta-label {
|
|
|
+ flex: 0 0 48px;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11.5px;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__mono {
|
|
|
+ min-width: 0;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 11.5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__card-ops {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ margin-top: auto;
|
|
|
+ padding-top: 10px;
|
|
|
+ border-top: 1px solid #eff2f7;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__ops-sp {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ &__op {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 4px;
|
|
|
+ height: 26px;
|
|
|
+ padding: 0 10px;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 11.5px;
|
|
|
+ font-weight: 500;
|
|
|
+ font-family: inherit;
|
|
|
+ white-space: nowrap;
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 6px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ color 0.15s ease,
|
|
|
+ background-color 0.15s ease,
|
|
|
+ border-color 0.15s ease;
|
|
|
+
|
|
|
+ &:hover:not(:disabled) {
|
|
|
+ color: #2a50ec;
|
|
|
+ background: #f8faff;
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:disabled {
|
|
|
+ cursor: not-allowed;
|
|
|
+ opacity: 0.55;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-danger:hover:not(:disabled) {
|
|
|
+ color: #e5484d;
|
|
|
+ background: #fff5f5;
|
|
|
+ border-color: rgb(229 72 77 / 35%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 1440px) {
|
|
|
+ .ai-model__summary {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 1180px) {
|
|
|
+ .ai-model__panel-desc {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 1120px) {
|
|
|
+ .ai-model__tab .jeesite-icon {
|
|
|
+ display: none !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+ /* 新增/编辑模型弹框:与页面卡片统一的圆角、控件尺寸与按钮规范 */
|
|
|
+ /*
|
|
|
+ * 项目全局对 .ant-modal 有过高优先级覆盖(:not(...) 链),这里必须用 !important
|
|
|
+ * 才能把圆角与头部配色统一到页面卡片规范。
|
|
|
+ */
|
|
|
+ .ai-model-modal .ant-modal-content {
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 14px !important;
|
|
|
+ padding: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-modal-header {
|
|
|
+ margin: 0 !important;
|
|
|
+ padding: 14px 20px !important;
|
|
|
+ background: #fff !important;
|
|
|
+ border-bottom: 1px solid #eff2f7 !important;
|
|
|
+ border-radius: 14px 14px 0 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-modal-title {
|
|
|
+ color: #111827 !important;
|
|
|
+ font-size: 15px !important;
|
|
|
+ font-weight: 600 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-modal-close {
|
|
|
+ top: 14px;
|
|
|
+ right: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-modal-body {
|
|
|
+ padding: 16px 20px 8px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-form-item-label > label {
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-input,
|
|
|
+ .ai-model-modal .ant-input-affix-wrapper,
|
|
|
+ .ai-model-modal .ant-select-selector {
|
|
|
+ min-height: 32px;
|
|
|
+ border-color: #e7ebf3 !important;
|
|
|
+ border-radius: 8px !important;
|
|
|
+ box-shadow: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-select-selector {
|
|
|
+ padding: 0 10px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ai-model-modal .ant-input:focus,
|
|
|
+ .ai-model-modal .ant-input-affix-wrapper-focused,
|
|
|
+ .ai-model-modal .ant-select-focused .ant-select-selector {
|
|
|
+ border-color: #2a50ec !important;
|
|
|
+ box-shadow: 0 0 0 2px rgb(42 80 236 / 12%) !important;
|
|
|
+ }
|
|
|
</style>
|