index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. <template>
  2. <div class="model-detail-root">
  3. <!-- 面包屑导航 -->
  4. <el-breadcrumb separator="/" class="model-breadcrumb">
  5. <el-breadcrumb-item>市场模型</el-breadcrumb-item>
  6. <el-breadcrumb-item>模型详情</el-breadcrumb-item>
  7. </el-breadcrumb>
  8. <div class="model-top-right-btns">
  9. <el-button type="plain" size="small" @click="returnModel" class="custom-button">
  10. <el-icon ><ArrowLeft /></el-icon>
  11. 返回列表
  12. </el-button>
  13. <el-button type="plain" size="small" class="custom-button">
  14. <el-icon><Refresh /></el-icon>
  15. 刷新
  16. </el-button>
  17. <el-button type="primary" size="small" class="custom-button">
  18. <el-icon><Edit /></el-icon>
  19. 编辑
  20. </el-button>
  21. <el-button type="danger" size="small" class="custom-button">
  22. <el-icon><SwitchButton /></el-icon>
  23. 禁用
  24. </el-button>
  25. </div>
  26. <!-- 顶部信息区(单卡片,左右分布) -->
  27. <el-card class="model-top-card" shadow="never">
  28. <div class="model-top-flex">
  29. <!-- 左侧基本信息 -->
  30. <div class="model-top-left">
  31. <div class="model-top-title">基本信息</div>
  32. <div class="model-top-row">
  33. <span class="model-top-label">模型名称:</span>
  34. <span class="model-top-value">GPT-4{{model?.modelName}}</span>
  35. </div>
  36. <div class="model-top-row">
  37. <span class="model-top-label">模型类型:</span>
  38. <span class="model-top-value">大语言模型(LLM){{model?.modelType}}</span>
  39. </div>
  40. <div class="model-top-row">
  41. <span class="model-top-label">模型版本:</span>
  42. <span class="model-top-value">gpt-4-1106-preview{{model?.modelVersion}}</span>
  43. </div>
  44. <div class="model-top-row">
  45. <span class="model-top-label">状态:</span>
  46. <el-tag type="success" size="small" v-if="model?.status===1">启用</el-tag>
  47. <el-tag type="warning" size="small" v-else-if="model?.status === 0">测试中</el-tag>
  48. </div>
  49. <div class="model-top-row">
  50. <span class="model-top-label">API地址:</span>
  51. <a class="model-top-link" href="https://api.openai.com/v1/chat/completions" target="_blank">https://api.openai.com/v1/chat/completions{{model?.baseUrl}}</a>
  52. </div>
  53. <div class="model-top-row">
  54. <span class="model-top-label">描述:</span>
  55. <span class="model-top-value">{{model?.description}}GPT-4是OpenAI最先进的大型语言模型,在各种专业和学术基准上表现最佳。相比GPT-3.5,GPT-4在创造性写作、编码和解决复杂问题方面表现出显著提升,能更好地遵循用户意图,理解和生成更长的内容。</span>
  56. </div>
  57. </div>
  58. <!-- 右侧服务信息和按钮 -->
  59. <div class="model-top-right">
  60. <div class="model-top-right-info">
  61. <div style="display: flex"><span class="model-top-label">服务商:</span>&nbsp&nbspOpenAI</div>
  62. <div style="display: flex"><span class="model-top-label">优先级:</span>&nbsp&nbsp90{{model?.priority}}</div>
  63. <div style="display: flex"><span class="model-top-label">创建时间:</span>&nbsp&nbsp2025-05-15 10:30:15{{model?.createTime}}</div>
  64. <div style="display: flex"><span class="model-top-label">更新时间:</span>&nbsp&nbsp2025-06-20 14:45:30{{model?.updateTime}}</div>
  65. </div>
  66. </div>
  67. </div>
  68. </el-card>
  69. <!-- 参数配置 -->
  70. <el-card class="model-section-card">
  71. <div class="model-section-title">
  72. <span>参数配置</span>
  73. <el-button type="primary" size="small" class="model-btn-add">+ 添加参数</el-button>
  74. </div>
  75. <el-table :data="tableData" class="model-param-table" border header-cell-class-name="model-th" cell-class-name="model-td">
  76. <el-table-column prop="name" label="参数名称" align="center" min-width="120" />
  77. <el-table-column prop="type" label="参数类型" align="center" min-width="120" />
  78. <el-table-column prop="defaultValue" label="默认值" align="center" min-width="100" />
  79. <el-table-column prop="isRequired" label="是否必需" align="center" min-width="100" />
  80. <el-table-column prop="description" label="描述" min-width="200" />
  81. <el-table-column label="操作" align="center" min-width="100">
  82. <template #default="scope">
  83. <el-button @click="handleEdit(scope.$index, scope.row)" type="text" size="small">编辑</el-button>
  84. <el-button @click="handleDelete(scope.$index, scope.row)" type="text" size="small">删除</el-button>
  85. </template>
  86. </el-table-column>
  87. </el-table>
  88. </el-card>
  89. <!-- 抽屉 -->
  90. <el-drawer
  91. v-model="drawerVisible"
  92. direction="rtl"
  93. size="40%"
  94. class="drawer-style"
  95. >
  96. <template #header="{close,titleId,titleClass}">
  97. <h2 :id="titleId" style="color:#000000">编辑参数</h2>
  98. </template>
  99. <div >
  100. <h4>模型</h4>
  101. <el-card class="model-card" style=" background:#F5F5F5;">
  102. GPT-4 (OpenAI)<br/>
  103. <span style="color: #777777">版本: gpt-4-1106-preview</span>
  104. </el-card>
  105. <h4>快速添加参数模板</h4>
  106. <el-row :gutter="20">
  107. <el-col :span="12">
  108. <el-card class="center-content" style="height: 150px;background: #FAFAFA" >
  109. <div>OpenAI Chat模板</div>
  110. <div style="color: #777777">6个参数</div>
  111. </el-card>
  112. </el-col>
  113. <el-col :span="12">
  114. <el-card class="center-content"style="background: #FAFAFA">
  115. <div>OpenAI Embedding模板</div>
  116. <div style="color: #777777">三个参数</div>
  117. </el-card>
  118. </el-col>
  119. </el-row>
  120. <h4>参数列表</h4>
  121. <div v-for="(item, index) in tableData" :key="index" class="parameter-card">
  122. <el-card shadow="never" style="background-color: #FAFAFA" >
  123. <div class="card-content">
  124. <div class="param-name">{{ item.name }} <span style="color: #777777">{{item.type}}</span></div>
  125. <div class="actions" style="display: flex">
  126. <el-button @click="handleEdit(index, item)" type="text" size="small" ></el-button>
  127. <el-button @click="handleDelete(index, item)" type="text" size="small"style="margin-left:auto" ><el-icon color="red"><DeleteFilled /></el-icon></el-button>
  128. </div>
  129. </div>
  130. </el-card>
  131. </div>
  132. <el-card style="display: flex;justify-content: center; align-items: center; flex-direction: column;" :body-style="{padding:'1px'}">
  133. <el-button @click="addParameter" type="plain" >添加参数</el-button>
  134. </el-card>
  135. <div style="text-align: right; margin-top: 20px;">
  136. <el-button @click="drawerVisible = false">取消</el-button>
  137. <el-button @click="saveEdit" type="primary">保存</el-button>
  138. </div>
  139. </div>
  140. </el-drawer>
  141. <!-- 调用统计 -->
  142. <el-row :gutter="20" class="model-stat-row">
  143. <el-col :span="6" v-for="(item, idx) in statList" :key="item.label">
  144. <el-card class="model-stat-card" shadow="never">
  145. <div class="model-stat-label">{{ item.label }}</div>
  146. <div class="model-stat-value">{{ item.value }}</div>
  147. <div :class="['model-stat-trend', item.trend > 0 ? 'up' : 'down']">
  148. <span v-if="item.trend > 0">↑ {{ item.trend }}% 较上月</span>
  149. <span v-else>↓ {{ Math.abs(item.trend) }}% 较上月</span>
  150. </div>
  151. </el-card>
  152. </el-col>
  153. <div class="model-stat-select">
  154. <el-select v-model="statRange" size="small" style="width: 80px">
  155. <el-option label="本月" value="month" />
  156. <el-option label="本年" value="year" />
  157. </el-select>
  158. </div>
  159. </el-row>
  160. <!-- 调用趋势 -->
  161. <el-card class="model-section-card model-chart-section">
  162. <div class="model-chart-title">调用趋势</div>
  163. <div id="callTrendChart" style="width: 100%; height: 320px;"></div>
  164. <div class="model-chart-footer">调用趋势曲线数据</div>
  165. </el-card>
  166. </div>
  167. </template>
  168. <script setup lang="ts">
  169. import { ref, onMounted } from 'vue'
  170. import * as echarts from 'echarts'
  171. import { ArrowLeft, Refresh } from '@element-plus/icons-vue';
  172. import { useRoute,useRouter } from 'vue-router';
  173. import { ModelConfigVo} from '@/api/modelConfig/types'
  174. import {toggleStatus} from '@/api/modelConfig/index.js'
  175. const route = useRoute()
  176. const router = useRouter()
  177. const model = ref<ModelConfigVo>(route.query.model)
  178. // 表格数据
  179. const tableData = ref([
  180. { name: 'temperature', type: 'NUMBER', defaultValue: '0.7', isRequired: '否', description: '控制生成文本的随机性,值越小结果越随机' },
  181. { name: 'max_tokens', type: 'NUMBER', defaultValue: '1024', isRequired: '否', description: '生成文本的最大令牌数' },
  182. { name: 'top_p', type: 'NUMBER', defaultValue: '0.95', isRequired: '否', description: '核采样阈值,控制模型选择词的概率分布' },
  183. { name: 'frequency_penalty', type: 'NUMBER', defaultValue: '0', isRequired: '否', description: '减少重复词组的出现概率' },
  184. { name: 'presence_penalty', type: 'NUMBER', defaultValue: '0', isRequired: '否', description: '增加新话题出现的概率' },
  185. { name: 'stream', type: 'BOOLEAN', defaultValue: 'false', isRequired: '否', description: '是否启用流式输出' }
  186. ])
  187. // 统计卡片数据
  188. const statList = ref([
  189. { label: '调用次数', value: '156,783', trend: 12.5 },
  190. { label: '平均响应时间', value: '1.68s', trend: -0.2 },
  191. { label: '成功率', value: '99.87%', trend: 0.05 },
  192. { label: 'Token消耗', value: '5.6M', trend: 18.3 }
  193. ])
  194. // 其他响应式数据
  195. const statRange = ref('month')
  196. const drawerVisible = ref(false)
  197. const currentRow = ref({})
  198. const currentRowIndex = ref(-1)
  199. // 生命周期钩子
  200. onMounted(() => {
  201. initCallTrendChart()
  202. })
  203. // 方法
  204. const handleEdit = (index, row) => {
  205. currentRowIndex.value = index
  206. currentRow.value = { ...row }
  207. drawerVisible.value = true
  208. }
  209. const handleDelete = (index) => {
  210. tableData.value.splice(index, 1)
  211. }
  212. const saveEdit = () => {
  213. tableData.value[currentRowIndex.value] = { ...currentRow.value }
  214. drawerVisible.value = false
  215. }
  216. const returnModel = () => {
  217. router.push('/modelMarket')
  218. }
  219. const initCallTrendChart = () => {
  220. const callTrendChart = echarts.init(document.getElementById('callTrendChart'))
  221. const option = {
  222. tooltip: {},
  223. xAxis: {
  224. type: 'category',
  225. data: ['1月', '2月', '3月', '4月', '5月', '6月'],
  226. axisLine: { lineStyle: { color: '#e5e6eb' } },
  227. axisLabel: { color: '#888' },
  228. },
  229. yAxis: {
  230. type: 'value',
  231. axisLine: { lineStyle: { color: '#e5e6eb' } },
  232. splitLine: { lineStyle: { color: '#f0f0f0' } },
  233. axisLabel: { color: '#888' },
  234. },
  235. series: [
  236. {
  237. data: [120, 200, 150, 80, 70, 110],
  238. type: 'bar',
  239. name: '调用次数',
  240. itemStyle: { color: '#409EFF', borderRadius: [4, 4, 0, 0] },
  241. barWidth: 24,
  242. },
  243. ],
  244. grid: { left: 40, right: 20, top: 30, bottom: 30 },
  245. }
  246. callTrendChart.setOption(option)
  247. }
  248. </script>
  249. <style scoped>
  250. .actions{
  251. display: flex;
  252. }
  253. .drawer-style{
  254. header.el-drawer__header{
  255. margin-bottom: 0 !important;
  256. }
  257. }
  258. :deep(header.el-drawer__header){
  259. margin-bottom: 0 !important;
  260. }
  261. /* 确保图标靠右对齐 */
  262. .parameter-card {
  263. margin-bottom: 0;
  264. }
  265. .card-content {
  266. display: flex;
  267. justify-content: space-between;
  268. width: 100%;
  269. }
  270. .param-name {
  271. font-size: 14px;
  272. color: #606266;
  273. }
  274. .center-content {
  275. display: flex;
  276. justify-content: center; /* 水平居中 */
  277. align-items: center; /* 垂直居中 */
  278. height: 100%; /* 确保容器有足够的高度 */
  279. }
  280. .model-detail-root {
  281. background: #f7f8fa;
  282. min-height: 100vh;
  283. padding: 0 24px 24px 24px;
  284. }
  285. .model-breadcrumb {
  286. margin: 18px 0 8px 0;
  287. }
  288. .model-top-card {
  289. border-radius: 10px;
  290. border: 1px solid #e5e6eb;
  291. background: #fff;
  292. box-shadow: none;
  293. padding: 0 32px 0 32px;
  294. margin-bottom: 24px;
  295. }
  296. .model-top-flex {
  297. display: flex;
  298. flex-direction: row;
  299. justify-content: space-between;
  300. align-items: flex-start;
  301. min-height: 160px;
  302. padding: 24px 0 18px 0;
  303. }
  304. .model-top-left {
  305. flex: 1.5;
  306. display: flex;
  307. flex-direction: column;
  308. justify-content: flex-start;
  309. gap: 20px;
  310. }
  311. .model-top-title {
  312. font-size: 16px;
  313. font-weight: 600;
  314. margin-bottom: 12px;
  315. }
  316. .model-top-row {
  317. display: flex;
  318. align-items: center;
  319. font-size: 13px;
  320. margin-bottom: 4px;
  321. gap: 20px;
  322. }
  323. .model-top-label {
  324. color: #888;
  325. min-width: 80px;
  326. margin-right: 2px;
  327. }
  328. .model-top-value {
  329. color: #222;
  330. margin-right: 24px;
  331. word-break: break-all;
  332. }
  333. .model-top-link {
  334. color: #409eff;
  335. text-decoration: underline;
  336. }
  337. .model-top-right {
  338. flex: 1;
  339. display: flex;
  340. flex-direction: column;
  341. align-items: flex-start;
  342. justify-content: space-between;
  343. min-width: 360px;
  344. margin-left: 32px;
  345. margin-right: 32px;
  346. gap: 30px;
  347. }
  348. .model-top-right-info {
  349. color: #222;
  350. font-size: 13px;
  351. margin-bottom: 0;
  352. text-align: left;
  353. display: flex;
  354. flex-direction: column;
  355. align-items: flex-start;
  356. justify-content: center;
  357. flex: 1;
  358. min-width: 180px;
  359. gap: 20px;
  360. }
  361. .model-top-right-btns {
  362. display: flex;
  363. flex-direction: row;
  364. gap:0.1px;
  365. margin-top: 0;
  366. align-items: flex-start;
  367. justify-content: flex-end;
  368. }
  369. .model-section-card {
  370. border-radius: 10px;
  371. border: 1px solid #e5e6eb;
  372. background: #fff;
  373. box-shadow: none;
  374. margin-top: 24px;
  375. padding: 0 0 18px 0;
  376. }
  377. .model-section-title {
  378. display: flex;
  379. justify-content: space-between;
  380. align-items: center;
  381. padding: 0 24px;
  382. font-size: 15px;
  383. font-weight: 500;
  384. height: 48px;
  385. background: #fff;
  386. border-bottom: 1px solid #f0f0f0;
  387. }
  388. .model-btn-add {
  389. min-width: 90px;
  390. margin-top: 0;
  391. align-self: flex-start;
  392. }
  393. .model-param-table {
  394. margin: 0 24px 0 24px;
  395. font-size: 13px;
  396. --el-table-border-color: #e5e6eb;
  397. --el-table-header-bg-color: #f7f8fa;
  398. --el-table-header-text-color: #888;
  399. --el-table-row-hover-bg-color: #f5f7fa;
  400. }
  401. .model-th {
  402. background: #f7f8fa !important;
  403. color: #888 !important;
  404. font-weight: 500 !important;
  405. height: 38px !important;
  406. }
  407. .model-td {
  408. height: 38px !important;
  409. padding: 0 8px !important;
  410. }
  411. .model-stat-row {
  412. margin-top: 24px;
  413. margin-bottom: 0;
  414. position: relative;
  415. }
  416. .model-stat-card {
  417. border-radius: 10px;
  418. border: 1px solid #e5e6eb;
  419. background: #fff;
  420. box-shadow: none;
  421. min-height: 100px;
  422. display: flex;
  423. flex-direction: column;
  424. justify-content: center;
  425. align-items: flex-start;
  426. padding: 18px 0 12px 24px;
  427. }
  428. .model-stat-label {
  429. color: #888;
  430. font-size: 13px;
  431. margin-bottom: 2px;
  432. }
  433. .model-stat-value {
  434. font-size: 22px;
  435. font-weight: 600;
  436. color: #222;
  437. margin: 2px 0 2px 0;
  438. }
  439. .model-stat-trend {
  440. font-size: 12px;
  441. margin-top: 2px;
  442. }
  443. .model-stat-trend.up {
  444. color: #67c23a;
  445. }
  446. .model-stat-trend.down {
  447. color: #f56c6c;
  448. }
  449. .model-stat-select {
  450. position: absolute;
  451. right: 24px;
  452. top: 0;
  453. }
  454. .model-chart-section {
  455. margin-top: 24px;
  456. padding: 0 0 18px 0;
  457. }
  458. .model-chart-title {
  459. font-size: 15px;
  460. font-weight: 500;
  461. color: #222;
  462. margin-bottom: 8px;
  463. padding-left: 24px;
  464. padding-top: 18px;
  465. }
  466. .model-chart-footer {
  467. color: #bfbfbf;
  468. font-size: 12px;
  469. text-align: right;
  470. margin-top: 8px;
  471. padding-right: 24px;
  472. }
  473. </style>