zhaoen 5 місяців тому
батько
коміт
8eec1344b1

+ 0 - 0
src/api/enterpriseModelConfig/index.ts


+ 65 - 0
src/api/modleServiceProvide/index.ts

@@ -0,0 +1,65 @@
+import request from '../../utils/request';
+import { AxiosPromise } from 'axios';
+import {
+    ModelVendorDto,
+    ModelVendorForm,
+    ModelVendorQuery
+} from './type';
+
+/**
+ * 分页查询模型供应商列表
+ * @param query 查询参数
+ */
+export const listModelVendor = (query?: ModelVendorQuery): AxiosPromise<{ list: ModelVendorDto[]; total: number }> => {
+    return request({
+        url: '/ai/model/vendor/page',
+        method: 'get',
+        params: query
+    });
+};
+
+/**
+ * 获取模型供应商详细信息
+ * @param id 供应商编码
+ */
+export const getModelVendor = (id: string): AxiosPromise<ModelVendorDto> => {
+    return request({
+        url: `/ai/model/vendor/${id}`,
+        method: 'get'
+    });
+};
+
+/**
+ * 新增模型供应商
+ * @param data 表单数据
+ */
+export const addModelVendor = (data: ModelVendorForm): AxiosPromise<void> => {
+    return request({
+        url: '/ai/model/vendor',
+        method: 'post',
+        data
+    });
+};
+
+/**
+ * 修改模型供应商
+ * @param data 表单数据
+ */
+export const updateModelVendor = (data: ModelVendorForm): AxiosPromise<void> => {
+    return request({
+        url: `/ai/model/vendor/${data.id}`,
+        method: 'put',
+        data
+    });
+};
+
+/**
+ * 删除模型供应商
+ * @param id 供应商编码
+ */
+export const delModelVendor = (id: string): AxiosPromise<void> => {
+    return request({
+        url: `/ai/model/vendor/${id}`,
+        method: 'delete'
+    });
+};

+ 6 - 0
src/router/index.ts

@@ -91,6 +91,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         name:'ModleServiceProvideDetail',
         meta: {title: '模型服务商详情', icon: 'dashboard', affix: true}
       },
