This commit is contained in:
dataeaseShu
2025-07-04 15:26:10 +08:00
18 changed files with 446 additions and 274 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
import { request } from '@/utils/request'
export const modelApi = {
pager: (pageNumber: number, pageSize: number) =>
request.get(`/system/aimodel/pager/${pageNumber}/${pageSize}`),
queryAll: (keyword?: string) =>
request.get('/system/aimodel', { params: keyword ? { keyword } : {} }),
add: (data: any) => request.post('/system/aimodel', data),
edit: (data: any) => request.put('/system/aimodel', data),
delete: (id: number) => request.delete(`/system/aimodel/${id}`),
+1 -1
View File
@@ -4,7 +4,7 @@ export interface SelectOption {
}
export const modelTypeOptions: SelectOption[] = [
{ label: 'OpenAI', value: 0 },
{ label: '大语言模型', value: 0 },
/* { label: 'Anthropic', value: 1 },
{ label: 'Baidu', value: 2 },
{ label: 'iFLYTEK', value: 3 },
+54
View File
@@ -0,0 +1,54 @@
import icon_alybl_colorful from '@/assets/model/icon_alybl_colorful.png'
import icon_bdyzn_colorful from '@/assets/model/icon_bdyzn_colorful.png'
import icon_deepseek_colorful from '@/assets/model/icon_deepseek_colorful.png'
type ModelConfig = Record<number, { api_domain: string; model_options: string[] }>
export const supplierList: Array<{
id: number
name: string
icon: any
model_config: ModelConfig
}> = [
{
id: 1,
name: '阿里云百炼',
icon: icon_alybl_colorful,
model_config: {
0: {
api_domain: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
model_options: ['qwen-plus'],
},
},
},
{
id: 2,
name: '千帆大模型I',
icon: icon_bdyzn_colorful,
model_config: {
0: {
api_domain: 'https://qianfan.baidubce.com/v2/',
model_options: ['ernie-x1-turbo-32k'],
},
},
},
{
id: 3,
name: 'DeepSeek',
icon: icon_deepseek_colorful,
model_config: {
0: {
api_domain: 'https://api.deepseek.com',
model_options: ['deepseek-chat'],
},
},
},
]
export const base_model_options = (supplier_id: number, model_type?: number) => {
const supplier = get_supplier(supplier_id)
return supplier?.model_config[model_type || 0].model_options
}
export const get_supplier = (supplier_id: number) => {
return supplierList.find((item: any) => item.id === supplier_id)
}
+13 -6
View File
@@ -1,8 +1,8 @@
<script lang="ts" setup>
import azure from '@/assets/model/icon_Azure_OpenAI_colorful.png'
import delIcon from '@/assets/svg/icon_delete.svg'
import edit from '@/assets/svg/icon_edit_outlined.svg'
import { get_supplier } from '@/entity/supplier'
import { computed } from 'vue'
const props = withDefaults(
defineProps<{
name: string
@@ -10,6 +10,7 @@ const props = withDefaults(
baseModle: string
id?: string
isDefault?: boolean
supplier?: number
}>(),
{
name: '-',
@@ -17,9 +18,15 @@ const props = withDefaults(
baseModle: '-',
id: '-',
isDefault: false,
supplier: 0,
}
)
const current_supplier = computed(() => {
if (!props.supplier) {
return null
}
return get_supplier(props.supplier)
})
const emits = defineEmits(['edit', 'del'])
const handleEdit = () => {
@@ -27,16 +34,16 @@ const handleEdit = () => {
}
const handleDel = () => {
emits('del', props.id)
emits('del', { id: props.id, name: props.name, default_model: props.isDefault })
}
</script>
<template>
<div class="card">
<div class="name-icon">
<img :src="azure" width="32px" height="32px" />
<img :src="current_supplier?.icon" width="32px" height="32px" />
<span class="name">{{ name }}</span>
<span class="default">{{ $t('model.default_model') }}</span>
<span v-if="isDefault" class="default">{{ $t('model.default_model') }}</span>
</div>
<div class="type-value">
<span class="type">{{ $t('model.model_type') }}</span>
+90 -79
View File
@@ -5,7 +5,6 @@ import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlin
import icon_admin_outlined from '@/assets/svg/icon_admin_outlined.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
import icon_Azure_OpenAI_colorful from '@/assets/model/icon_Azure_OpenAI_colorful.png'
import icon_done_outlined from '@/assets/svg/icon_done_outlined.svg'
import icon_close_outlined from '@/assets/svg/operate/ope-close.svg'
import ModelList from './ModelList.vue'
@@ -15,12 +14,15 @@ import { modelApi } from '@/api/system'
import Card from './Card.vue'
import { getModelTypeName } from '@/entity/CommonEntity.ts'
import { useI18n } from 'vue-i18n'
import { get_supplier } from '@/entity/supplier'
interface Model {
name: string
type: string
baseModle: string
model_type: string
base_model: string
id?: string
default_model: boolean
supplier: number
}
const { t } = useI18n()
@@ -32,34 +34,18 @@ const activeStep = ref(0)
const activeName = ref('')
const modelFormRef = ref()
const state = reactive({
reactive({
form: {
id: '',
name: '',
type: 0,
model_type: 0,
api_key: '',
endpoint: '',
max_context_window: 0,
temperature: 0,
status: false,
description: '',
},
pageInfo: {
currentPage: 1,
pageSize: 20,
total: 0,
api_domain: '',
},
selectedIds: [],
})
const modelList = shallowRef([] as Model[])
const defaultModelList = shallowRef([
{
img: icon_Azure_OpenAI_colorful,
name: '千帆大模型-chinese',
},
] as (Model & { img: string })[])
const currentDefaultModel = ref('')
const modelListWithSearch = computed(() => {
if (!keywords.value) return modelList.value
return modelList.value.filter((ele) =>
@@ -68,32 +54,37 @@ const modelListWithSearch = computed(() => {
})
const beforeClose = () => {
modelConfigvVisible.value = false
ElMessage.success(t('model.set_successfully'))
// ElMessageBox.confirm(t('model.system_default_model', { msg: msg}), {
// confirmButtonType: 'primary',
// tip: t('model.operate_with_caution'),
// confirmButtonText: t('datasource.confirm'),
// cancelButtonText: t('common.cancel'),
// customClass: 'confirm-no_icon',
// autofocus: false,
// })
// ElMessageBox.confirm(t('model.system_default_model', { msg: msg}), {
// confirmButtonType: 'danger',
// confirmButtonText: t('dashboard.delete'),
// cancelButtonText: t('common.cancel'),
// customClass: 'confirm-no_icon',
// autofocus: false,
// })
}
const defaultModelListWithSearch = computed(() => {
if (!defaultModelKeywords.value) return defaultModelList.value
return defaultModelList.value.filter((ele) =>
ele.name.toLowerCase().includes(defaultModelKeywords.value.toLowerCase())
)
let tempModelList = modelList.value
if (defaultModelKeywords.value) {
tempModelList = tempModelList.filter((ele) =>
ele.name.toLowerCase().includes(defaultModelKeywords.value.toLowerCase())
)
}
return tempModelList.map((item: any) => {
item['supplier_item'] = get_supplier(item.supplier)
return item
})
})
const handleDefaultModelChange = (item: any) => {
currentDefaultModel.value = item.name
ElMessageBox.confirm(`是否设置 ${item.name} 为系统默认模型?`, {
confirmButtonType: 'primary',
tip: '系统默认模型被替换后,智能问数的结果将会受到影响,请谨慎操作。',
confirmButtonText: t('datasource.confirm'),
cancelButtonText: t('common.cancel'),
customClass: 'confirm-no_icon',
autofocus: false,
callback: (val: string) => {
if (val === 'confirm') {
modelList.value.forEach((ele: any) => {
ele.default_model = ele.id === item.id
})
ElMessage.success('设置成功')
}
},
})
}
const formatKeywords = (item: string) => {
@@ -103,47 +94,66 @@ const formatKeywords = (item: string) => {
`<span class="isSearch">${defaultModelKeywords.value}</span>`
)
}
const handleAddModel = () => {
activeStep.value = 0
modelConfigvVisible.value = true
}
const handleEditModel = (id: any) => {
activeStep.value = 1
modelApi.query(id).then((res: any) => {
modelConfigvVisible.value = true
nextTick(() => {
modelFormRef.value.initForm({ ...res, temperature: res.temperature * 100 })
modelFormRef.value.initForm({ ...res })
})
})
}
const deleteHandler = (id: any) => {
ElMessageBox.confirm('Are you sure to delete?', 'Warning', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
modelApi.delete(id).then(() => {
ElMessage({
type: 'success',
message: 'Delete completed',
const deleteHandler = (item: any) => {
if (item.default_model) {
ElMessageBox.confirm(`无法删除模型:${item.name}?`, {
confirmButtonType: 'primary',
tip: '该模型为系统默认模型,请先设置其他模型为系统默认模型,再删除此模型。',
showConfirmButton: false,
confirmButtonText: '确定',
cancelButtonText: '知道了',
customClass: 'confirm-no_icon',
autofocus: false,
callback: (val: string) => {
console.log(val)
},
})
return
}
ElMessageBox.confirm(`是否删除模型:${item.name}?`, {
confirmButtonType: 'danger',
confirmButtonText: '删除',
cancelButtonText: '取消',
customClass: 'confirm-no_icon',
autofocus: false,
callback: (value: string) => {
if (value === 'confirm') {
modelApi.delete(item.id).then(() => {
ElMessage({
type: 'success',
message: '删除成功',
})
search()
})
search()
})
})
.catch(() => {
ElMessage({
type: 'info',
message: 'Delete canceled',
})
})
}
},
})
}
const clickModel = (ele: any) => {
activeStep.value = 1
activeName.value = ele.name
supplierChang(ele)
}
const clickModelSide = (ele: any) => {
const supplierChang = (ele: any) => {
activeName.value = ele.name
nextTick(() => {
modelFormRef.value.supplierChang({ ...ele })
})
}
const cancel = () => {
@@ -155,13 +165,12 @@ const preStep = () => {
}
const saveModel = () => {
modelFormRef.value.submit()
modelFormRef.value.submitModle()
}
const search = () => {
modelApi.pager(state.pageInfo.currentPage, state.pageInfo.pageSize).then((res: any) => {
modelList.value = res.items
state.pageInfo.total = res.total
modelApi.queryAll().then((res: any) => {
modelList.value = res
})
}
search()
@@ -234,10 +243,10 @@ const submit = (item: any) => {
v-for="ele in defaultModelListWithSearch"
:key="ele.name"
class="popover-item"
:class="currentDefaultModel === ele.name && 'isActive'"
:class="ele.default_model && 'isActive'"
@click="handleDefaultModelChange(ele)"
>
<img :src="ele.img" width="24px" height="24px" />
<img :src="ele.supplier_item.icon" width="24px" height="24px" />
<div class="model-name" v-html="formatKeywords(ele.name)"></div>
<el-icon size="16" class="done">
<icon_done_outlined></icon_done_outlined>
@@ -250,7 +259,7 @@ const submit = (item: any) => {
</div>
</el-popover>
<el-button type="primary">
<el-button type="primary" @click="handleAddModel">
<template #icon>
<icon_add_outlined></icon_add_outlined>
</template>
@@ -265,16 +274,18 @@ const submit = (item: any) => {
/>
<div v-else class="card-content">
<Card
<card
v-for="ele in modelListWithSearch"
:id="ele.id"
:key="ele.id"
:name="ele.name"
:modle-type="getModelTypeName(ele.type)"
:base-modle="ele.baseModle"
:supplier="ele.supplier"
:modle-type="getModelTypeName(ele['model_type'])"
:base-modle="ele['base_model']"
:is-default="ele['default_model']"
@edit="handleEditModel"
@del="deleteHandler"
></Card>
></card>
</div>
<el-drawer
v-model="modelConfigvVisible"
@@ -305,7 +316,7 @@ const submit = (item: any) => {
<ModelListSide
v-if="activeStep === 1"
:active-name="activeName"
@click-model="clickModelSide"
@click-model="supplierChang"
></ModelListSide>
<ModelForm
v-if="activeStep === 1"
+52 -42
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, reactive, onMounted, nextTick } from 'vue'
import { ref, reactive, onMounted, nextTick, computed } from 'vue'
import arrow_down from '@/assets/svg/arrow-down.svg'
import dashboard_info from '@/assets/svg/dashboard-info.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
@@ -7,6 +7,7 @@ import icon_delete from '@/assets/svg/icon_delete.svg'
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import ParamsForm from './ParamsForm.vue'
import { modelTypeOptions } from '@/entity/CommonEntity.ts'
import { base_model_options } from '@/entity/supplier'
import { useI18n } from 'vue-i18n'
withDefaults(
@@ -18,36 +19,37 @@ withDefaults(
}
)
interface Options {
label: string
value: string
}
interface Form {
name: string
id: string
interface ParamsFormData {
key?: string
val?: string
name?: string
id?: string
}
const { t } = useI18n()
const modelForm = reactive({
id: '',
supplier: 0,
name: '',
type: 0,
model_type: 0,
base_model: '',
api_key: '',
endpoint: '',
max_context_window: 0,
temperature: 0,
status: false,
description: '',
api_domain: '',
config_list: [],
})
let isCreate = false
const modelRef = ref()
const paramsFormRef = ref()
const advancedSetting = ref([] as Form[])
const modelList = ref([] as Options[])
const advancedSetting = ref([] as ParamsFormData[])
const paramsFormDrawer = ref(false)
const advancedSettingExpand = ref(false)
const configExpand = ref(false)
const modelList = computed(() => {
if (!modelForm.supplier) {
return []
}
return base_model_options(modelForm.supplier, modelForm.model_type)
})
const handleParamsEdite = (ele?: any) => {
isCreate = false
paramsFormDrawer.value = true
@@ -65,18 +67,18 @@ const handleParamsCreate = () => {
}
const handleParamsDel = (item: any) => {
advancedSetting.value = advancedSetting.value.filter((ele) => ele.name !== item.name)
advancedSetting.value = advancedSetting.value.filter((ele) => ele.key !== item.key)
}
const rules = {
type: [
model_type: [
{
required: true,
message: 'type',
trigger: 'change',
},
],
endpoint: [
api_domain: [
{
required: true,
message: t('datasource.please_enter') + t('common.empty') + t('model.api_domain_name'),
@@ -84,6 +86,7 @@ const rules = {
},
],
modelName: [{ required: true, message: t('model.the_basic_model_de'), trigger: 'blur' }],
base_model: [{ required: true, message: t('model.the_basic_model_de'), trigger: 'blur' }],
name: [{ required: true, message: t('model.the_basic_model'), trigger: 'blur' }],
api_key: [
{
@@ -123,11 +126,23 @@ const submit = (item: any) => {
const cancel = () => {
paramsFormDrawer.value = false
}
const initForm = (item: any) => {
const supplierChang = (supplier: any) => {
modelForm.supplier = supplier.id
const config = supplier.model_config[modelForm.model_type || 0]
modelForm.api_domain = config.api_domain
if (!modelForm.id) {
modelForm.name = supplier.name
}
}
const initForm = (item?: any) => {
modelForm.id = ''
modelRef.value.clearValidate()
Object.assign(modelForm, { ...item })
if (item) {
Object.assign(modelForm, { ...item })
if (item?.config_list?.length) {
advancedSetting.value = item.config_list
}
}
}
const emits = defineEmits(['submit'])
@@ -136,8 +151,7 @@ const submitModle = () => {
if (res) {
emits('submit', {
...modelForm,
temperature: modelForm.temperature / 100,
advancedSetting: [...advancedSetting.value],
config_list: [...advancedSetting.value],
})
}
})
@@ -146,6 +160,7 @@ const submitModle = () => {
defineExpose({
initForm,
submitModle,
supplierChang,
})
</script>
@@ -177,7 +192,7 @@ defineExpose({
/>
</el-form-item>
<el-form-item prop="type" :label="t('model.model_type')">
<el-select v-model="modelForm.type" style="width: 100%">
<el-select v-model="modelForm.model_type" style="width: 100%">
<el-option
v-for="item in modelTypeOptions"
:key="item.value"
@@ -192,7 +207,7 @@ defineExpose({
<span class="enter">{{ t('model.enter_to_add') }}</span>
</template>
<el-select
v-model="modelForm.type"
v-model="modelForm['base_model']"
style="width: 100%"
multiple
filterable
@@ -200,17 +215,12 @@ defineExpose({
default-first-option
:reserve-keyword="false"
>
<el-option
v-for="item in modelList"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-option v-for="item in modelList" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item prop="endpoint" :label="t('model.api_domain_name')">
<el-form-item prop="api_domain" :label="t('model.api_domain_name')">
<el-input
v-model="modelForm.endpoint"
v-model="modelForm.api_domain"
:placeholder="
$t('datasource.please_enter') + $t('common.empty') + $t('model.api_domain_name')
"
@@ -227,15 +237,15 @@ defineExpose({
</el-form>
<div
class="advance-setting"
:class="advancedSettingExpand && 'expand'"
@click="advancedSettingExpand = !advancedSettingExpand"
:class="configExpand && 'expand'"
@click="configExpand = !configExpand"
>
{{ t('model.advanced_settings') }}
<el-icon size="16">
<arrow_down></arrow_down>
</el-icon>
</div>
<div v-if="advancedSettingExpand" class="model-params">
<div v-if="configExpand" class="model-params">
{{ t('model.model_parameters') }}
<span class="add" @click="handleParamsCreate">
<el-icon size="16">
@@ -245,11 +255,11 @@ defineExpose({
</span>
</div>
<div v-if="advancedSettingExpand" class="params-table">
<div v-if="configExpand" class="params-table">
<el-table :data="advancedSetting" style="width: 100%">
<el-table-column prop="params" :label="t('model.parameters')" width="280" />
<el-table-column prop="key" :label="t('model.parameters')" width="280" />
<el-table-column prop="name" :label="t('model.display_name')" width="280" />
<el-table-column prop="value" :label="t('model.parameter_value')" />
<el-table-column prop="val" :label="t('model.parameter_value')" />
<el-table-column
fixed="right"
width="80"
+8 -24
View File
@@ -1,30 +1,14 @@
<script lang="ts" setup>
import { ref, shallowRef, computed } from 'vue'
import { ref, computed } from 'vue'
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
import icon_Azure_OpenAI_colorful from '@/assets/model/icon_Azure_OpenAI_colorful.png'
interface Model {
name: string
modleType: string
baseModle: string
img?: string
}
import { supplierList } from '@/entity/supplier'
const keywords = ref('')
const modelList = shallowRef([
{
img: icon_Azure_OpenAI_colorful,
name: '千帆大模型-chinese',
},
{
img: icon_Azure_OpenAI_colorful,
name: '千帆大模-chinese',
},
] as Model[])
const modelListWithSearch = computed(() => {
if (!keywords.value) return modelList.value
return modelList.value.filter((ele) =>
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
)
if (!keywords.value) return supplierList
return supplierList.filter((ele) => ele.name.toLowerCase().includes(keywords.value.toLowerCase()))
})
const emits = defineEmits(['clickModel'])
const handleModelClick = (item: any) => {
@@ -54,7 +38,7 @@ const handleModelClick = (item: any) => {
class="model"
@click="handleModelClick(ele)"
>
<img width="32px" height="32px" :src="ele.img" />
<img width="32px" height="32px" :src="ele.icon" />
<span class="name">{{ ele.name }}</span>
</div>
<EmptyBackground
@@ -85,7 +69,7 @@ const handleModelClick = (item: any) => {
display: flex;
justify-content: space-between;
height: calc(100% - 40px);
gap: 16px;
.model:nth-child(odd) {
margin-left: 0;
}
@@ -1,14 +1,9 @@
<script lang="ts" setup>
import { ref, shallowRef, computed } from 'vue'
import { ref, computed } from 'vue'
import icon_searchOutline_outlined from '@/assets/svg/icon_search-outline_outlined.svg'
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
import icon_Azure_OpenAI_colorful from '@/assets/model/icon_Azure_OpenAI_colorful.png'
interface Model {
name: string
modleType: string
baseModle: string
img?: string
}
import { supplierList } from '@/entity/supplier'
withDefaults(
defineProps<{
activeName: string
@@ -18,21 +13,10 @@ withDefaults(
}
)
const keywords = ref('')
const modelList = shallowRef([
{
img: icon_Azure_OpenAI_colorful,
name: '千帆大模型-chinese',
},
{
img: icon_Azure_OpenAI_colorful,
name: '千帆大模-chinese',
},
] as Model[])
const modelListWithSearch = computed(() => {
if (!keywords.value) return modelList.value
return modelList.value.filter((ele) =>
ele.name.toLowerCase().includes(keywords.value.toLowerCase())
)
if (!keywords.value) return supplierList
return supplierList.filter((ele) => ele.name.toLowerCase().includes(keywords.value.toLowerCase()))
})
const emits = defineEmits(['clickModel'])
const handleModelClick = (item: any) => {
@@ -62,7 +46,7 @@ const handleModelClick = (item: any) => {
:class="activeName === ele.name && 'isActive'"
@click="handleModelClick(ele)"
>
<img width="32px" height="32px" :src="ele.img" />
<img width="32px" height="32px" :src="ele.icon" />
<span class="name">{{ ele.name }}</span>
</div>
<EmptyBackground
@@ -6,9 +6,8 @@ const { t } = useI18n()
const paramsRef = ref()
const paramsForm = reactive({
name: '',
params: '',
value: null,
type: 0,
key: '',
val: '',
})
const rules = {
@@ -19,14 +18,14 @@ const rules = {
trigger: 'blur',
},
],
params: [
key: [
{
required: true,
message: t('datasource.please_enter') + t('common.empty') + t('model.parameters'),
trigger: 'blur',
},
],
value: [
val: [
{
required: true,
message: t('datasource.please_enter') + t('common.empty') + t('model.parameter_value'),
@@ -66,9 +65,9 @@ defineExpose({
:model="paramsForm"
style="width: 100%"
>
<el-form-item prop="params" :label="$t('model.parameters')">
<el-form-item prop="key" :label="$t('model.parameters')">
<el-input
v-model="paramsForm.params"
v-model="paramsForm.key"
:placeholder="$t('datasource.please_enter') + $t('common.empty') + $t('model.parameters')"
/>
</el-form-item>
@@ -80,9 +79,9 @@ defineExpose({
"
/>
</el-form-item>
<el-form-item prop="value" :label="$t('model.parameter_value')">
<el-form-item prop="val" :label="$t('model.parameter_value')">
<el-input
v-model="paramsForm.value"
v-model="paramsForm.val"
:placeholder="
$t('datasource.please_enter') + $t('common.empty') + $t('model.parameter_value')
"
+2 -2
View File
@@ -265,10 +265,10 @@ const onFormClose = () => {
}
const search = () => {
modelApi.pager(state.pageInfo.currentPage, state.pageInfo.pageSize).then((res: any) => {
/* modelApi.pager(state.pageInfo.currentPage, state.pageInfo.pageSize).then((res: any) => {
state.tableData = res.items
state.pageInfo.total = res.total
})
}) */
}
const addTerm = () => {
const param: any = {