kboss/f/web-kboss/src/views/modelManagement/AddModelDialog.vue
2026-05-22 11:07:13 +08:00

423 lines
10 KiB
Vue

<template>
<el-dialog
title="模型上架"
:visible.sync="dialogVisible"
width="760px"
custom-class="add-model-dialog"
:before-close="handleClose"
>
<el-form ref="form" class="model-form" :model="form" :rules="rules" label-position="top">
<section class="form-section">
<div class="section-title">
<i class="el-icon-info"></i>
<span>基本信息</span>
</div>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="模型名称/版本" prop="name">
<el-input v-model="form.name" placeholder="请输入模型名称/版本"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="模型类型" prop="type">
<el-input v-model="form.type" placeholder="请输入模型类型"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商" prop="supplier">
<el-input v-model="form.supplier" placeholder="请输入供应商"></el-input>
</el-form-item>
</el-col>
</el-row>
</section>
<section class="form-section">
<div class="section-title">
<i class="el-icon-money"></i>
<span>模型价格</span>
</div>
<el-form-item label="计费方式">
<el-input v-model="form.billingMethod" placeholder="请输入计费方式"></el-input>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入单位"></el-input>
</el-form-item>
<el-form-item label="输入价格">
<el-input v-model="form.inputPrice" placeholder="请输入输入价格"></el-input>
</el-form-item>
<el-form-item label="输出价格">
<el-input v-model="form.outputPrice" placeholder="请输入输出价格"></el-input>
</el-form-item>
<el-form-item label="缓存命中价格">
<el-input v-model="form.cacheHitInputPrice" placeholder="请输入缓存命中价格"></el-input>
</el-form-item>
</section>
<section class="form-section">
<div class="section-title">
<i class="el-icon-s-operation"></i>
<span>模型介绍</span>
</div>
<el-form-item prop="description">
<el-input
v-model="form.description"
type="textarea"
:rows="6"
placeholder="请详细描述模型的功能、特点、使用场景等信息"
></el-input>
</el-form-item>
</section>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button class="cancel-btn" @click="handleClose">取消</el-button>
<el-button type="primary" class="submit-btn" icon="el-icon-upload2" @click="handleSubmit">提交</el-button>
</span>
</el-dialog>
</template>
<script>
const defaultForm = () => ({
id: '',
llmid: '',
name: '',
displayName: '',
type: '',
supplier: '',
contextLength: '',
billingMethod: '',
unit: '',
inputPrice: '',
outputPrice: '',
cacheHitInputPrice: '',
capabilities: '',
limitations: '',
highlights: '',
description: ''
})
export default {
name: 'AddModelDialog',
props: {
visible: {
type: Boolean,
default: false
},
modelDetail: {
type: Object,
default: null
}
},
data() {
return {
form: defaultForm(),
modelNameOptions: ['GPT-3.5-Turbo', 'GPT-4', 'Claude-3', 'DeepSeek-V4', 'Llama-2-70B'],
modelTypeOptions: [
{ label: '自然语言处理', value: '自然语言处理' },
{ label: '计算机视觉', value: '计算机视觉' },
{ label: '语音', value: '语音' },
{ label: '多模态', value: '多模态' }
],
supplierOptions: ['OpenAI', 'Google', '开元云', 'Anthropic', 'Meta', 'DeepSeek'],
rules: {
name: [{ required: true, message: '请输入模型名称/版本', trigger: 'blur' }],
type: [{ required: true, message: '请输入模型类型', trigger: 'blur' }],
supplier: [{ required: true, message: '请输入供应商', trigger: 'blur' }],
unit: [{ required: true, message: '请输入单位', trigger: 'blur' }],
}
}
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
}
}
},
watch: {
visible(value) {
if (value) {
this.fillForm(this.modelDetail)
}
},
modelDetail: {
handler(value) {
if (this.visible) {
this.fillForm(value)
}
},
deep: true
}
},
methods: {
fillForm(detail) {
if (!detail) {
this.form = defaultForm()
return
}
this.form = {
id: detail.id || '',
llmid: detail.llmid || '',
name: detail.model_name || '',
displayName: detail.display_name || detail.model_name || '',
type: detail.model_type || '',
supplier: detail.provider || '',
contextLength: detail.context_length || '',
billingMethod: detail.billing_method || '',
unit: detail.billing_unit || '',
inputPrice: detail.input_token_price == null ? '' : String(detail.input_token_price),
outputPrice: detail.output_token_price == null ? '' : String(detail.output_token_price),
cacheHitInputPrice: detail.cache_hit_input_price == null ? '' : String(detail.cache_hit_input_price),
capabilities: detail.capabilities || '',
limitations: detail.limitations || '',
highlights: detail.highlights || '',
description: detail.description || ''
}
},
buildSubmitPayload() {
return {
id: this.form.id,
llmid: this.form.llmid,
provider: this.form.supplier,
model_name: this.form.name,
display_name: this.form.name,
context_length: this.form.contextLength,
model_type: this.form.type,
input_token_price: this.form.inputPrice,
output_token_price: this.form.outputPrice,
cache_hit_input_price: this.form.cacheHitInputPrice,
billing_method: this.form.billingMethod,
billing_unit: this.form.unit,
capabilities: this.form.capabilities,
limitations: this.form.limitations,
highlights: this.form.highlights,
description: this.form.description
}
},
handleFileChange(file, fileList) {
this.form.fileList = fileList
},
handleFileRemove(file, fileList) {
this.form.fileList = fileList
},
handleClose() {
this.dialogVisible = false
},
handleSubmit() {
this.$refs.form.validate(valid => {
if (!valid) {
return
}
this.$emit('submit', this.buildSubmitPayload())
})
}
}
}
</script>
<style lang="less" scoped>
/deep/ .add-model-dialog {
margin-top: 40px !important;
border-radius: 10px;
overflow: hidden;
.el-dialog__header {
display: flex;
align-items: center;
height: 64px;
padding: 0 24px;
background: #ffffff;
border-bottom: 1px solid #edf0f3;
}
.el-dialog__title {
color: #111827;
font-size: 20px;
font-weight: 700;
}
.el-dialog__headerbtn {
top: 21px;
right: 24px;
font-size: 22px;
}
.el-dialog__body {
padding: 0 24px;
background: #ffffff;
overflow-x: hidden;
}
.el-dialog__footer {
padding: 16px 24px;
background: #ffffff;
border-top: 1px solid #edf0f3;
}
}
.model-form {
max-height: calc(100vh - 300px);
padding: 24px 26px;
overflow-x: hidden;
overflow-y: auto;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
background: #8a8f98;
border-radius: 999px;
}
&::-webkit-scrollbar-track {
background: #f1f2f4;
}
}
/deep/ .el-row {
max-width: 100%;
margin-left: 0 !important;
margin-right: 0 !important;
}
/deep/ .el-col {
padding-left: 0 !important;
padding-right: 16px !important;
}
/deep/ .el-col:nth-child(2n) {
padding-right: 0 !important;
}
.form-section {
margin-bottom: 28px;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 18px;
color: #111827;
font-size: 18px;
font-weight: 700;
i {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
margin-right: 12px;
font-size: 14px;
border-radius: 50%;
}
}
.form-section:nth-child(1) .section-title i {
color: #8b3ff6;
background: #f2e8ff;
}
.form-section:nth-child(2) .section-title i {
color: #16a34a;
background: #dcfce7;
}
.form-section:nth-child(3) .section-title i {
color: #6366f1;
background: #e8e9ff;
}
/deep/ .el-form-item {
margin-bottom: 16px;
max-width: 100%;
}
/deep/ .el-form-item__label {
padding: 0 0 7px;
color: #4b5563;
font-size: 14px;
line-height: 20px;
}
/deep/ .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label::before {
color: #f56c6c;
}
/deep/ .el-input__inner,
/deep/ .el-textarea__inner {
max-width: 100%;
color: #111827;
border-color: #dcdfe6;
border-radius: 7px;
box-shadow: none;
}
/deep/ .el-select,
/deep/ .el-input,
/deep/ .el-textarea {
max-width: 100%;
}
/deep/ .el-input__inner {
height: 40px;
line-height: 40px;
}
/deep/ .el-input__inner::placeholder,
/deep/ .el-textarea__inner::placeholder {
color: #a8abb2;
}
/deep/ .el-textarea__inner {
min-height: 112px !important;
padding: 12px 14px;
line-height: 1.6;
}
.dialog-footer {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
.cancel-btn {
color: #374151;
border: none;
background: transparent;
&:hover,
&:focus {
color: #111827;
background: transparent;
}
}
.submit-btn {
min-width: 104px;
height: 40px;
font-weight: 600;
background: linear-gradient(135deg, #8b2ff6 0%, #b02cf4 100%);
border: none;
border-radius: 8px;
box-shadow: 0 8px 18px rgba(139, 47, 246, 0.28);
&:hover,
&:focus {
background: linear-gradient(135deg, #7c25e8 0%, #a025e6 100%);
}
}
</style>