+      {
+        path: '/enterpriseModelConfig',
+        component: () => import('@/views/enterpriseModelConfig/index.vue'),
+        name:'enterpriseModelConfig',
+        meta: {title: '企业模型配置', icon: 'dashboard', affix: true}
+      },
       {
         path:'/dashboard',
         component: () => import('@/views/dashboard/index.vue'),

+ 397 - 0
src/views/enterpriseModelConfig/components/AddModelDialog.vue

@@ -0,0 +1,397 @@
+<template>
+  <el-dialog
+      v-model="dialogVisible"
+      :title="isEdit ? '编辑模型配置' : '新增模型配置'"
+      width="800px"
+      :close-on-click-modal="false"
+      :destroy-on-close="true"
+  >
+    <!-- 步骤导航 -->
+    <div class="step-header">
+      <div
+          v-for="(step, index) in steps"
+          :key="index"
+          class="step-item"
+          :class="{ active: currentStep === index + 1, completed: stepsCompleted[index] }"
+      >
+        <span class="step-number">{{ index + 1 }}</span>
+        <span class="step-title">{{ step }}</span>
+      </div>
+    </div>
+
+    <!-- 步骤内容 -->
+    <el-form
+        ref="formRef"
+        :model="form"
+        :rules="rules"
+        label-width="120px"
+        style="margin-top: 20px"
+    >
+      <!-- 第一步:选择模型 -->
+      <div v-if="currentStep === 1">
+        <el-form-item label="选择模型服务商">
+          <el-radio-group v-model="form.step1.selectedVendor">
+            <el-radio v-for="vendor in vendors" :key="vendor.id" :label="vendor.id">
+              {{ vendor.name }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+
+        <el-form-item label="选择模型" prop="selectedModel">
+          <el-select
+              v-model="form.step1.selectedModel"
+              placeholder="请选择模型"
+              @change="onModelSelected"
+          >
+            <el-option
+                v-for="model in modelsList"
+                :key="model.id"
+                :label="model.name"
+                :value="model.id"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+
+      <!-- 第二步:基本配置 -->
+      <div v-if="currentStep === 2">
+        <el-form-item label="模型名称" prop="modelName">
+          <el-input v-model="form.step2.modelName" placeholder="请输入模型名称,如:智能客服助手" />
+          <p class="form-tip">给模型起一个易于识别的名字,便于在企业内部使用</p>
+        </el-form-item>
+
+        <el-form-item label="模型版本" prop="modelVersion">
+          <el-input v-model="form.step2.modelVersion" placeholder="请输入模型版本" />
+          <p class="form-tip">API调用时使用的模型版本标识符</p>
+        </el-form-item>
+
+        <el-form-item label="环境" prop="env">
+          <el-select v-model="form.step2.env" placeholder="请选择环境">
+            <el-option label="生产" value="PROD" />
+            <el-option label="测试" value="TEST" />
+            <el-option label="开发" value="DEV" />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="API基础地址" prop="apiBaseURL">
+          <el-input v-model="form.step2.apiBaseURL" placeholder="默认使用服务商的API基础地址,可自定义" />
+        </el-form-item>
+
+        <el-form-item label="API密钥" prop="apiKey">
+          <el-input v-model="form.step2.apiKey" placeholder="API安全密钥将使用安全算法进行加密存储" />
+        </el-form-item>
+
+        <el-form-item label="优先级" prop="priority">
+          <el-input-number
+              v-model="form.step2.priority"
+              :min="0"
+              :max="100"
+              controls-position="right"
+          />
+          <p class="form-tip">数值越大优先级越高,范围0-100</p>
+        </el-form-item>
+
+        <el-form-item label="状态" prop="status">
+          <el-switch v-model="form.step2.status" />
+          <p class="form-tip" :class="{ 'text-gray': !form.step2.status }">
+            {{ form.step2.status ? '启用' : '禁用' }}
+          </p>
+        </el-form-item>
+
+        <el-form-item label="描述" prop="description">
+          <el-input
+              v-model="form.step2.description"
+              type="textarea"
+              :rows="3"
+              placeholder="请输入模型描述"
+          />
+        </el-form-item>
+      </div>
+
+      <!-- 第三步:参数设置 -->
+      <div v-if="currentStep === 3">
+        <el-form-item label="快速添加参数模板">
+          <el-select v-model="form.step3.selectedTemplate" placeholder="请选择模板" @change="addTemplateParams">
+            <el-option
+                v-for="template in parameterTemplates"
+                :key="template.id"
+                :label="template.name"
+                :value="template.id"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="参数列表">
+          <el-table :data="form.step3.parameters" border style="width: 100%">
+            <el-table-column prop="name" label="参数名" />
+            <el-table-column prop="value" label="参数值">
+              <template #default="scope">
+                <el-input v-model="scope.row.value" />
+              </template>
+            </el-table-column>
+            <el-table-column label="操作">
+              <template #default="scope">
+                <el-button
+                    size="small"
+                    type="danger"
+                    @click="deleteParameter(scope.$index)"
+                >
+                  删除
+                </el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </div>
+    </el-form>
+
+    <!-- 操作按钮 -->
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="prevStep" v-if="currentStep > 1">上一步</el-button>
+        <el-button @click="cancelDialog">取消</el-button>
+        <el-button
+            type="primary"
+            @click="nextStep"
+            v-if="currentStep < 3"
+        >
+          下一步
+        </el-button>
+        <el-button
+            type="success"
+            @click="submitForm"
+            v-if="currentStep === 3"
+        >
+          保存
+        </el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, reactive, watch, defineEmits, defineProps } from 'vue'
+import { ElMessage } from 'element-plus'
+
+const props = defineProps(['model'])
+const emit = defineEmits(['success'])
+
+// 弹窗控制
+const dialogVisible = ref(false)
+const currentStep = ref(1)
+const stepsCompleted = ref([false, false, false])
+const isEdit = ref(false)
+
+// 表单数据
+const formRef = ref()
+const form = reactive({
+  step1: {
+    selectedVendor: '',
+    selectedModel: ''
+  },
+  step2: {
+    modelName: '',
+    modelVersion: '',
+    env: '',
+    apiBaseURL: '',
+    apiKey: '',
+    priority: 0,
+    status: true,
+    description: ''
+  },
+  step3: {
+    selectedTemplate: '',
+    parameters: []
+  }
+})
+
+// 表单规则
+const rules = {
+  selectedModel: [{ required: true, message: '请选择模型', trigger: 'change' }],
+  modelName: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
+  modelVersion: [{ required: true, message: '请输入模型版本', trigger: 'blur' }],
+  env: [{ required: true, message: '请选择环境', trigger: 'change' }],
+  apiBaseURL: [{ required: true, message: '请输入API基础地址', trigger: 'blur' }],
+  apiKey: [{ required: true, message: '请输入API密钥', trigger: 'blur' }],
+  priority: [
+    { required: true, message: '请输入优先级', trigger: 'blur' },
+    { type: 'number', min: 0, max: 100, message: '优先级范围0-100', trigger: 'blur' }
+  ]
+}
+
+// 模拟数据
+const vendors = ref([
+  { id: 'v1', name: '阿里云' },
+  { id: 'v2', name: '腾讯云' }
+])
+
+const modelsList = ref([
+  { id: 'm1', name: 'Qwen-Plus' },
+  { id: 'm2', name: 'Qwen-Max' }
+])
+
+const parameterTemplates = ref([
+  { id: 't1', name: '基础模板', parameters: [{ name: 'timeout', value: '60' }] },
+  { id: 't2', name: '高级模板', parameters: [{ name: 'retries', value: '3' }] }
+])
+
+// 方法
+const open = (row = null) => {
+  dialogVisible.value = true
+  isEdit.value = !!row
+
+  if (row) {
+    // 编辑模式初始化数据
+    form.step1.selectedVendor = row.vendorId
+    form.step1.selectedModel = row.modelId
+    form.step2.modelName = row.modelName
+    form.step2.modelVersion = row.modelVersion
+    form.step2.env = row.env
+    form.step2.apiBaseURL = row.apiBaseURL
+    form.step2.apiKey = row.apiKey
+    form.step2.priority = row.priority
+    form.step2.status = row.status
+    form.step2.description = row.description
+    form.step3.parameters = row.parameters || []
+  } else {
+    resetForm()
+  }
+}
+
+const onModelSelected = () => {
+  stepsCompleted.value[0] = true
+}
+
+const nextStep = async () => {
+  const currentRules = getRulesForCurrentStep()
+  const isValid = await formRef.value.validate(currentRules)
+  if (!isValid) return
+
+  currentStep.value++
+  stepsCompleted.value[currentStep.value - 1] = true
+}
+
+const prevStep = () => {
+  currentStep.value--
+}
+
+const cancelDialog = () => {
+  dialogVisible.value = false
+  resetForm()
+}
+
+const submitForm = async () => {
+  const isValid = await formRef.value.validate()
+  if (!isValid) return
+
+  // 提交逻辑
+  ElMessage.success('保存成功')
+  dialogVisible.value = false
+  emit('success')
+  resetForm()
+}
+
+const addTemplateParams = () => {
+  const template = parameterTemplates.value.find(t => t.id === form.step3.selectedTemplate)
+  if (template) {
+    form.step3.parameters.push(...template.parameters)
+  }
+}
+
+const deleteParameter = (index) => {
+  form.step3.parameters.splice(index, 1)
+}
+
+const resetForm = () => {
+  currentStep.value = 1
+  stepsCompleted.value = [false, false, false]
+  formRef.value.resetFields()
+  form.step1.selectedVendor = ''
+  form.step1.selectedModel = ''
+  form.step2.modelName = ''
+  form.step2.modelVersion = ''
+  form.step2.env = ''
+  form.step2.apiBaseURL = ''
+  form.step2.apiKey = ''
+  form.step2.priority = 0
+  form.step2.status = true
+  form.step2.description = ''
+  form.step3.selectedTemplate = ''
+  form.step3.parameters = []
+}
+
+// 辅助函数
+const getRulesForCurrentStep = () => {
+  const stepRules = {
+    1: { selectedModel: rules.selectedModel },
+    2: {
+      modelName: rules.modelName,
+      modelVersion: rules.modelVersion,
+      env: rules.env,
+      apiBaseURL: rules.apiBaseURL,
+      apiKey: rules.apiKey,
+      priority: rules.priority
+    }
+  }
+  return stepRules[currentStep.value]
+}
+
+defineExpose({ open })
+</script>
+
+<style scoped>
+.step-header {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 20px;
+}
+
+.step-item {
+  flex: 1;
+  text-align: center;
+  position: relative;
+  padding-bottom: 10px;
+  cursor: not-allowed;
+}
+
+.step-item.active, .step-item.completed {
+  cursor: default;
+}
+
+.step-item.active .step-number {
+  background-color: #409EFF;
+  color: white;
+}
+
+.step-item.completed .step-number {
+  background-color: #67C23A;
+  color: white;
+}
+
+.step-number {
+  display: inline-block;
+  width: 24px;
+  height: 24px;
+  line-height: 24px;
+  border-radius: 50%;
+  background-color: #E4E7ED;
+  margin-right: 8px;
+  font-weight: bold;
+}
+
+.form-tip {
+  margin-top: 4px;
+  font-size: 12px;
+  color: #999;
+}
+
+.text-gray {
+  color: #999;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 279 - 0
src/views/enterpriseModelConfig/index.vue

@@ -0,0 +1,279 @@
+<template>
+  <div class="model-management">
+    <!-- 标题 -->
+    <h3 class="title">企业模型配置</h3>
+
+    <!-- 使用提示 -->
+    <el-card class="tip-card">
+      <div class="tip-header">
+        <el-icon><Warning /></el-icon>
+        <span class="tip-title">使用提醒</span>
+      </div>
+      <div class="tip-content">
+        企业模型配置是基于市场模型定义的实际应用配置,可设置不同环境的API密钥,自定义参数等,同一模型可创建多个配置用于不同场景。
+      </div>
+    </el-card>
+
+    <!-- 筛选器 -->
+    <el-row :gutter="20" style="margin-bottom: 20px">
+      <!-- 新增配置和导出按钮 -->
+      <el-col :span="4" style="margin-left: 20px">
+        <div class="button-group">
+          <el-button type="primary" @click="openAddDialog">+ 新增配置</el-button>
+          <el-button>导出</el-button>
+        </div>
+      </el-col>
+
+      <!-- 搜索区域 -->
+      <el-col :span="16">
+        <el-form inline :model="queryParams" label-width="100px">
+          <el-form-item label="模型名称">
+            <el-input v-model="queryParams.modelName" placeholder="请输入模型名称" style="width: 240px" />
+          </el-form-item>
+          <el-form-item label="模型类型">
+            <el-select v-model="queryParams.modelType" placeholder="请选择" style="width: 240px">
+              <el-option label="LLM" value="LLM" />
+              <el-option label="多模态" value="MULTIMODAL" />
+              <el-option label="OCR" value="OCR" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="状态">
+            <el-select v-model="queryParams.status" placeholder="请选择" style="width: 240px">
+              <el-option label="全部" value="" />
+              <el-option label="启用" value="1" />
+              <el-option label="禁用" value="0" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="创建人">
+            <el-input v-model="queryParams.creator" placeholder="请输入创建人" style="width: 240px" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery">搜索</el-button>
+            <el-button @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-col>
+    </el-row>
+
+    <!-- 环境筛选 -->
+    <el-row :gutter="20" style="margin-bottom: 20px">
+      <el-col :span="4" style="margin-left: 20px">
+        <el-radio-group v-model="queryParams.env" size="large">
+          <el-radio-button label="">全部</el-radio-button>
+          <el-radio-button label="PROD">生产</el-radio-button>
+          <el-radio-button label="TEST">测试</el-radio-button>
+          <el-radio-button label="DEV">开发</el-radio-button>
+        </el-radio-group>
+      </el-col>
+    </el-row>
+
+    <!-- 表格区域 -->
+    <el-table :data="models" border stripe @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" />
+      <el-table-column prop="modelName" label="模型名称" />
+      <el-table-column prop="modelType" label="模型类型" />
+      <el-table-column prop="modelVersion" label="模型版本" />
+      <el-table-column prop="env" label="环境" />
+      <el-table-column prop="creator" label="创建人" />
+      <el-table-column prop="createdAt" label="创建时间" />
+      <el-table-column label="操作" width="200">
+        <template #default="scope">
+          <el-button size="mini" type="text" @click="handleView(scope.row)">查看</el-button>
+          <el-button size="mini" type="text" @click="handleEdit(scope.row)">编辑</el-button>
+          <el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button>
+          <el-button size="mini" type="text" @click="handleDelete(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 批量操作 -->
+    <el-row :gutter="20" style="margin-top: 15px; margin-bottom: 10px">
+      <el-col :span="4" style="margin-left: 20px">
+        <el-button v-if="selectedRows.length > 0" @click="batchEnable">
+          批量启用({{ selectedRows.length }}项)
+        </el-button>
+        <el-button v-if="selectedRows.length > 0" @click="batchDisable">
+          批量禁用({{ selectedRows.length }}项)
+        </el-button>
+        <el-button v-if="selectedRows.length > 0" type="danger" @click="batchDelete">
+          批量删除({{ selectedRows.length }}项)
+        </el-button>
+      </el-col>
+      <el-col :span="4" :offset="16">
+        <el-pagination
+            @size-change="handleSizeChange"
+            @current-change="handleCurrentChange"
+            :current-page="queryParams.pageNum"
+            :page-sizes="[10, 20, 50, 100]"
+            :page-size="queryParams.pageSize"
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="total"
+        />
+      </el-col>
+    </el-row>
+
+    <!-- 新增/编辑弹窗 -->
+    <AddModelDialog ref="addModelDialog" @success="getList" />
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { Warning } from '@element-plus/icons-vue'
+
+// 查询参数
+const queryParams = ref({
+  modelName: '',
+  modelType: '',
+  status: '',
+  creator: '',
+  env: '',
+  pageNum: 1,
+  pageSize: 10
+})
+
+// 选中行
+const selectedRows = ref([])
+const addModelDialog = ref()
+
+// 表格数据
+const models = ref([])
+const total = ref(0)
+
+// 获取列表数据
+const getList = async () => {
+  // const res = await listModelVendor(queryParams.value)
+  // const list = res.data.list || []
+
+  // 模拟数据
+  const list = [
+    {
+      modelName: 'Qwen',
+      modelType: 'LLM',
+      modelVersion: 'v2.0',
+      env: 'PROD',
+      creator: 'admin',
+      createdAt: '2024-03-20',
+      status: 1
+    },
+    // ...其他数据
+  ]
+
+  models.value = list
+  total.value = list.length
+}
+
+// 搜索按钮点击事件
+const handleQuery = () => {
+  queryParams.value.pageNum = 1
+  getList()
+}
+
+// 重置按钮点击事件
+const resetQuery = () => {
+  queryParams.value = {
+    modelName: '',
+    modelType: '',
+    status: '',
+    creator: '',
+    env: '',
+    pageNum: 1,
+    pageSize: 10
+  }
+  getList()
+}
+
+// 分页相关方法
+const handleSizeChange = (val) => {
+  queryParams.value.pageSize = val
+  getList()
+}
+
+const handleCurrentChange = (val) => {
+  queryParams.value.pageNum = val
+  getList()
+}
+
+// 操作方法
+const handleAdd = () => {
+  addModelDialog.value.open()
+}
+
+const handleEdit = (row) => {
+  addModelDialog.value.open(row) // 传入编辑数据
+}
+
+const handleView = (row) => {
+  console.log('查看:', row)
+}
+
+const handleDetail = (row) => {
+  console.log('详情:', row)
+}
+
+const handleDelete = (row) => {
+  console.log('删除:', row)
+}
+
+// 批量操作
+const handleSelectionChange = (selection) => {
+  selectedRows.value = selection
+}
+
+const batchEnable = () => {
+  console.log('批量启用:', selectedRows.value)
+}
+
+const batchDisable = () => {
+  console.log('批量禁用:', selectedRows.value)
+}
+
+const batchDelete = () => {
+  console.log('批量删除:', selectedRows.value)
+}
+
+// 页面加载时获取数据
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped>
+.model-management {
+  padding: 20px;
+}
+
+.title {
+  margin-bottom: 20px;
+  font-size: 20px;
+  color: #333;
+}
+
+.tip-card {
+  background-color: #fffbe6;
+  border: 1px solid #ffe58f;
+  margin-bottom: 20px;
+}
+
+.tip-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  font-weight: bold;
+  color: #e69900;
+}
+
+.tip-title {
+  font-size: 16px;
+}
+
+.tip-content {
+  margin-top: 10px;
+  color: #666;
+}
+
+.button-group {
+  display: flex;
+  gap: 10px;
+}
+</style>