@epxing/business 5.3.0-beta.2 → 5.3.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +115 -111
- package/README.md +44 -44
- package/dist/src/base/AiSkillEditor.js +1 -1
- package/dist/src/base/AiSkillEditor.vue.js +2 -0
- package/dist/src/base/AppConfigFormItem.js +1 -3
- package/dist/src/base/AppConfigFormItem.vue.js +210 -85
- package/dist/src/base/CommonFieldList.js +1 -1
- package/dist/src/base/CommonFieldList.vue.js +2 -1
- package/dist/src/base/ConfigModuleIndex.js +38 -1
- package/dist/src/base/ManageAppNav.js +1 -1
- package/dist/src/base/ManageMenu.js +1 -1
- package/dist/src/base/RoleBindTrace.js +253 -0
- package/dist/src/base/RoleGroupManage.js +1037 -0
- package/dist/src/base/ServiceMenu.js +7 -6
- package/dist/src/components/AiList.js +1278 -3
- package/dist/src/components/EntityCheckboxFind.vue.js +14 -10
- package/dist/src/components/EntityRightFormItem.js +1 -5
- package/dist/src/components/EntityRightFormItem.vue.js +3 -1
- package/dist/src/components/EntitySystemEr.js +492 -0
- package/dist/src/components/HallNav.vue.js +7 -36
- package/dist/src/components/IconFontLibrary.js +22 -1
- package/dist/src/components/IconFontList.js +21 -1
- package/dist/src/components/PrintFormList.js +2 -1
- package/dist/src/components/PrintFormList.vue.js +3 -1
- package/dist/src/components/WikiWorkbench.js +153 -129
- package/dist/src/components/authorization/AuthorizationInterface.js +3 -1
- package/dist/src/components/custom-bpmn/PropertiesView.js +1991 -1942
- package/dist/src/components/icon-gallery/IconGallery.js +193 -0
- package/dist/src/components/icon-gallery/mixin.js +150 -0
- package/dist/src/components/wiki-workbench/WikiIntake.js +86 -93
- package/dist/src/components/wiki-workbench/WikiIntake.vue.js +153 -73
- package/dist/src/index.js +26 -20
- package/dist/src/public-components.js +16 -14
- package/dist/src/route-components.js +5 -5
- package/dist/src/system.component.index.js +1 -1
- package/dist/src/system.component.js +8 -3
- package/dist/src/system.component.lazy.js +1 -1
- package/dist/src/views/Add.js +1 -1
- package/dist/src/views/Add.vue.js +8 -3
- package/dist/src/views/AiToolManager.js +792 -317
- package/dist/src/views/App.js +3 -45
- package/dist/src/views/AppRoleSelect.js +3 -230
- package/dist/src/views/Error.js +3 -207
- package/dist/src/views/ExcelDesign.js +326 -316
- package/dist/src/views/JvmMemoryMonitor.js +463 -0
- package/dist/src/views/Module.js +3 -170
- package/dist/src/views/WikiBinding.js +88 -25
- package/dist/style.css +2867 -1487
- package/package.json +12 -6
- package/src/base/AiSkillEditor.vue +2 -0
- package/src/base/AppConfigFormItem.vue +211 -145
- package/src/base/CommonFieldList.vue +9 -6
- package/src/base/ConfigModuleIndex.vue +37 -1
- package/src/base/ManageAppNav.vue +1188 -1188
- package/src/base/ManageMenu.vue +933 -933
- package/src/base/RoleBindTrace.vue +364 -0
- package/src/base/RoleGroupManage.vue +1160 -0
- package/src/base/ServiceMenu.vue +6 -5
- package/src/components/AiList.vue +439 -113
- package/src/components/EntityCheckboxFind.vue +12 -8
- package/src/components/EntityRightFormItem.vue +9 -10
- package/src/components/EntitySystemEr.vue +517 -0
- package/src/components/HallNav.vue +6 -33
- package/src/components/IconFontLibrary.vue +27 -60
- package/src/components/IconFontList.vue +13 -64
- package/src/components/PrintFormList.vue +33 -27
- package/src/components/WikiWorkbench.vue +90 -60
- package/src/components/authorization/AuthorizationInterface.vue +2 -0
- package/src/components/custom-bpmn/CustomPalette.js +1 -1
- package/src/components/custom-bpmn/CustomRenderer.js +1 -1
- package/src/components/custom-bpmn/PropertiesView.vue +35 -4
- package/src/components/custom-bpmn/index.js +1 -1
- package/src/components/icon-gallery/IconGallery.vue +130 -0
- package/src/components/icon-gallery/mixin.jsx +109 -0
- package/src/components/wiki-workbench/WikiIntake.vue +178 -104
- package/src/index.js +24 -20
- package/src/public-components.js +1 -0
- package/src/route-components.js +4 -4
- package/src/system.component.index.js +1 -1
- package/src/views/Add.vue +8 -5
- package/src/views/AiToolManager.vue +326 -4
- package/src/views/App.vue +4 -53
- package/src/views/AppRoleSelect.vue +4 -334
- package/src/views/Error.vue +4 -63
- package/src/views/ExcelDesign.vue +24 -7
- package/src/views/JvmMemoryMonitor.vue +284 -0
- package/src/views/Module.vue +4 -210
- package/src/views/WikiBinding.vue +42 -0
- package/types/app.d.ts +1 -0
- package/types/dept-find.d.ts +1 -1
- package/types/index.d.ts +15 -14
- package/types/popup.d.ts +2 -2
- package/types/route-components.d.ts +1 -1
- package/types/user-find.d.ts +1 -1
- package/dist/assets/role_select-DTqRlFNz.webp +0 -0
- package/dist/src/components/AiList.vue.js +0 -93
- package/dist/src/components/IconFontLibrary.vue.js +0 -67
- package/dist/src/components/IconFontList.vue.js +0 -79
package/src/base/ServiceMenu.vue
CHANGED
|
@@ -183,8 +183,9 @@ export default {
|
|
|
183
183
|
this.clickItem = { id: item.id, ...item }
|
|
184
184
|
const { detailPageFlag, detailPageFlagM } = this.$configs
|
|
185
185
|
// this.load = false
|
|
186
|
-
this.
|
|
187
|
-
this
|
|
186
|
+
const isMobileMenu = this.mode === 'move' || (this.mode === 'all' && item.TERMINAL === 'move')
|
|
187
|
+
this.pageFlag = isMobileMenu ? detailPageFlagM : detailPageFlag
|
|
188
|
+
this.emitCreatePage({
|
|
188
189
|
metaId: 'FM_MODULE',
|
|
189
190
|
flag: this.pageFlag,
|
|
190
191
|
component: 'entity-add-page',
|
|
@@ -207,7 +208,7 @@ export default {
|
|
|
207
208
|
return
|
|
208
209
|
}
|
|
209
210
|
const { detailPageFlag, detailPageFlagM } = this.$configs
|
|
210
|
-
this
|
|
211
|
+
this.emitCreatePage({
|
|
211
212
|
metaId: 'FM_MODULE',
|
|
212
213
|
flag: this.mode === 'pc' ? detailPageFlag : detailPageFlagM,
|
|
213
214
|
component: 'entity-add-page',
|
|
@@ -224,7 +225,7 @@ export default {
|
|
|
224
225
|
const { detailPageFlag, detailPageFlagM } = this.configs
|
|
225
226
|
this.load = false
|
|
226
227
|
this.pageFlag = this.mode === 'pc' ? detailPageFlag : detailPageFlagM
|
|
227
|
-
this
|
|
228
|
+
this.emitCreatePage({
|
|
228
229
|
metaId: 'FM_MODULE',
|
|
229
230
|
flag: this.pageFlag,
|
|
230
231
|
pageType: 'EDIT',
|
|
@@ -256,7 +257,7 @@ export default {
|
|
|
256
257
|
const defaultValue = { }
|
|
257
258
|
defaultValue[field] = id
|
|
258
259
|
defaultValue[field + '#'] = text
|
|
259
|
-
this
|
|
260
|
+
this.emitCreatePage({
|
|
260
261
|
metaId: 'FM_MODULE',
|
|
261
262
|
flag: this.mode === 'pc' ? addPageFlag : addPageFlagM,
|
|
262
263
|
component: 'entity-add-page',
|
|
@@ -1,132 +1,458 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ai-registry" :style="{ height: registryHeight ? registryHeight + 'px' : null }" v-loading="registryLoading">
|
|
3
|
+
<div class="ai-registry__heading">
|
|
4
|
+
<div><h2>模型注册</h2><p>按接入组管理模型,一次配置连接,按需添加多个模型。</p></div>
|
|
5
|
+
<el-button size="small" icon="el-icon-plus" @click="openGroup()">新建接入组</el-button>
|
|
6
|
+
</div>
|
|
7
|
+
<el-alert v-if="registryError" :title="registryError" type="error" :closable="false" show-icon>
|
|
8
|
+
<el-button type="text" @click="reloadData">重新加载</el-button>
|
|
9
|
+
</el-alert>
|
|
10
|
+
<div v-if="registryReady" class="ai-registry__layout">
|
|
11
|
+
<aside class="ai-registry__groups">
|
|
12
|
+
<div class="ai-registry__between"><strong>接入组</strong><span class="ai-registry__muted">{{ realGroups.length }} 个组</span></div>
|
|
13
|
+
<el-input v-model="groupKeyword" size="small" placeholder="搜索接入组" prefix-icon="el-icon-search" clearable />
|
|
14
|
+
<div class="ai-registry__group-list">
|
|
15
|
+
<button v-for="group in visibleGroups" :key="group.ID || 'ungrouped'" type="button" class="ai-registry__group" :class="{ 'is-active': activeGroupId === group.ID }" @click="selectGroup(group.ID)">
|
|
16
|
+
<el-image v-if="group.LOGO" class="ai-registry__group-logo" :src="$util.getFileUrl(group.LOGO)" fit="contain"><template #error><i class="el-icon-folder-opened" /></template></el-image><i v-else :class="group.ID ? 'el-icon-folder-opened' : 'el-icon-folder'" />
|
|
17
|
+
<span><strong>{{ group.NAME }}</strong><small>{{ group.ID ? codeName(providerOptions, group.TYPE) : '保留原有独立连接' }}</small></span>
|
|
18
|
+
<em>{{ group.MODEL_COUNT }}</em>
|
|
19
|
+
</button>
|
|
20
|
+
<p v-if="!visibleGroups.length" class="ai-registry__muted">没有匹配的接入组</p>
|
|
21
|
+
</div>
|
|
22
|
+
<p class="ai-registry__group-tip"><i class="el-icon-info" /> 一个组对应一套接入配置,同一提供商可创建多个接入组。</p>
|
|
23
|
+
</aside>
|
|
24
|
+
<div class="ai-registry__detail">
|
|
25
|
+
<section class="ai-registry__panel" v-if="activeGroup.ID">
|
|
26
|
+
<div class="ai-registry__between">
|
|
27
|
+
<div class="ai-registry__identity"><span class="ai-registry__icon"><el-image v-if="activeGroup.LOGO" :src="$util.getFileUrl(activeGroup.LOGO)" fit="contain"><template #error><i class="el-icon-connection" /></template></el-image><i v-else class="el-icon-connection" /></span><div><h3>{{ activeGroup.NAME }}</h3><p v-show="connectionExpanded">{{ activeGroup.NOTE || '管理组内模型与共享连接' }}</p></div></div>
|
|
28
|
+
<div><el-button size="small" type="text" :icon="connectionExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" @click="connectionExpanded = !connectionExpanded">{{ connectionExpanded ? '收起' : '展开' }}</el-button><el-button size="small" icon="el-icon-setting" @click="openGroup(activeGroup)">编辑组配置</el-button></div>
|
|
29
|
+
</div>
|
|
30
|
+
<div v-show="connectionExpanded" class="ai-registry__connection">
|
|
31
|
+
<div><small>模型提供商</small><span>{{ codeName(providerOptions, activeGroup.TYPE) }}</span></div>
|
|
32
|
+
<div><small>接口基础地址</small><code>{{ activeGroup.BASE_URL }}</code></div>
|
|
33
|
+
<div><small>接口密钥</small><span>{{ activeGroup.HAS_SECRET ? '•••••••••••• 已配置' : '未配置' }}</span></div>
|
|
34
|
+
</div>
|
|
35
|
+
<div v-show="connectionExpanded" class="ai-registry__notice"><i class="el-icon-link" /> 组内模型共用以上连接配置,修改后对本组全部模型生效。</div>
|
|
36
|
+
</section>
|
|
37
|
+
<el-alert v-else title="未分组模型继续使用各自的原有连接配置。编辑模型并选择接入组后,将改为使用组内共享连接。" type="info" :closable="false" show-icon />
|
|
38
|
+
<section class="ai-registry__panel ai-registry__models">
|
|
39
|
+
<div class="ai-registry__between"><h3>{{ activeGroup.ID ? '组内模型' : '未分组模型' }} <el-tag size="small" type="info">{{ activeGroup.MODEL_COUNT || 0 }} 个</el-tag></h3><el-button type="primary" size="small" icon="el-icon-plus" @click="openModel()">添加模型</el-button></div>
|
|
40
|
+
<div class="ai-registry__filters">
|
|
41
|
+
<el-input v-model="modelKeyword" size="small" placeholder="搜索模型名称或编号" prefix-icon="el-icon-search" clearable @input="scheduleSearch" />
|
|
42
|
+
<el-select v-model="modelCategory" size="small" placeholder="全部分类" clearable @change="getData(1)"><el-option v-for="item in categoryOptions" :key="item.code" :label="item.name" :value="item.code" /></el-select>
|
|
43
|
+
<el-select v-model="modelStatus" size="small" placeholder="全部状态" clearable @change="getData(1)"><el-option label="已启用" value="1" /><el-option label="已停用" value="0" /></el-select>
|
|
44
|
+
</div>
|
|
45
|
+
<el-alert v-if="modelError" :title="modelError" type="error" :closable="false" show-icon />
|
|
46
|
+
<div class="ai-registry__table">
|
|
47
|
+
<el-table v-loading="loading" :data="data" height="100%" empty-text="暂无模型,请添加模型或调整筛选条件">
|
|
48
|
+
<el-table-column label="模型名称 / 编号" min-width="220"><template #default="scope"><div class="ai-registry__name">{{ scope.row.NAME }} <el-tag v-if="scope.row.IS_DEFAULT === '1'" size="small">分类默认</el-tag></div><code class="ai-registry__model-code">{{ scope.row.CODE }}</code></template></el-table-column>
|
|
49
|
+
<el-table-column label="分类" min-width="100"><template #default="scope"><el-tag size="small" type="info" class="ai-registry__category" :class="'ai-registry__category--' + scope.row.MODE_TYPE">{{ codeName(categoryOptions, scope.row.MODE_TYPE) }}</el-tag></template></el-table-column>
|
|
50
|
+
<el-table-column label="能力" min-width="160"><template #default="scope"><el-tag v-if="scope.row.THINK === '1'" size="small" type="info">慢思考</el-tag> <el-tag v-if="scope.row.SEARCH === '1'" size="small" type="info">联网搜索</el-tag> <el-tag v-if="scope.row.MULTIMODAL === '1'" size="small" type="info">多模态</el-tag><span v-if="!hasCapabilities(scope.row)" class="ai-registry__muted">—</span></template></el-table-column>
|
|
51
|
+
<el-table-column label="输入上限 / 向量维度" min-width="170"><template #default="scope">{{ formatLimit(scope.row.MAX_TOKEN) }} <span class="ai-registry__muted">{{ scope.row.MODE_TYPE === 'embedding' ? '维' : 'tokens' }}</span></template></el-table-column>
|
|
52
|
+
<el-table-column label="状态" width="100"><template #default="scope"><el-switch :model-value="scope.row.ENABLE === '1'" :disabled="mutationLoading" @change="toggleModel(scope.row, $event)" /></template></el-table-column>
|
|
53
|
+
<el-table-column label="操作" width="220"><template #default="scope"><el-button size="small" type="text" @click="openModel(scope.row)">编辑</el-button><el-button size="small" type="text" :disabled="scope.row.IS_DEFAULT === '1' || scope.row.ENABLE !== '1' || mutationLoading" @click="setDefault(scope.row)">设为默认</el-button><el-button size="small" type="text" class="ai-registry__delete" :disabled="mutationLoading" @click="deleteModel(scope.row)">删除</el-button></template></el-table-column>
|
|
54
|
+
</el-table>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="ai-registry__footer"><span class="ai-registry__muted">默认模型按分类设置,跨接入组保持唯一。</span><el-pagination layout="total, prev, pager, next" :total="total" :current-page="currentPage" :page-size="20" @current-change="getData" /></div>
|
|
57
|
+
</section>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<el-dialog :title="groupForm.ID ? '编辑组配置' : '新建接入组'" v-model="groupDialog" width="640px" append-to-body :close-on-click-modal="false" :before-close="closeGroup">
|
|
61
|
+
<div class="ai-registry__dialog">
|
|
62
|
+
<el-alert :title="groupForm.ID ? '修改连接配置将影响本组全部模型;密钥留空时保留原配置。' : '先配置接入渠道,再向组内添加模型。以下连接信息由组内模型共用。'" type="info" :closable="false" show-icon />
|
|
63
|
+
<el-form ref="groupForm" :model="groupForm" label-position="top" :rules="groupRules" size="small">
|
|
64
|
+
<el-form-item label="组图标(可选)"><div class="ai-registry__logo-editor"><el-image v-if="groupForm.LOGO" class="ai-registry__logo-preview" :src="$util.getFileUrl(groupForm.LOGO)" fit="contain" /><el-upload action="#" accept=".png,.jpg,.jpeg,.gif" :show-file-list="false" :http-request="uploadGroupLogo" :disabled="saving || logoUploading"><el-button size="small" :loading="logoUploading" :disabled="saving">上传图标</el-button></el-upload><el-button v-if="groupForm.LOGO" type="text" :disabled="saving || logoUploading" @click="groupForm.LOGO = ''">移除</el-button></div></el-form-item>
|
|
65
|
+
<div class="ai-registry__form-row"><el-form-item label="组名称" prop="NAME"><el-input v-model.trim="groupForm.NAME" maxlength="100" /></el-form-item><el-form-item label="模型提供商" prop="TYPE"><el-select v-model="groupForm.TYPE"><el-option v-for="item in providerOptions" :key="item.code" :label="item.name" :value="item.code" /></el-select></el-form-item></div>
|
|
66
|
+
<el-form-item label="接口基础地址" prop="BASE_URL"><el-input v-model.trim="groupForm.BASE_URL" placeholder="https://…/v1" maxlength="1024" /></el-form-item>
|
|
67
|
+
<el-form-item :label="groupForm.ID ? '接口密钥(留空保留原值)' : '接口密钥'" prop="API_SECRET" :rules="groupForm.ID ? [] : [{ required: true, message: '请输入接口密钥', trigger: 'blur' }]"><el-input v-model="groupForm.API_SECRET" type="password" autocomplete="new-password" maxlength="1024" /></el-form-item>
|
|
68
|
+
<el-form-item label="应用 ID(可选)"><el-input v-model.trim="groupForm.APP_ID" /></el-form-item><el-form-item label="分组说明(可选)"><el-input v-model.trim="groupForm.NOTE" maxlength="500" /></el-form-item>
|
|
69
|
+
</el-form>
|
|
70
|
+
</div>
|
|
71
|
+
<template #footer><span ><el-button size="small" :disabled="saving || logoUploading" @click="groupDialog = false">取消</el-button><el-button size="small" type="primary" :loading="saving" :disabled="logoUploading" @click="saveGroup">{{ groupForm.ID ? '保存组配置' : '创建并添加模型' }}</el-button></span></template>
|
|
72
|
+
</el-dialog>
|
|
73
|
+
<el-dialog :title="modelForm.ID ? '编辑模型' : '添加模型'" v-model="modelDialog" width="680px" append-to-body :close-on-click-modal="false" :before-close="closeModel">
|
|
74
|
+
<div class="ai-registry__dialog">
|
|
75
|
+
<el-alert :title="modelForm.GROUP_ID ? '自动使用所选接入组的提供商、接口地址和密钥。' : '此模型尚未分组,继续保留原有连接;选择接入组后将使用组配置。'" type="info" :closable="false" show-icon />
|
|
76
|
+
<el-form ref="modelForm" :model="modelForm" :rules="modelRules" label-position="top" size="small">
|
|
77
|
+
<el-form-item label="所属接入组" prop="GROUP_ID"><el-select v-model="modelForm.GROUP_ID"><el-option v-if="modelForm.ID && !originalModelGroup" label="未分组(保留原配置)" value="" /><el-option v-for="group in realGroups" :key="group.ID" :label="group.NAME" :value="group.ID" /></el-select></el-form-item>
|
|
78
|
+
<div class="ai-registry__form-row"><el-form-item label="模型名称" prop="NAME"><el-input v-model.trim="modelForm.NAME" maxlength="100" /></el-form-item><el-form-item label="模型编号" prop="CODE"><el-input v-model.trim="modelForm.CODE" maxlength="100" /></el-form-item></div>
|
|
79
|
+
<div class="ai-registry__form-row"><el-form-item label="模型分类" prop="MODE_TYPE"><el-select v-model="modelForm.MODE_TYPE"><el-option v-for="item in categoryOptions" :key="item.code" :label="item.name" :value="item.code" /></el-select></el-form-item><el-form-item :label="modelForm.MODE_TYPE === 'embedding' ? '向量维度' : '输入上限(tokens)'" prop="MAX_TOKEN"><el-input-number v-model="modelForm.MAX_TOKEN" :min="1" :max="2147483647" :precision="0" /></el-form-item></div>
|
|
80
|
+
<el-form-item label="模型能力"><el-checkbox v-model="modelForm.THINK" true-label="1" false-label="0">慢思考</el-checkbox><el-checkbox v-model="modelForm.SEARCH" true-label="1" false-label="0">联网搜索</el-checkbox><el-checkbox v-model="modelForm.MULTIMODAL" true-label="1" false-label="0">多模态</el-checkbox></el-form-item>
|
|
81
|
+
<el-form-item><el-checkbox v-model="modelForm.ENABLE" true-label="1" false-label="0">启用模型</el-checkbox><el-checkbox v-model="modelForm.IS_DEFAULT" true-label="1" false-label="0">设为该分类默认模型</el-checkbox></el-form-item>
|
|
82
|
+
<div v-if="modelForm.ID && !modelForm.GROUP_ID" class="ai-registry__legacy"><p class="ai-registry__muted">原有独立连接配置</p><el-form-item label="模型提供商"><el-select v-model="modelForm.TYPE"><el-option v-for="item in providerOptions" :key="item.code" :label="item.name" :value="item.code" /></el-select></el-form-item><el-form-item label="接口基础地址"><el-input v-model.trim="modelForm.BASE_URL" /></el-form-item><el-form-item label="接口密钥(留空保留原值)"><el-input v-model="modelForm.API_SECRET" type="password" autocomplete="new-password" /></el-form-item><el-form-item label="应用 ID"><el-input v-model.trim="modelForm.APP_ID" /></el-form-item></div>
|
|
83
|
+
<p class="ai-registry__muted">模型编号需与接入渠道提供的调用标识一致。</p>
|
|
84
|
+
</el-form>
|
|
85
|
+
</div>
|
|
86
|
+
<template #footer><span ><el-button size="small" :disabled="saving" @click="modelDialog = false">取消</el-button><el-button size="small" type="primary" :loading="saving" @click="saveModel">{{ modelForm.ID ? '保存模型' : '添加到本组' }}</el-button></span></template>
|
|
87
|
+
</el-dialog>
|
|
88
|
+
</div>
|
|
89
|
+
</template>
|
|
3
90
|
|
|
91
|
+
<script>
|
|
4
92
|
import EntityList from '@epxing/components/src/entity-list'
|
|
93
|
+
|
|
94
|
+
const modelFields = ['ID', 'NAME', 'CODE', 'GROUP_ID', 'MODE_TYPE', 'MAX_TOKEN', 'ENABLE', 'IS_DEFAULT', 'THINK', 'SEARCH', 'MULTIMODAL']
|
|
95
|
+
|
|
5
96
|
export default {
|
|
6
97
|
name: 'epx-ai-list',
|
|
98
|
+
// 保留平台列表的页面上下文、权限入口和刷新事件,重写注册页布局与取数。
|
|
7
99
|
extends: EntityList,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
100
|
+
data () {
|
|
101
|
+
return {
|
|
102
|
+
data: [],
|
|
103
|
+
loading: false,
|
|
104
|
+
registryHeight: 0,
|
|
105
|
+
connectionExpanded: false,
|
|
106
|
+
currentPage: 1,
|
|
107
|
+
total: 0,
|
|
108
|
+
groups: [],
|
|
109
|
+
activeGroupId: null,
|
|
110
|
+
groupKeyword: '',
|
|
111
|
+
modelKeyword: '',
|
|
112
|
+
modelCategory: '',
|
|
113
|
+
modelStatus: '',
|
|
114
|
+
registryReady: false,
|
|
115
|
+
registryLoading: false,
|
|
116
|
+
registryError: '',
|
|
117
|
+
modelError: '',
|
|
118
|
+
providerOptions: [],
|
|
119
|
+
categoryOptions: [],
|
|
120
|
+
groupDialog: false,
|
|
121
|
+
modelDialog: false,
|
|
122
|
+
groupForm: {},
|
|
123
|
+
modelForm: {},
|
|
124
|
+
originalModelGroup: '',
|
|
125
|
+
saving: false,
|
|
126
|
+
logoUploading: false,
|
|
127
|
+
mutationLoading: false,
|
|
128
|
+
// 序号隔离快速切组、搜索产生的旧响应。
|
|
129
|
+
requestSerial: 0,
|
|
130
|
+
searchTimer: null,
|
|
131
|
+
groupRules: {
|
|
132
|
+
NAME: [{ required: true, message: '请输入组名称', trigger: 'blur' }],
|
|
133
|
+
TYPE: [{ required: true, message: '请选择模型提供商', trigger: 'change' }],
|
|
134
|
+
BASE_URL: [{ required: true, message: '请输入接口基础地址', trigger: 'blur' }]
|
|
135
|
+
},
|
|
136
|
+
modelRules: {
|
|
137
|
+
NAME: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
|
|
138
|
+
CODE: [{ required: true, message: '请输入模型编号', trigger: 'blur' }],
|
|
139
|
+
MODE_TYPE: [{ required: true, message: '请选择模型分类', trigger: 'change' }],
|
|
140
|
+
MAX_TOKEN: [{ required: true, type: 'number', min: 1, message: '请输入正整数', trigger: 'change' }]
|
|
16
141
|
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
computed: {
|
|
145
|
+
realGroups () {
|
|
146
|
+
return this.groups.filter(group => group.ID)
|
|
17
147
|
},
|
|
18
|
-
|
|
19
|
-
this
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
.then(() => {
|
|
25
|
-
this.$message.success('修改成功')
|
|
26
|
-
this.reloadData()
|
|
27
|
-
})
|
|
28
|
-
.catch(err => {
|
|
29
|
-
this.$message.error(err.message)
|
|
30
|
-
})
|
|
148
|
+
visibleGroups () {
|
|
149
|
+
const keyword = this.groupKeyword.trim().toLowerCase()
|
|
150
|
+
return this.groups.filter(group => group.NAME.toLowerCase().includes(keyword))
|
|
151
|
+
},
|
|
152
|
+
activeGroup () {
|
|
153
|
+
return this.groups.find(group => group.ID === this.activeGroupId) || {}
|
|
31
154
|
}
|
|
32
155
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
156
|
+
mounted () {
|
|
157
|
+
this.registryPage = this.$el.closest('.epx-base-page')
|
|
158
|
+
if (this.registryPage) {
|
|
159
|
+
this.registryResizeObserver = new ResizeObserver(this.updateRegistryHeight)
|
|
160
|
+
this.registryResizeObserver.observe(this.registryPage)
|
|
161
|
+
this.updateRegistryHeight()
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
beforeUnmount () {
|
|
165
|
+
if (this.registryResizeObserver) this.registryResizeObserver.disconnect()
|
|
166
|
+
clearTimeout(this.searchTimer)
|
|
167
|
+
this.requestSerial++
|
|
168
|
+
},
|
|
169
|
+
methods: {
|
|
170
|
+
updateRegistryHeight () {
|
|
171
|
+
const page = this.registryPage
|
|
172
|
+
if (!page || !page.clientHeight) return
|
|
173
|
+
const offset = this.$el.getBoundingClientRect().top - page.getBoundingClientRect().top + page.scrollTop
|
|
174
|
+
const margin = parseFloat(getComputedStyle(this.$el).marginBottom) || 0
|
|
175
|
+
this.registryHeight = Math.max(0, Math.floor(page.clientHeight - offset - margin))
|
|
176
|
+
},
|
|
177
|
+
initComponent () {
|
|
178
|
+
this.reloadData()
|
|
179
|
+
},
|
|
180
|
+
async reloadData () {
|
|
181
|
+
if (this.registryLoading) return
|
|
182
|
+
this.registryLoading = true
|
|
183
|
+
this.registryError = ''
|
|
184
|
+
try {
|
|
185
|
+
// 摘要只含安全连接信息与服务端统计;代码项直接复用平台码表。
|
|
186
|
+
const [groups, providers, categories] = await Promise.all([
|
|
187
|
+
this.$http.$post({ entityId: this.$metaId, action: 'registryGroups' }),
|
|
188
|
+
this.$http.getCodes({ key: 'AI_TYPE' }),
|
|
189
|
+
this.$http.getCodes({ key: 'MODE_FUN' })
|
|
190
|
+
])
|
|
191
|
+
this.groups = groups
|
|
192
|
+
this.providerOptions = providers
|
|
193
|
+
this.categoryOptions = categories
|
|
194
|
+
if (!groups.some(group => group.ID === this.activeGroupId)) this.activeGroupId = groups.length ? groups[0].ID : ''
|
|
195
|
+
this.registryReady = true
|
|
196
|
+
await this.getData(this.currentPage)
|
|
197
|
+
} catch (err) {
|
|
198
|
+
this.registryError = (err && (err.msg || err.message)) || '加载模型接入组失败'
|
|
199
|
+
this.$message.error(this.registryError)
|
|
200
|
+
} finally {
|
|
201
|
+
this.registryLoading = false
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
async getData (page = 1, callback) {
|
|
205
|
+
if (!this.registryReady) return
|
|
206
|
+
const serial = ++this.requestSerial
|
|
207
|
+
this.loading = true
|
|
208
|
+
this.modelError = ''
|
|
209
|
+
this.currentPage = Number(page) || 1
|
|
210
|
+
try {
|
|
211
|
+
// 标准列表接口负责分页与过滤,显式投影避免列表传输密钥。
|
|
212
|
+
const params = {
|
|
213
|
+
entityId: this.$metaId,
|
|
214
|
+
page: this.currentPage,
|
|
215
|
+
limit: 20,
|
|
216
|
+
columnFields: modelFields.map(attriName => ({ attriName })),
|
|
217
|
+
order_field: 'NAME',
|
|
218
|
+
order_mode: 'asc',
|
|
219
|
+
'scope.MODE_TYPE': this.modelCategory,
|
|
220
|
+
'scope.ENABLE': this.modelStatus,
|
|
221
|
+
...(this.activeGroupId ? { 'scope.GROUP_ID': this.activeGroupId } : { 'scope.GROUP_ID#is_null': '1' })
|
|
222
|
+
}
|
|
223
|
+
const filters = (this.scopeParams || []).map(item => ({ ...item }))
|
|
224
|
+
if (this.modelKeyword.trim()) {
|
|
225
|
+
if (filters.length) filters[filters.length - 1].link = 'and'
|
|
226
|
+
filters.push(
|
|
227
|
+
{ lb: '(', field: 'NAME', mode: 'like', values: this.modelKeyword.trim(), link: 'or' },
|
|
228
|
+
{ field: 'CODE', mode: 'like', values: this.modelKeyword.trim(), rb: ')' }
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
if (filters.length) params.scopeParams = JSON.stringify(filters)
|
|
232
|
+
const result = await this.$http.list(params)
|
|
233
|
+
if (serial !== this.requestSerial) return
|
|
234
|
+
this.data = result.entitys || []
|
|
235
|
+
this.total = result.total || 0
|
|
236
|
+
if (callback) callback(this.data)
|
|
237
|
+
} catch (err) {
|
|
238
|
+
if (serial !== this.requestSerial) return
|
|
239
|
+
this.data = []
|
|
240
|
+
this.total = 0
|
|
241
|
+
this.modelError = (err && (err.msg || err.message)) || '加载模型失败'
|
|
242
|
+
this.$message.error(this.modelError)
|
|
243
|
+
} finally {
|
|
244
|
+
if (serial === this.requestSerial) this.loading = false
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
selectGroup (id) {
|
|
248
|
+
this.activeGroupId = id
|
|
249
|
+
this.modelKeyword = ''
|
|
250
|
+
this.modelCategory = ''
|
|
251
|
+
this.modelStatus = ''
|
|
252
|
+
clearTimeout(this.searchTimer)
|
|
253
|
+
this.getData(1)
|
|
254
|
+
},
|
|
255
|
+
scheduleSearch () {
|
|
256
|
+
clearTimeout(this.searchTimer)
|
|
257
|
+
this.searchTimer = setTimeout(() => this.getData(1), 250)
|
|
258
|
+
},
|
|
259
|
+
codeName (options, code) {
|
|
260
|
+
const item = options.find(item => item.code === code)
|
|
261
|
+
return item ? item.name : code
|
|
262
|
+
},
|
|
263
|
+
formatLimit (value) {
|
|
264
|
+
return Number(value || 0).toLocaleString()
|
|
265
|
+
},
|
|
266
|
+
hasCapabilities (model) {
|
|
267
|
+
return ['THINK', 'SEARCH', 'MULTIMODAL'].some(key => model[key] === '1')
|
|
268
|
+
},
|
|
269
|
+
openGroup (group) {
|
|
270
|
+
this.groupForm = { NAME: '', TYPE: '', BASE_URL: '', APP_ID: '', NOTE: '', LOGO: '', ...group, API_SECRET: '' }
|
|
271
|
+
this.groupDialog = true
|
|
272
|
+
this.$nextTick(() => this.$refs.groupForm.clearValidate())
|
|
273
|
+
},
|
|
274
|
+
async uploadGroupLogo ({ file }) {
|
|
275
|
+
this.logoUploading = true
|
|
276
|
+
try {
|
|
277
|
+
const meta = await this.$http.getMeta({ metaId: 'FM_AI_MODEL_GROUP' })
|
|
278
|
+
const attribute = meta.attributes.LOGO
|
|
279
|
+
if (!attribute) throw new Error('接入组图标字段尚未配置')
|
|
280
|
+
const result = await this.$http.$post({ action: 'uploadOfEntity', metaId: 'FM_AI_MODEL_GROUP', attributeId: attribute.id, file }, { requestType: 'formData' })
|
|
281
|
+
const id = result.bean || result.id
|
|
282
|
+
if (!id) throw new Error((result.fileCheck && result.fileCheck.checkResult) || '上传图标失败')
|
|
283
|
+
this.groupForm.LOGO = id
|
|
284
|
+
} catch (err) {
|
|
285
|
+
this.$message.error((err && (err.msg || err.message)) || '上传图标失败')
|
|
286
|
+
} finally {
|
|
287
|
+
this.logoUploading = false
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
async saveGroup () {
|
|
291
|
+
if (this.saving || this.logoUploading) return
|
|
292
|
+
const valid = await this.$refs.groupForm.validate().catch(() => false)
|
|
293
|
+
if (!valid) return
|
|
294
|
+
this.saving = true
|
|
295
|
+
const create = !this.groupForm.ID
|
|
296
|
+
try {
|
|
297
|
+
const id = await this.$http.$post({ entityId: this.$metaId, action: 'saveModelGroup', bean: { ...this.groupForm } })
|
|
298
|
+
this.groupForm.API_SECRET = ''
|
|
299
|
+
this.groupDialog = false
|
|
300
|
+
this.activeGroupId = id
|
|
301
|
+
this.currentPage = 1
|
|
302
|
+
await this.reloadData()
|
|
303
|
+
this.$message.success('接入组已保存')
|
|
304
|
+
if (create && !this.registryError) this.openModel()
|
|
305
|
+
} catch (err) {
|
|
306
|
+
this.$message.error((err && (err.msg || err.message)) || '保存接入组失败')
|
|
307
|
+
} finally {
|
|
308
|
+
this.saving = false
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
async openModel (model) {
|
|
312
|
+
if (!model && !this.activeGroupId) {
|
|
313
|
+
if (!this.realGroups.length) return this.openGroup()
|
|
314
|
+
this.selectGroup(this.realGroups[0].ID)
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
// 标准详情读取保留旧模型的独立连接,密钥始终留空,不将脱敏值回写。
|
|
318
|
+
const detail = model ? await this.$http.get({ entityId: this.$metaId, id: model.ID }) : null
|
|
319
|
+
const value = detail ? detail.data : {}
|
|
320
|
+
this.modelForm = {
|
|
321
|
+
NAME: '',
|
|
322
|
+
CODE: '',
|
|
323
|
+
GROUP_ID: this.activeGroupId,
|
|
324
|
+
MODE_TYPE: 'text',
|
|
325
|
+
MAX_TOKEN: 32768,
|
|
326
|
+
ENABLE: '1',
|
|
327
|
+
IS_DEFAULT: '0',
|
|
328
|
+
THINK: '0',
|
|
329
|
+
SEARCH: '0',
|
|
330
|
+
MULTIMODAL: '0',
|
|
331
|
+
...value,
|
|
332
|
+
API_SECRET: ''
|
|
333
|
+
}
|
|
334
|
+
this.modelForm.GROUP_ID = this.modelForm.GROUP_ID || ''
|
|
335
|
+
this.modelForm.MAX_TOKEN = Number(this.modelForm.MAX_TOKEN)
|
|
336
|
+
this.originalModelGroup = this.modelForm.GROUP_ID
|
|
337
|
+
this.modelDialog = true
|
|
338
|
+
this.$nextTick(() => this.$refs.modelForm.clearValidate())
|
|
339
|
+
} catch (err) {
|
|
340
|
+
this.$message.error((err && (err.msg || err.message)) || '读取模型配置失败')
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
async saveModel () {
|
|
344
|
+
if (this.saving) return
|
|
345
|
+
const valid = await this.$refs.modelForm.validate().catch(() => false)
|
|
346
|
+
if (!valid) return
|
|
347
|
+
this.saving = true
|
|
348
|
+
try {
|
|
349
|
+
await this.$http.$post({ entityId: this.$metaId, action: 'saveRegistryModel', bean: { ...this.modelForm } })
|
|
350
|
+
this.modelForm.API_SECRET = ''
|
|
351
|
+
this.modelDialog = false
|
|
352
|
+
this.activeGroupId = this.modelForm.GROUP_ID || ''
|
|
353
|
+
await this.reloadData()
|
|
354
|
+
this.$message.success('模型已保存')
|
|
355
|
+
} catch (err) {
|
|
356
|
+
this.$message.error((err && (err.msg || err.message)) || '保存模型失败')
|
|
357
|
+
} finally {
|
|
358
|
+
this.saving = false
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
async toggleModel (model, enabled) {
|
|
362
|
+
this.mutationLoading = true
|
|
363
|
+
try {
|
|
364
|
+
await this.$http.$post({ entityId: this.$metaId, action: 'setModelEnabled', id: model.ID, enabled: enabled ? '1' : '0' })
|
|
365
|
+
await this.reloadData()
|
|
366
|
+
} catch (err) {
|
|
367
|
+
this.$message.error((err && (err.msg || err.message)) || '修改模型状态失败')
|
|
368
|
+
} finally {
|
|
369
|
+
this.mutationLoading = false
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
async setDefault (model) {
|
|
373
|
+
this.mutationLoading = true
|
|
374
|
+
try {
|
|
375
|
+
await this.$http.$post({ entityId: this.$metaId, action: 'setDefault', id: model.ID })
|
|
376
|
+
await this.reloadData()
|
|
377
|
+
this.$message.success('已设置分类默认模型')
|
|
378
|
+
} catch (err) {
|
|
379
|
+
this.$message.error((err && (err.msg || err.message)) || '设置默认模型失败')
|
|
380
|
+
} finally {
|
|
381
|
+
this.mutationLoading = false
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
async deleteModel (model) {
|
|
385
|
+
try {
|
|
386
|
+
await this.$confirm('确认删除模型“' + model.NAME + '”?删除后无法恢复。', '删除模型', { type: 'warning', confirmButtonText: '删除', cancelButtonText: '取消' })
|
|
387
|
+
this.mutationLoading = true
|
|
388
|
+
await this.$http.removeMulti({ entityId: this.$metaId, ids: model.ID })
|
|
389
|
+
await this.reloadData()
|
|
390
|
+
this.$message.success('模型已删除')
|
|
391
|
+
} catch (err) {
|
|
392
|
+
if (err !== 'cancel' && err !== 'close') this.$message.error((err && (err.msg || err.message)) || '删除模型失败')
|
|
393
|
+
} finally {
|
|
394
|
+
this.mutationLoading = false
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
closeGroup (done) {
|
|
398
|
+
if (!this.saving && !this.logoUploading) { this.groupForm.API_SECRET = ''; done() }
|
|
399
|
+
},
|
|
400
|
+
closeModel (done) {
|
|
401
|
+
if (!this.saving) { this.modelForm.API_SECRET = ''; done() }
|
|
402
|
+
}
|
|
69
403
|
}
|
|
70
404
|
}
|
|
71
405
|
</script>
|
|
72
406
|
|
|
73
407
|
<style lang="scss" scoped>
|
|
74
|
-
.ai-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.ai-item {
|
|
79
|
-
margin: 10px;
|
|
80
|
-
width: calc(16.6% - 20px);
|
|
81
|
-
background: var(--epx-bg-card);
|
|
82
|
-
border-radius: var(--epx-radius-base);
|
|
83
|
-
overflow: hidden;
|
|
84
|
-
}
|
|
85
|
-
.ai-item__content {
|
|
86
|
-
width: 100%;
|
|
87
|
-
height: 132px;
|
|
88
|
-
background: url(@/assets/images/ai_bg.webp) no-repeat center;
|
|
89
|
-
background-size:cover ;
|
|
90
|
-
display: flex;
|
|
91
|
-
justify-content: center;
|
|
92
|
-
align-items: center;
|
|
93
|
-
>img {
|
|
94
|
-
height: 80px;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
.ai-item__title {
|
|
98
|
-
padding: 0 20px;
|
|
99
|
-
height: 48px;
|
|
100
|
-
display: flex;
|
|
101
|
-
align-items: center;
|
|
102
|
-
justify-content: space-between;
|
|
103
|
-
}
|
|
104
|
-
.ai-item__operate {
|
|
105
|
-
cursor: pointer;
|
|
106
|
-
}
|
|
107
|
-
.ai-item--add {
|
|
108
|
-
color: #444;
|
|
109
|
-
line-height: 20px;
|
|
408
|
+
.ai-registry {
|
|
409
|
+
box-sizing: border-box;
|
|
410
|
+
height: 100%;
|
|
411
|
+
min-height: 0;
|
|
110
412
|
display: flex;
|
|
111
413
|
flex-direction: column;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
414
|
+
overflow: hidden;
|
|
415
|
+
padding: 12px 16px;
|
|
416
|
+
color: var(--color-text-primary, #444);
|
|
417
|
+
h2, h3, p { margin: 0; }
|
|
418
|
+
h2 { font-size: 18px; font-weight: 600; white-space: nowrap; }
|
|
419
|
+
h3 { font-size: 16px; font-weight: 600; }
|
|
420
|
+
&__heading, &__between { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
|
421
|
+
&__heading { flex-shrink: 0; margin-bottom: 12px; > div { display: flex; align-items: baseline; gap: 12px; } p { font-size: 12px; color: var(--color-text-secondary, #888); } }
|
|
422
|
+
&__layout { flex: 1; min-height: 0; display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 12px; overflow: hidden; }
|
|
423
|
+
&__groups, &__panel { border-radius: 4px; background: var(--background-color, #fff); padding: 16px; }
|
|
424
|
+
&__groups { padding: 16px; display: flex; flex-direction: column; gap: 16px; min-height: 0; overflow: hidden; > :not(.ai-registry__group-list) { flex-shrink: 0; } }
|
|
425
|
+
&__group-list { flex: 1; min-height: 0; overflow-y: auto; }
|
|
426
|
+
&__group-logo { width: 32px; height: 32px; flex-shrink: 0; }
|
|
427
|
+
&__logo-editor { display: flex; align-items: center; gap: 12px; }
|
|
428
|
+
&__logo-preview { width: 48px; height: 48px; }
|
|
429
|
+
&__group { display: flex; align-items: center; gap: 12px; padding: 16px 12px; margin-bottom: 8px; width: 100%; border: 1px solid transparent; border-radius: 4px; background: transparent; text-align: left; cursor: pointer; font: inherit; color: inherit; strong { display: block; font-weight: 500; } small { display: block; margin-top: 4px; color: var(--color-text-secondary, #888); } em { margin-left: auto; font-style: normal; } &:hover { background: var(--background-color-base, #f5f7fa); } &.is-active { background: var(--epx-bg-card, #edf5ff); border-color: transparent; color: var(--el-color-primary, #1765c1); box-shadow: inset 2px 0 #0874e7; } }
|
|
430
|
+
&__group-tip { font-size: 12px; line-height: 1.8; color: var(--color-text-secondary, #888); border-top: 1px solid var(--border-color-base, #e4e7ed); padding-top: 16px; }
|
|
431
|
+
&__detail { display: flex; flex-direction: column; gap: 12px; min-width: 0; min-height: 0; overflow: hidden; > :not(.ai-registry__models) { flex-shrink: 0; } }
|
|
432
|
+
&__models { flex: 1; min-height: 0; display: flex; flex-direction: column; > :not(.ai-registry__table) { flex-shrink: 0; } }
|
|
433
|
+
&__table { flex: 1; min-height: 0; overflow: hidden; &:deep() { .el-table td, .el-table th { padding: 4px 0; } .el-table .cell { line-height: 18px; } } }
|
|
434
|
+
&__identity { display: flex; gap: 12px; align-items: center; p { margin-top: 8px; color: var(--color-text-secondary, #888); } }
|
|
435
|
+
&__icon { display: grid; place-items: center; height: 36px; width: 36px; flex-shrink: 0; background: var(--background-color-base, #f2f3f7); font-size: 22px; border-radius: 4px; .el-image { width: 32px; height: 32px; } }
|
|
436
|
+
&__connection { display: grid; grid-template-columns: 1fr 2fr 1fr; gap: 24px; margin-top: 20px; padding: 20px 0; border-top: 1px solid var(--border-color-base, #e4e7ed); small { display: block; margin-bottom: 8px; color: var(--color-text-secondary, #888); } code { font-size: 13px; overflow-wrap: anywhere; } }
|
|
437
|
+
&__notice { background: var(--background-color-base, #f5f7fa); padding: 12px; color: var(--color-text-secondary, #888); font-size: 13px; }
|
|
438
|
+
&__filters { display: flex; gap: 12px; margin: 12px 0; > .el-input { max-width: 280px; } > .el-select { max-width: 150px; } }
|
|
439
|
+
&__name { font-weight: 600; line-height: 20px; }
|
|
440
|
+
&__model-code { display: block; line-height: 14px; font-size: 11px; color: var(--color-text-secondary, #888); }
|
|
441
|
+
&__category {
|
|
442
|
+
font-weight: 600;
|
|
443
|
+
&--text { color: var(--el-color-primary, #1765c1); background: var(--epx-bg, #eaf3ff); border-color: var(--epx-border, #cbdfff); }
|
|
444
|
+
&--multimodal { color: var(--epx-text, #7543bd); background: var(--epx-bg, #f3ecff); border-color: var(--epx-border, #e1d0fa); }
|
|
445
|
+
&--embedding { color: var(--epx-text, #21844a); background: var(--epx-bg-card, #eaf8ef); border-color: var(--epx-border, #c6e9d2); }
|
|
446
|
+
&--rerank { color: var(--epx-text-secondary, #ad6816); background: var(--epx-bg-card, #fff4e3); border-color: var(--epx-border, #f5d8a8); }
|
|
447
|
+
&--speech2text { color: var(--epx-text, #087e8b); background: var(--epx-bg, #e5f7fa); border-color: var(--epx-border, #b9e5ec); }
|
|
448
|
+
&--tts { color: var(--epx-text-secondary, #b63d78); background: var(--epx-bg-card, #ffedf5); border-color: var(--epx-border, #f5c9dd); }
|
|
126
449
|
}
|
|
450
|
+
&__delete { color: var(--epx-text-secondary, #d64646); &:hover, &:focus { color: var(--epx-text, #b63131); } }
|
|
451
|
+
&__muted { color: var(--color-text-secondary, #888); font-size: 13px; }
|
|
452
|
+
&__footer { display: flex; gap: 16px; justify-content: space-between; align-items: center; padding-top: 8px; }
|
|
453
|
+
&__dialog { .el-form { margin-top: 20px; } .el-select, .el-input-number { width: 100%; } }
|
|
454
|
+
&__form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
455
|
+
&__legacy { border-top: 1px solid var(--border-color-base, #e4e7ed); padding-top: 16px; }
|
|
127
456
|
}
|
|
128
|
-
.ai-
|
|
129
|
-
margin-left: 4px;
|
|
130
|
-
}
|
|
131
|
-
|
|
457
|
+
@media (max-width: 1200px) { .ai-registry { &__layout { grid-template-columns: 210px minmax(0, 1fr); } &__panel { padding: 16px; } &__connection { grid-template-columns: 1fr; gap: 12px; } &__footer { align-items: flex-start; flex-direction: column; } } }
|
|
132
458
|
</style>
|