|
|
@@ -0,0 +1,472 @@
|
|
|
+<template>
|
|
|
+ <div class="model-detail-root">
|
|
|
+ <!-- 面包屑导航 -->
|
|
|
+ <el-breadcrumb separator="/" class="model-breadcrumb">
|
|
|
+ <el-breadcrumb-item>市场模型</el-breadcrumb-item>
|
|
|
+ <el-breadcrumb-item>模型详情</el-breadcrumb-item>
|
|
|
+ </el-breadcrumb>
|
|
|
+ <div class="model-top-right-btns">
|
|
|
+ <el-button type="plain" size="small" class="custom-button">
|
|
|
+ <el-icon><Refresh /></el-icon>
|
|
|
+ 刷新
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" size="small" class="custom-button">
|
|
|
+ <el-icon><Edit /></el-icon>
|
|
|
+ 编辑
|
|
|
+ </el-button>
|
|
|
+ <el-button type="danger" size="small" class="custom-button">
|
|
|
+ <el-icon><SwitchButton /></el-icon>
|
|
|
+ 禁用
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 顶部信息区(单卡片,左右分布) -->
|
|
|
+ <el-card class="model-top-card" shadow="never">
|
|
|
+ <div class="model-top-flex">
|
|
|
+ <!-- 左侧基本信息 -->
|
|
|
+ <div class="model-top-left">
|
|
|
+ <div class="model-top-title">基本信息</div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">模型名称:</span>
|
|
|
+ <span class="model-top-value">GPT-4</span>
|
|
|
+ </div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">模型类型:</span>
|
|
|
+ <span class="model-top-value">大语言模型(LLM)</span>
|
|
|
+ </div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">模型版本:</span>
|
|
|
+ <span class="model-top-value">gpt-4-1106-preview</span>
|
|
|
+ </div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">状态:</span>
|
|
|
+ <el-tag type="success" size="small">启用</el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">API地址:</span>
|
|
|
+ <a class="model-top-link" href="https://api.openai.com/v1/chat/completions" target="_blank">https://api.openai.com/v1/chat/completions</a>
|
|
|
+ </div>
|
|
|
+ <div class="model-top-row">
|
|
|
+ <span class="model-top-label">描述:</span>
|
|
|
+ <span class="model-top-value">GPT-4是OpenAI最先进的大型语言模型,在各种专业和学术基准上表现最佳。相比GPT-3.5,GPT-4在创造性写作、编码和解决复杂问题方面表现出显著提升,能更好地遵循用户意图,理解和生成更长的内容。</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 右侧服务信息和按钮 -->
|
|
|
+ <div class="model-top-right">
|
|
|
+ <div class="model-top-right-info">
|
|
|
+ <div><span class="model-top-label">服务商:</span>  OpenAI</div>
|
|
|
+ <div><span class="model-top-label">优先级:</span>  90</div>
|
|
|
+ <div><span class="model-top-label">创建时间:</span>  2025-05-15 10:30:15</div>
|
|
|
+ <div><span class="model-top-label">更新时间:</span>  2025-06-20 14:45:30</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 参数配置 -->
|
|
|
+ <el-card class="model-section-card">
|
|
|
+ <div class="model-section-title">
|
|
|
+ <span>参数配置</span>
|
|
|
+ <el-button type="primary" size="small" class="model-btn-add">+ 添加参数</el-button>
|
|
|
+ </div>
|
|
|
+ <el-table :data="tableData" class="model-param-table" border header-cell-class-name="model-th" cell-class-name="model-td">
|
|
|
+ <el-table-column prop="name" label="参数名称" align="center" min-width="120" />
|
|
|
+ <el-table-column prop="type" label="参数类型" align="center" min-width="120" />
|
|
|
+ <el-table-column prop="defaultValue" label="默认值" align="center" min-width="100" />
|
|
|
+ <el-table-column prop="isRequired" label="是否必需" align="center" min-width="100" />
|
|
|
+ <el-table-column prop="description" label="描述" min-width="200" />
|
|
|
+ <el-table-column label="操作" align="center" min-width="100">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button @click="handleEdit(scope.$index, scope.row)" type="text" size="small">编辑</el-button>
|
|
|
+ <el-button @click="handleDelete(scope.$index, scope.row)" type="text" size="small">删除</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-card>
|
|
|
+ <!-- 抽屉 -->
|
|
|
+ <el-drawer
|
|
|
+ title="编辑参数"
|
|
|
+ v-model="drawerVisible"
|
|
|
+ direction="rtl"
|
|
|
+ size="20%"
|
|
|
+ >
|
|
|
+ <el-divider style="margin: 1px 0;" />
|
|
|
+ <div >
|
|
|
+ <h3>模型</h3>
|
|
|
+ <el-card class="model-card" style=" background:#F5F5F5;">
|
|
|
+ GPT-4 (OpenAI)<br/>
|
|
|
+ <span style="color: #777777">版本: gpt-4-1106-preview</span>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <h3 style="color: #777777">快速添加参数模板</h3>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-card class="center-content" style="height: 150px;background: #FAFAFA" >
|
|
|
+ <div>OpenAI Chat模板</div>
|
|
|
+ <div style="color: #777777">6个参数</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-card class="center-content"style="background: #FAFAFA">
|
|
|
+ <div>OpenAI Embedding模板</div>
|
|
|
+ <div style="color: #777777">三个参数</div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <h3>参数列表</h3>
|
|
|
+ <div v-for="(item, index) in tableData" :key="index" class="parameter-card">
|
|
|
+ <el-card shadow="never" style="background-color: #FAFAFA" >
|
|
|
+ <div class="card-content">
|
|
|
+ <div class="param-name">{{ item.name }} <span style="color: #777777">{{item.type}}</span></div>
|
|
|
+ <div class="actions" style="display: flex">
|
|
|
+ <el-button @click="handleEdit(index, item)" type="text" size="small" ></el-button>
|
|
|
+ <el-button @click="handleDelete(index, item)" type="text" size="small"style="margin-left:auto" ><el-icon color="red"><DeleteFilled /></el-icon></el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+ <el-card style="display: flex;justify-content: center; align-items: center; flex-direction: column;" :body-style="{padding:'1px'}">
|
|
|
+ <el-button @click="addParameter" type="plain" >添加参数</el-button>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+
|
|
|
+ <div style="text-align: right; margin-top: 20px;">
|
|
|
+ <el-button @click="drawerVisible = false">取消</el-button>
|
|
|
+ <el-button @click="saveEdit" type="primary">保存</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </el-drawer>
|
|
|
+
|
|
|
+ <!-- 调用统计 -->
|
|
|
+ <el-row :gutter="20" class="model-stat-row">
|
|
|
+ <el-col :span="6" v-for="(item, idx) in statList" :key="item.label">
|
|
|
+ <el-card class="model-stat-card" shadow="never">
|
|
|
+ <div class="model-stat-label">{{ item.label }}</div>
|
|
|
+ <div class="model-stat-value">{{ item.value }}</div>
|
|
|
+ <div :class="['model-stat-trend', item.trend > 0 ? 'up' : 'down']">
|
|
|
+ <span v-if="item.trend > 0">↑ {{ item.trend }}% 较上月</span>
|
|
|
+ <span v-else>↓ {{ Math.abs(item.trend) }}% 较上月</span>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
+ </el-col>
|
|
|
+ <div class="model-stat-select">
|
|
|
+ <el-select v-model="statRange" size="small" style="width: 80px">
|
|
|
+ <el-option label="本月" value="month" />
|
|
|
+ <el-option label="本年" value="year" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 调用趋势 -->
|
|
|
+ <el-card class="model-section-card model-chart-section">
|
|
|
+ <div class="model-chart-title">调用趋势</div>
|
|
|
+ <div id="callTrendChart" style="width: 100%; height: 320px;"></div>
|
|
|
+ <div class="model-chart-footer">调用趋势曲线数据</div>
|
|
|
+ </el-card>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import * as echarts from 'echarts';
|
|
|
+import {Refresh} from "@element-plus/icons-vue";
|
|
|
+export default {
|
|
|
+ components: {Refresh},
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ tableData: [
|
|
|
+ { name: 'temperature', type: 'NUMBER', defaultValue: '0.7', isRequired: '否', description: '控制生成文本的随机性,值越小结果越随机' },
|
|
|
+ { name: 'max_tokens', type: 'NUMBER', defaultValue: '1024', isRequired: '否', description: '生成文本的最大令牌数' },
|
|
|
+ { name: 'top_p', type: 'NUMBER', defaultValue: '0.95', isRequired: '否', description: '核采样阈值,控制模型选择词的概率分布' },
|
|
|
+ { name: 'frequency_penalty', type: 'NUMBER', defaultValue: '0', isRequired: '否', description: '减少重复词组的出现概率' },
|
|
|
+ { name: 'presence_penalty', type: 'NUMBER', defaultValue: '0', isRequired: '否', description: '增加新话题出现的概率' },
|
|
|
+ { name: 'stream', type: 'BOOLEAN', defaultValue: 'false', isRequired: '否', description: '是否启用流式输出' }
|
|
|
+ ],
|
|
|
+ statList: [
|
|
|
+ { label: '调用次数', value: '156,783', trend: 12.5 },
|
|
|
+ { label: '平均响应时间', value: '1.68s', trend: -0.2 },
|
|
|
+ { label: '成功率', value: '99.87%', trend: 0.05 },
|
|
|
+ { label: 'Token消耗', value: '5.6M', trend: 18.3 }
|
|
|
+ ],
|
|
|
+ statRange: 'month',
|
|
|
+ drawerVisible: false,
|
|
|
+ currentRow: {}, // 当前编辑的行数据
|
|
|
+ currentRowIndex: -1
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 当前编辑的行索引
|
|
|
+ mounted() {
|
|
|
+ this.initCallTrendChart();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handleEdit(index, row) {
|
|
|
+ this.currentRowIndex = index
|
|
|
+ this.currentRow = { ...row } // 拷贝一份数据,避免直接修改原数据
|
|
|
+ this.drawerVisible = true
|
|
|
+ },
|
|
|
+ handleDelete(index, row) {
|
|
|
+ this.tableData.splice(index, 1)
|
|
|
+ },
|
|
|
+ saveEdit() {
|
|
|
+ this.$set(this.tableData, this.currentRowIndex, { ...this.currentRow }) // 替换数组中的对象
|
|
|
+ this.drawerVisible = false
|
|
|
+ },
|
|
|
+ initCallTrendChart() {
|
|
|
+ const callTrendChart = echarts.init(document.getElementById('callTrendChart'));
|
|
|
+ const option = {
|
|
|
+ tooltip: {},
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
|
|
+ axisLine: { lineStyle: { color: '#e5e6eb' } },
|
|
|
+ axisLabel: { color: '#888' },
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ axisLine: { lineStyle: { color: '#e5e6eb' } },
|
|
|
+ splitLine: { lineStyle: { color: '#f0f0f0' } },
|
|
|
+ axisLabel: { color: '#888' },
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ data: [120, 200, 150, 80, 70, 110],
|
|
|
+ type: 'bar',
|
|
|
+ name: '调用次数',
|
|
|
+ itemStyle: { color: '#409EFF', borderRadius: [4, 4, 0, 0] },
|
|
|
+ barWidth: 24,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ grid: { left: 40, right: 20, top: 30, bottom: 30 },
|
|
|
+ }
|
|
|
+ callTrendChart.setOption(option);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.actions{
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/* 确保图标靠右对齐 */
|
|
|
+
|
|
|
+.parameter-card {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.card-content {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.param-name {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+.center-content {
|
|
|
+
|
|
|
+ display: flex;
|
|
|
+ justify-content: center; /* 水平居中 */
|
|
|
+ align-items: center; /* 垂直居中 */
|
|
|
+ height: 100%; /* 确保容器有足够的高度 */
|
|
|
+}
|
|
|
+.model-detail-root {
|
|
|
+ background: #f7f8fa;
|
|
|
+ min-height: 100vh;
|
|
|
+ padding: 0 24px 24px 24px;
|
|
|
+}
|
|
|
+.model-breadcrumb {
|
|
|
+ margin: 18px 0 8px 0;
|
|
|
+}
|
|
|
+.model-top-card {
|
|
|
+ border-radius: 10px;
|
|
|
+ border: 1px solid #e5e6eb;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: none;
|
|
|
+ padding: 0 32px 0 32px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+}
|
|
|
+.model-top-flex {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ min-height: 160px;
|
|
|
+ padding: 24px 0 18px 0;
|
|
|
+}
|
|
|
+.model-top-left {
|
|
|
+ flex: 1.5;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: flex-start;
|
|
|
+}
|
|
|
+.model-top-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+.model-top-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 13px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+.model-top-label {
|
|
|
+ color: #888;
|
|
|
+ min-width: 80px;
|
|
|
+ margin-right: 2px;
|
|
|
+}
|
|
|
+.model-top-value {
|
|
|
+ color: #222;
|
|
|
+ margin-right: 24px;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
+.model-top-link {
|
|
|
+ color: #409eff;
|
|
|
+ text-decoration: underline;
|
|
|
+}
|
|
|
+.model-top-right {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: space-between;
|
|
|
+ min-width: 360px;
|
|
|
+ margin-left: 32px;
|
|
|
+ margin-right: 32px;
|
|
|
+ gap: 32px;
|
|
|
+}
|
|
|
+.model-top-right-info {
|
|
|
+ color: #222;
|
|
|
+ font-size: 13px;
|
|
|
+ margin-bottom: 0;
|
|
|
+ text-align: left;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: center;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 180px;
|
|
|
+}
|
|
|
+.model-top-right-btns {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ gap:0.1px;
|
|
|
+ margin-top: 0;
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: flex-end;
|
|
|
+}
|
|
|
+.model-section-card {
|
|
|
+ border-radius: 10px;
|
|
|
+ border: 1px solid #e5e6eb;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: none;
|
|
|
+ margin-top: 24px;
|
|
|
+ padding: 0 0 18px 0;
|
|
|
+}
|
|
|
+.model-section-title {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 24px;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ height: 48px;
|
|
|
+ background: #fff;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+.model-btn-add {
|
|
|
+ min-width: 90px;
|
|
|
+ margin-top: 0;
|
|
|
+ align-self: flex-start;
|
|
|
+}
|
|
|
+.model-param-table {
|
|
|
+ margin: 0 24px 0 24px;
|
|
|
+ font-size: 13px;
|
|
|
+ --el-table-border-color: #e5e6eb;
|
|
|
+ --el-table-header-bg-color: #f7f8fa;
|
|
|
+ --el-table-header-text-color: #888;
|
|
|
+ --el-table-row-hover-bg-color: #f5f7fa;
|
|
|
+}
|
|
|
+.model-th {
|
|
|
+ background: #f7f8fa !important;
|
|
|
+ color: #888 !important;
|
|
|
+ font-weight: 500 !important;
|
|
|
+ height: 38px !important;
|
|
|
+}
|
|
|
+.model-td {
|
|
|
+ height: 38px !important;
|
|
|
+ padding: 0 8px !important;
|
|
|
+}
|
|
|
+.model-stat-row {
|
|
|
+ margin-top: 24px;
|
|
|
+ margin-bottom: 0;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.model-stat-card {
|
|
|
+ border-radius: 10px;
|
|
|
+ border: 1px solid #e5e6eb;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: none;
|
|
|
+ min-height: 100px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: flex-start;
|
|
|
+ padding: 18px 0 12px 24px;
|
|
|
+}
|
|
|
+.model-stat-label {
|
|
|
+ color: #888;
|
|
|
+ font-size: 13px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+}
|
|
|
+.model-stat-value {
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #222;
|
|
|
+ margin: 2px 0 2px 0;
|
|
|
+}
|
|
|
+.model-stat-trend {
|
|
|
+ font-size: 12px;
|
|
|
+ margin-top: 2px;
|
|
|
+}
|
|
|
+.model-stat-trend.up {
|
|
|
+ color: #67c23a;
|
|
|
+}
|
|
|
+.model-stat-trend.down {
|
|
|
+ color: #f56c6c;
|
|
|
+}
|
|
|
+.model-stat-select {
|
|
|
+ position: absolute;
|
|
|
+ right: 24px;
|
|
|
+ top: 0;
|
|
|
+}
|
|
|
+.model-chart-section {
|
|
|
+ margin-top: 24px;
|
|
|
+ padding: 0 0 18px 0;
|
|
|
+}
|
|
|
+.model-chart-title {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #222;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ padding-left: 24px;
|
|
|
+ padding-top: 18px;
|
|
|
+}
|
|
|
+.model-chart-footer {
|
|
|
+ color: #bfbfbf;
|
|
|
+ font-size: 12px;
|
|
|
+ text-align: right;
|
|
|
+ margin-top: 8px;
|
|
|
+ padding-right: 24px;
|
|
|
+}
|
|
|
+
|
|
|
+</style>
|