feat: 模型创建
This commit is contained in:
parent
367e92c414
commit
7a2df95ede
@ -39,8 +39,8 @@ class AzureLLMModelCredential(BaseForm, BaseModelCredential):
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
AzureModelProvider().query(model_type, model_name, model_credential,
|
model = AzureModelProvider().get_model(model_type, model_name, model_credential)
|
||||||
message=[HumanMessage(content='valid')])
|
model([HumanMessage(content='valid')])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if isinstance(e, AppApiException):
|
if isinstance(e, AppApiException):
|
||||||
raise e
|
raise e
|
||||||
@ -69,7 +69,9 @@ model_dict = {
|
|||||||
'gpt-3.5-turbo-0301': ModelInfo('gpt-3.5-turbo-0301', '', ModelTypeConst.LLM, azure_llm_model_credential,
|
'gpt-3.5-turbo-0301': ModelInfo('gpt-3.5-turbo-0301', '', ModelTypeConst.LLM, azure_llm_model_credential,
|
||||||
api_version='2023-07-01-preview'),
|
api_version='2023-07-01-preview'),
|
||||||
'gpt-3.5-turbo-16k-0613': ModelInfo('gpt-3.5-turbo-16k-0613', '', ModelTypeConst.LLM, azure_llm_model_credential,
|
'gpt-3.5-turbo-16k-0613': ModelInfo('gpt-3.5-turbo-16k-0613', '', ModelTypeConst.LLM, azure_llm_model_credential,
|
||||||
api_version='2023-07-01-preview')
|
api_version='2023-07-01-preview'),
|
||||||
|
'gpt-4-0613': ModelInfo('gpt-4-0613', '', ModelTypeConst.LLM, azure_llm_model_credential,
|
||||||
|
api_version='2023-07-01-preview'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
|
import { request } from './../request/index'
|
||||||
import { Result } from '@/request/Result'
|
import { Result } from '@/request/Result'
|
||||||
import { get, post, del, put } from '@/request/index'
|
import { get, post, del, put } from '@/request/index'
|
||||||
import { type Ref } from 'vue'
|
import { type Ref } from 'vue'
|
||||||
import type { modelRequest, Provider, ListModelRequest, Model } from '@/api/type/model'
|
import type {
|
||||||
|
modelRequest,
|
||||||
|
Provider,
|
||||||
|
ListModelRequest,
|
||||||
|
Model,
|
||||||
|
BaseModel,
|
||||||
|
CreateModelRequest
|
||||||
|
} from '@/api/type/model'
|
||||||
|
import type { FormField } from '@/components/dynamics-form/type'
|
||||||
|
import type { KeyValue } from './type/common'
|
||||||
const prefix = '/model'
|
const prefix = '/model'
|
||||||
const prefix_provider = '/provider'
|
const prefix_provider = '/provider'
|
||||||
|
|
||||||
@ -23,7 +33,68 @@ const getProvider: (loading?: Ref<boolean>) => Promise<Result<Array<Provider>>>
|
|||||||
return get(`${prefix_provider}`, {}, loading)
|
return get(`${prefix_provider}`, {}, loading)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模型创建表单
|
||||||
|
* @param provider
|
||||||
|
* @param model_type
|
||||||
|
* @param model_name
|
||||||
|
* @param loading
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getModelCreateForm: (
|
||||||
|
provider: string,
|
||||||
|
model_type: string,
|
||||||
|
model_name: string,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<Array<FormField>>> = (provider, model_type, model_name, loading) => {
|
||||||
|
return get(`${prefix_provider}/model_form`, { provider, model_type, model_name }, loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取模型类型列表
|
||||||
|
* @param provider 供应商
|
||||||
|
* @param loading 加载器
|
||||||
|
* @returns 模型类型列表
|
||||||
|
*/
|
||||||
|
const listModelType: (
|
||||||
|
provider: string,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<Array<KeyValue<string, string>>>> = (provider, loading?: Ref<boolean>) => {
|
||||||
|
return get(`${prefix_provider}/model_type_list`, { provider }, loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取基础模型列表
|
||||||
|
* @param provider
|
||||||
|
* @param model_type
|
||||||
|
* @param loading
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const listBaseModel: (
|
||||||
|
provider: string,
|
||||||
|
model_type: string,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<Array<BaseModel>>> = (provider, model_type, loading) => {
|
||||||
|
return get(`${prefix_provider}/model_list`, { provider, model_type }, loading)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建模型
|
||||||
|
* @param request 请求对象
|
||||||
|
* @param loading 加载器
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const createModel: (request: CreateModelRequest, loading?: Ref<boolean>) => Promise<Model> = (
|
||||||
|
request,
|
||||||
|
loading
|
||||||
|
) => {
|
||||||
|
return post(`${prefix}`, request, {}, loading)
|
||||||
|
}
|
||||||
export default {
|
export default {
|
||||||
getModel,
|
getModel,
|
||||||
getProvider
|
getProvider,
|
||||||
|
getModelCreateForm,
|
||||||
|
listModelType,
|
||||||
|
listBaseModel,
|
||||||
|
createModel
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,4 +65,41 @@ interface Model {
|
|||||||
*/
|
*/
|
||||||
provider: string
|
provider: string
|
||||||
}
|
}
|
||||||
export type { modelRequest, Provider, ListModelRequest, Model }
|
interface CreateModelRequest {
|
||||||
|
/**
|
||||||
|
* 模型名
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
/**
|
||||||
|
* 模型类型
|
||||||
|
*/
|
||||||
|
model_type: string
|
||||||
|
/**
|
||||||
|
* 基础模型
|
||||||
|
*/
|
||||||
|
model_name: string
|
||||||
|
/**
|
||||||
|
* 认证信息
|
||||||
|
*/
|
||||||
|
credential: any
|
||||||
|
/**
|
||||||
|
* 供应商
|
||||||
|
*/
|
||||||
|
provider: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BaseModel {
|
||||||
|
/**
|
||||||
|
* 基础模型名称
|
||||||
|
*/
|
||||||
|
name: string
|
||||||
|
/**
|
||||||
|
* 基础模型描述
|
||||||
|
*/
|
||||||
|
desc: string
|
||||||
|
/**
|
||||||
|
* 基础模型类型
|
||||||
|
*/
|
||||||
|
model_type: string
|
||||||
|
}
|
||||||
|
export type { modelRequest, Provider, ListModelRequest, Model, BaseModel, CreateModelRequest }
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||||
</AppAvatar>
|
</AppAvatar>
|
||||||
<slot v-else name="icon"> </slot>
|
<slot v-else name="icon"> </slot>
|
||||||
<h4 class="ellipsis-1">{{ title }}</h4>
|
<h4 class="ellipsis-1" style="width: 100%">{{ title }}</h4>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
@ -59,6 +59,7 @@ function cardLeave() {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: var(--card-min-height);
|
min-height: var(--card-min-height);
|
||||||
|
min-width: var(--card-min-width);
|
||||||
border: 1px solid #ffffff;
|
border: 1px solid #ffffff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
.description {
|
.description {
|
||||||
@ -66,7 +67,7 @@ function cardLeave() {
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 40px;
|
height: var(--app-card-box-description-height, 40px);
|
||||||
color: var(--app-text-color-secondary);
|
color: var(--app-text-color-secondary);
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|||||||
@ -6,7 +6,6 @@
|
|||||||
label-suffix=":"
|
label-suffix=":"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
:model="formValue"
|
|
||||||
>
|
>
|
||||||
<slot :form_value="formValue"></slot>
|
<slot :form_value="formValue"></slot>
|
||||||
<template v-for="item in formFieldList" :key="item.field">
|
<template v-for="item in formFieldList" :key="item.field">
|
||||||
@ -34,7 +33,7 @@
|
|||||||
import type { Dict } from '@/api/type/common'
|
import type { Dict } from '@/api/type/common'
|
||||||
import FormItem from '@/components/dynamics-form/FormItem.vue'
|
import FormItem from '@/components/dynamics-form/FormItem.vue'
|
||||||
import type { FormField } from '@/components/dynamics-form/type'
|
import type { FormField } from '@/components/dynamics-form/type'
|
||||||
import { ref, onMounted, type Ref } from 'vue'
|
import { ref, onMounted, watch, type Ref } from 'vue'
|
||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import triggerApi from '@/api/provider'
|
import triggerApi from '@/api/provider'
|
||||||
import type Result from '@/request/Result'
|
import type Result from '@/request/Result'
|
||||||
@ -51,6 +50,8 @@ const props = withDefaults(
|
|||||||
defaultItemWidth?: string
|
defaultItemWidth?: string
|
||||||
|
|
||||||
parent_field?: string
|
parent_field?: string
|
||||||
|
|
||||||
|
modelValue?: Dict<any>
|
||||||
}>(),
|
}>(),
|
||||||
{ view: false, defaultItemWidth: '75%', otherParams: () => {} }
|
{ view: false, defaultItemWidth: '75%', otherParams: () => {} }
|
||||||
)
|
)
|
||||||
@ -98,9 +99,12 @@ const emit = defineEmits(['update:modelValue'])
|
|||||||
*/
|
*/
|
||||||
const change = (field: FormField, value: any) => {
|
const change = (field: FormField, value: any) => {
|
||||||
formValue.value[field.field] = value
|
formValue.value[field.field] = value
|
||||||
emit('update:modelValue', formValue.value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(formValue.value, () => {
|
||||||
|
emit('update:modelValue', formValue.value)
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 触发器,用户获取子表单 或者 下拉选项
|
* 触发器,用户获取子表单 或者 下拉选项
|
||||||
* @param field
|
* @param field
|
||||||
@ -143,18 +147,22 @@ const initDefaultData = (formField: FormField) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof props.render_data == 'string') {
|
render(props.render_data)
|
||||||
triggerApi.get(props.render_data, {}, loading).then((ok) => {
|
})
|
||||||
|
|
||||||
|
const render = (render_data: string | Array<FormField> | Promise<Result<Array<FormField>>>) => {
|
||||||
|
if (typeof render_data == 'string') {
|
||||||
|
triggerApi.get(render_data, {}, loading).then((ok) => {
|
||||||
formFieldList.value = ok.data
|
formFieldList.value = ok.data
|
||||||
})
|
})
|
||||||
} else if (props.render_data instanceof Array) {
|
} else if (render_data instanceof Array) {
|
||||||
formFieldList.value = props.render_data
|
formFieldList.value = render_data
|
||||||
} else {
|
} else {
|
||||||
props.render_data.then((ok) => {
|
render_data.then((ok) => {
|
||||||
formFieldList.value = ok.data
|
formFieldList.value = ok.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
/**
|
/**
|
||||||
* 校验函数
|
* 校验函数
|
||||||
*/
|
*/
|
||||||
@ -169,6 +177,7 @@ const validate = () => {
|
|||||||
defineExpose({
|
defineExpose({
|
||||||
initDefaultData,
|
initDefaultData,
|
||||||
validate,
|
validate,
|
||||||
|
render,
|
||||||
ruleFormRef
|
ruleFormRef
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
24
ui/src/utils/common.ts
Normal file
24
ui/src/utils/common.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 拆分数组 每n个拆分为一个数组
|
||||||
|
* @param sourceDataList 资源数据
|
||||||
|
* @param splitNum 每多少个拆分为一个数组
|
||||||
|
* @returns 拆分后数组
|
||||||
|
*/
|
||||||
|
export function splitArray<T>(sourceDataList: Array<T>, splitNum: number) {
|
||||||
|
const count =
|
||||||
|
sourceDataList.length % splitNum == 0
|
||||||
|
? sourceDataList.length / splitNum
|
||||||
|
: sourceDataList.length / splitNum + 1
|
||||||
|
const arrayList: Array<Array<T>> = []
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let index = i * splitNum
|
||||||
|
const list: Array<T> = []
|
||||||
|
let j = 0
|
||||||
|
while (j < splitNum && index < sourceDataList.length) {
|
||||||
|
list.push(sourceDataList[index++])
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
arrayList.push(list)
|
||||||
|
}
|
||||||
|
return arrayList
|
||||||
|
}
|
||||||
203
ui/src/views/template/component/CreateModel.vue
Normal file
203
ui/src/views/template/component/CreateModel.vue
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-loading="loading"
|
||||||
|
v-model="dialogVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="close"
|
||||||
|
>
|
||||||
|
<template #header="{ close, titleId, titleClass }">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item
|
||||||
|
><span @click="toSelectProvider" class="select-provider"
|
||||||
|
>选择供应商</span
|
||||||
|
></el-breadcrumb-item
|
||||||
|
>
|
||||||
|
<el-breadcrumb-item
|
||||||
|
><span class="active-breadcrumb">{{
|
||||||
|
`添加 ${providerValue?.name} 模型`
|
||||||
|
}}</span></el-breadcrumb-item
|
||||||
|
>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<DynamicsForm
|
||||||
|
v-model="form_data"
|
||||||
|
:render_data="model_form_field"
|
||||||
|
:model="form_data"
|
||||||
|
ref="dynamicsFormRef"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-form-item label="模型名称" prop="name" :rules="base_form_data_rule.name">
|
||||||
|
<el-input v-model="base_form_data.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="模型类型" prop="model_type" :rules="base_form_data_rule.model_type">
|
||||||
|
<el-select
|
||||||
|
v-loading="model_type_loading"
|
||||||
|
@change="list_base_model($event)"
|
||||||
|
style="width: 100%"
|
||||||
|
v-model="base_form_data.model_type"
|
||||||
|
class="m-2"
|
||||||
|
placeholder="请选择模型类型"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in model_type_list"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.key"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option
|
||||||
|
></el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="基础模型" prop="model_name" :rules="base_form_data_rule.model_name">
|
||||||
|
<el-select
|
||||||
|
@change="getModelForm($event)"
|
||||||
|
v-loading="base_model_loading"
|
||||||
|
style="width: 100%"
|
||||||
|
v-model="base_form_data.model_name"
|
||||||
|
class="m-2"
|
||||||
|
placeholder="请选择模型类型"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in base_model_list"
|
||||||
|
:key="item.name"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.name"
|
||||||
|
></el-option
|
||||||
|
></el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</DynamicsForm>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="submit"> 添加 </el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import type { Provider, BaseModel } from '@/api/type/model'
|
||||||
|
import type { Dict, KeyValue } from '@/api/type/common'
|
||||||
|
import ModelApi from '@/api/model'
|
||||||
|
import type { FormField } from '@/components/dynamics-form/type'
|
||||||
|
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||||
|
import type { FormRules } from 'element-plus'
|
||||||
|
import { MsgSuccess } from '@/utils/message'
|
||||||
|
const providerValue = ref<Provider>()
|
||||||
|
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
||||||
|
const emit = defineEmits(['change', 'submit'])
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
const model_type_loading = ref<boolean>(false)
|
||||||
|
const base_model_loading = ref<boolean>(false)
|
||||||
|
const model_type_list = ref<Array<KeyValue<string, string>>>([])
|
||||||
|
|
||||||
|
const base_model_list = ref<Array<BaseModel>>()
|
||||||
|
const model_form_field = ref<Array<FormField>>([])
|
||||||
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
|
||||||
|
const base_form_data_rule = ref<FormRules>({
|
||||||
|
name: { required: true, trigger: 'blur', message: '模型名不能为空' },
|
||||||
|
model_type: { required: true, trigger: 'change', message: '模型类型不能为空' },
|
||||||
|
model_name: { required: true, trigger: 'change', message: '基础模型不能为空' }
|
||||||
|
})
|
||||||
|
|
||||||
|
const base_form_data = ref<{
|
||||||
|
name: string
|
||||||
|
|
||||||
|
model_type: string
|
||||||
|
|
||||||
|
model_name: string
|
||||||
|
}>({ name: '', model_type: '', model_name: '' })
|
||||||
|
|
||||||
|
const credential_form_data = ref<Dict<any>>({})
|
||||||
|
|
||||||
|
const form_data = computed({
|
||||||
|
get: () => {
|
||||||
|
return { ...base_form_data.value, ...credential_form_data.value }
|
||||||
|
},
|
||||||
|
set: (event: any) => {
|
||||||
|
credential_form_data.value = event
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const getModelForm = (model_name: string) => {
|
||||||
|
if (providerValue.value) {
|
||||||
|
ModelApi.getModelCreateForm(
|
||||||
|
providerValue.value.provider,
|
||||||
|
form_data.value.model_type,
|
||||||
|
model_name
|
||||||
|
).then((ok) => {
|
||||||
|
model_form_field.value = ok.data
|
||||||
|
// 渲染动态表单
|
||||||
|
dynamicsFormRef.value?.render(model_form_field.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = (provider: Provider) => {
|
||||||
|
ModelApi.listModelType(provider.provider, model_type_loading).then((ok) => {
|
||||||
|
model_type_list.value = ok.data
|
||||||
|
})
|
||||||
|
providerValue.value = provider
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const list_base_model = (model_type: any) => {
|
||||||
|
form_data.value.model_name = ''
|
||||||
|
if (providerValue.value) {
|
||||||
|
ModelApi.listBaseModel(providerValue.value.provider, model_type, base_model_loading).then(
|
||||||
|
(ok) => {
|
||||||
|
base_model_list.value = ok.data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const close = () => {
|
||||||
|
base_form_data.value = { name: '', model_type: '', model_name: '' }
|
||||||
|
credential_form_data.value = {}
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
const submit = () => {
|
||||||
|
dynamicsFormRef.value?.validate().then(() => {
|
||||||
|
if (providerValue.value) {
|
||||||
|
ModelApi.createModel(
|
||||||
|
{
|
||||||
|
...base_form_data.value,
|
||||||
|
credential: credential_form_data.value,
|
||||||
|
provider: providerValue.value.provider
|
||||||
|
},
|
||||||
|
loading
|
||||||
|
).then((ok) => {
|
||||||
|
MsgSuccess('创建模型成功')
|
||||||
|
emit('submit')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const toSelectProvider = () => {
|
||||||
|
close()
|
||||||
|
emit('change')
|
||||||
|
}
|
||||||
|
defineExpose({ open, close })
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.select-provider {
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(100, 106, 115, 1);
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: rgba(51, 112, 255, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.active-breadcrumb {
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(31, 35, 41, 1);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
ui/src/views/template/component/Model.vue
Normal file
50
ui/src/views/template/component/Model.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<card-box
|
||||||
|
style="
|
||||||
|
--app-card-box-description-height: 100%;
|
||||||
|
--card-min-height: 153px;
|
||||||
|
--card-min-width: 20px;
|
||||||
|
width: 100%;
|
||||||
|
"
|
||||||
|
:title="model.name"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<AppAvatar
|
||||||
|
class="mr-12"
|
||||||
|
shape="square"
|
||||||
|
style="--el-avatar-bg-color: rgba(255, 255, 255, 0)"
|
||||||
|
:size="32"
|
||||||
|
>
|
||||||
|
<span style="height: 24px; width: 24px" :innerHTML="icon"></span
|
||||||
|
></AppAvatar>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<el-descriptions :column="1" label-align="center">
|
||||||
|
<el-descriptions-item label="模型类型" label-class-name="ellipsis-1">
|
||||||
|
<span class="ellipsis-1"> {{ model.model_type }}</span></el-descriptions-item
|
||||||
|
>
|
||||||
|
<el-descriptions-item label="模型名称" label-class-name="ellipsis-1">
|
||||||
|
<span class="ellipsis-1">{{ model.model_name }}</span></el-descriptions-item
|
||||||
|
>
|
||||||
|
</el-descriptions>
|
||||||
|
</template>
|
||||||
|
</card-box>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Provider, Model } from '@/api/type/model'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
const props = defineProps<{
|
||||||
|
model: Model
|
||||||
|
provider_list: Array<Provider>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const icon = computed(() => {
|
||||||
|
return props.provider_list.find((p) => p.provider === props.model.provider)?.icon
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-descriptions__cell) {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
14
ui/src/views/template/component/RadioCard.vue
Normal file
14
ui/src/views/template/component/RadioCard.vue
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<template>
|
||||||
|
<el-row :gutter="12">
|
||||||
|
<el-col :span="12" v-for="data in data_list">
|
||||||
|
<el-card shadow="hover" @click="emit('update:modelValue', data)">
|
||||||
|
<slot v-bind="data"></slot>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{ modelValue?: any; data_list: Array<any> }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
70
ui/src/views/template/component/SelectProvider.vue
Normal file
70
ui/src/views/template/component/SelectProvider.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:before-close="close"
|
||||||
|
>
|
||||||
|
<template #header="{ close, titleId, titleClass }">
|
||||||
|
<el-breadcrumb separator=">">
|
||||||
|
<el-breadcrumb-item>
|
||||||
|
<span class="active-breadcrumb">选择供应商</span>
|
||||||
|
</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</template>
|
||||||
|
<RadioCard class="mb-8" :data_list="list_provider" @update:model-value="go_create"
|
||||||
|
><template #default="scope">
|
||||||
|
<div class="center">
|
||||||
|
<span
|
||||||
|
:innerHTML="scope.icon"
|
||||||
|
alt=""
|
||||||
|
style="display: inline-block; height: 24px; width: 24px"
|
||||||
|
class="mr-8"
|
||||||
|
/>
|
||||||
|
<span>{{ scope.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template></RadioCard
|
||||||
|
>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import ModelApi from '@/api/model'
|
||||||
|
import type { Provider } from '@/api/type/model'
|
||||||
|
import RadioCard from '@/views/template/component/RadioCard.vue'
|
||||||
|
const loading = ref<boolean>(false)
|
||||||
|
const dialogVisible = ref<boolean>(false)
|
||||||
|
const list_provider = ref<Array<Provider>>([])
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
ModelApi.getProvider(loading).then((ok) => {
|
||||||
|
list_provider.value = ok.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
const emit = defineEmits(['change'])
|
||||||
|
const go_create = (provider: Provider) => {
|
||||||
|
close()
|
||||||
|
emit('change', provider)
|
||||||
|
}
|
||||||
|
defineExpose({ open, close })
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.active-breadcrumb {
|
||||||
|
font-size: 16px;
|
||||||
|
color: rgba(31, 35, 41, 1);
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -27,56 +27,80 @@
|
|||||||
</template>
|
</template>
|
||||||
</common-list>
|
</common-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="template-manage__right p-24">
|
<div class="template-manage__right p-24" v-loading="list_model_loading">
|
||||||
<h4>全部模型</h4>
|
<h3 v-if="active_provider">{{ active_provider.name }}</h3>
|
||||||
<card-box :title="model.name" v-for="model in model_list">
|
<h3 v-else>全部模型</h3>
|
||||||
<template #icon>
|
<div class="flex-between mt-8">
|
||||||
<AppAvatar
|
<el-button type="primary" @click="openCreateModel(active_provider)">创建模型</el-button>
|
||||||
class="mr-12"
|
<el-input
|
||||||
shape="square"
|
v-model="model_search_form.name"
|
||||||
style="--el-avatar-bg-color: rgba(255, 255, 255, 0)"
|
@change="list_model"
|
||||||
:size="32"
|
placeholder="按 名称 搜索"
|
||||||
>
|
prefix-icon="Search"
|
||||||
<span style="height: 24px; width: 24px" :innerHTML="get_model_icon(model)"></span
|
class="w-240"
|
||||||
></AppAvatar>
|
/>
|
||||||
</template>
|
</div>
|
||||||
<template #description>
|
|
||||||
{{ model.model_type }}
|
<el-row :gutter="15" v-for="row in model_split_list">
|
||||||
{{ model.model_name }}
|
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" class="mt-8" v-for="model in row">
|
||||||
</template>
|
<ModelVue :model="model" :provider_list="provider_list"> </ModelVue>
|
||||||
</card-box>
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<CreateModel
|
||||||
|
ref="createModelRef"
|
||||||
|
@submit="list_model"
|
||||||
|
@change="openCreateModel()"
|
||||||
|
></CreateModel>
|
||||||
|
<SelectProvider ref="selectProviderRef" @change="openCreateModel($event)"></SelectProvider>
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, ref, reactive, watch } from 'vue'
|
import { onMounted, ref, computed, watch } from 'vue'
|
||||||
import ModelApi from '@/api/model'
|
import ModelApi from '@/api/model'
|
||||||
import type { Provider, Model } from '@/api/type/model'
|
import type { Provider, Model } from '@/api/type/model'
|
||||||
import AppIcon from '@/components/icons/AppIcon.vue'
|
import AppIcon from '@/components/icons/AppIcon.vue'
|
||||||
|
import ModelVue from '@/views/template/component/Model.vue'
|
||||||
|
import { splitArray } from '@/utils/common'
|
||||||
|
import CreateModel from '@/views/template/component/CreateModel.vue'
|
||||||
|
import SelectProvider from '@/views/template/component/SelectProvider.vue'
|
||||||
|
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
|
|
||||||
const active_provider = ref<Provider>()
|
const active_provider = ref<Provider>()
|
||||||
|
const model_search_form = ref<{ name: string }>({ name: '' })
|
||||||
|
const list_model_loading = ref<boolean>(false)
|
||||||
const provider_list = ref<Array<Provider>>([])
|
const provider_list = ref<Array<Provider>>([])
|
||||||
const get_model_icon = (model: Model) => {
|
|
||||||
return provider_list.value.find((p) => p.provider === model.provider)?.icon
|
|
||||||
}
|
|
||||||
const model_list = ref<Array<Model>>([])
|
const model_list = ref<Array<Model>>([])
|
||||||
watch(
|
|
||||||
active_provider,
|
const model_split_list = computed(() => {
|
||||||
() => {
|
return splitArray(model_list.value, 2)
|
||||||
ModelApi.getModel(
|
})
|
||||||
active_provider.value ? { provider: active_provider.value.provider } : {}
|
const createModelRef = ref<InstanceType<typeof CreateModel>>()
|
||||||
).then((ok) => {
|
const selectProviderRef = ref<InstanceType<typeof SelectProvider>>()
|
||||||
|
|
||||||
|
const openCreateModel = (provider?: Provider) => {
|
||||||
|
if (provider) {
|
||||||
|
createModelRef.value?.open(provider)
|
||||||
|
} else {
|
||||||
|
selectProviderRef.value?.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const list_model = () => {
|
||||||
|
const params = active_provider.value ? { provider: active_provider.value.provider } : {}
|
||||||
|
ModelApi.getModel({ ...model_search_form.value, ...params }, list_model_loading).then((ok) => {
|
||||||
model_list.value = ok.data
|
model_list.value = ok.data
|
||||||
})
|
})
|
||||||
},
|
|
||||||
{
|
|
||||||
immediate: true
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
watch(active_provider, list_model, {
|
||||||
|
immediate: true
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
ModelApi.getProvider(loading).then((ok) => {
|
ModelApi.getProvider(loading).then((ok) => {
|
||||||
provider_list.value = [...ok.data]
|
provider_list.value = [...ok.data]
|
||||||
@ -91,5 +115,9 @@ onMounted(() => {
|
|||||||
width: var(--setting-left-width);
|
width: var(--setting-left-width);
|
||||||
min-width: var(--setting-left-width);
|
min-width: var(--setting-left-width);
|
||||||
}
|
}
|
||||||
|
&__right {
|
||||||
|
width: 100%;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user