UnisKB/ui/src/views/template/component/ModelCard.vue

270 lines
7.7 KiB
Vue
Raw Normal View History

2023-11-28 10:26:31 +00:00
<template>
2023-12-04 07:23:09 +00:00
<card-box :title="model.name" shadow="hover" class="model-card">
2024-03-22 11:27:00 +00:00
<template #header>
2024-07-19 08:27:12 +00:00
<div class="flex">
2024-03-22 11:27:00 +00:00
<span style="height: 32px; width: 32px" :innerHTML="icon" class="mr-12"></span>
<div style="width: calc(100% - 32px - 4px - var(--app-base-px))">
<div class="flex" style="height: 22px">
2024-07-19 08:27:12 +00:00
<auto-tooltip :content="model.name" style="max-width: 40%">
{{ model.name }}
</auto-tooltip>
<span v-if="currentModel.status === 'ERROR'">
<el-tooltip effect="dark" :content="errMessage" placement="top">
<el-icon class="danger ml-4" size="18"><Warning /></el-icon>
</el-tooltip>
</span>
<span v-if="currentModel.status === 'PAUSE_DOWNLOAD'">
<el-tooltip
effect="dark"
:content="`基础模型: ${props.model.model_name} 下载失败`"
placement="top"
>
2024-07-19 08:27:12 +00:00
<el-icon class="danger ml-4" size="18"><Warning /></el-icon>
</el-tooltip>
</span>
</div>
2024-07-19 06:58:08 +00:00
<div class="mt-4">
<el-tag v-if="model.permission_type === 'PRIVATE'" type="danger" class="danger-tag"
>私有</el-tag
>
2024-07-22 10:38:53 +00:00
<el-tag v-else type="info" class="info-tag">公用</el-tag>
2024-07-19 06:58:08 +00:00
</div>
</div>
2024-03-22 11:27:00 +00:00
</div>
2023-11-28 10:26:31 +00:00
</template>
2024-03-22 11:27:00 +00:00
<div class="mt-16">
2023-12-04 07:23:09 +00:00
<ul>
<li class="flex mt-16">
<el-text type="info">模型类型</el-text>
2024-07-24 03:19:30 +00:00
<span class="ellipsis ml-16">
{{ modelType[model.model_type as keyof typeof modelType] }}</span
>
2023-12-04 07:23:09 +00:00
</li>
<li class="flex mt-12">
2024-01-29 07:06:44 +00:00
<el-text type="info">基础模型</el-text>
2024-07-24 03:19:30 +00:00
<span class="ellipsis-1 ml-16" style="height: 20px; width: 70%">
{{ model.model_name }}</span
>
2023-12-04 07:23:09 +00:00
</li>
2024-10-09 13:44:06 +00:00
<li class="flex mt-12">
<el-text type="info">创建者</el-text>
<span class="ellipsis-1 ml-16" style="height: 20px; width: 70%">
{{ model.username }}</span
>
</li>
2023-12-04 07:23:09 +00:00
</ul>
</div>
2024-03-22 10:52:49 +00:00
<!-- progress -->
2024-03-22 11:58:18 +00:00
<div class="progress-mask" v-if="currentModel.status === 'DOWNLOAD'">
2024-07-19 06:58:08 +00:00
<DownloadLoading class="percentage" />
<div class="percentage-label flex-center">
正在下载中 <span class="dotting"></span>
<el-button
link
type="primary"
class="ml-16"
:disabled="!is_permisstion"
@click.stop="cancelDownload"
2024-07-19 06:58:08 +00:00
>取消下载</el-button
>
</div>
2024-03-22 10:52:49 +00:00
</div>
2023-12-04 07:23:09 +00:00
2023-12-04 08:24:41 +00:00
<template #mouseEnter>
2024-07-23 03:35:50 +00:00
<div class="operation-button">
2023-12-04 08:24:41 +00:00
<el-tooltip effect="dark" content="修改" placement="top">
<el-button text :disabled="!is_permisstion" @click.stop="openEditModel">
2024-03-22 10:52:49 +00:00
<el-icon>
2024-07-19 08:13:44 +00:00
<component
:is="
currentModel.status === 'ERROR' || currentModel.status === 'PAUSE_DOWNLOAD'
? 'RefreshRight'
: 'EditPen'
"
/>
2024-03-22 10:52:49 +00:00
</el-icon>
2023-12-04 08:24:41 +00:00
</el-button>
</el-tooltip>
<el-dropdown trigger="click">
<el-button text @click.stop>
<el-icon><MoreFilled /></el-icon>
2023-12-04 08:24:41 +00:00
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-if="currentModel.model_type === 'TTS' || currentModel.model_type === 'LLM'"
:disabled="!is_permisstion"
icon="Setting" @click.stop="openParamSetting"
>
模型参数设置
</el-dropdown-item>
<el-dropdown-item icon="Delete" :disabled="!is_permisstion" text @click.stop="deleteModel">
删除
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
2023-12-04 08:24:41 +00:00
</div>
</template>
2024-08-15 09:17:25 +00:00
<EditModel ref="editModelRef" @submit="emit('change')"></EditModel>
<ParamSettingDialog ref="paramSettingRef" :model="model"/>
2023-11-28 10:26:31 +00:00
</card-box>
</template>
<script setup lang="ts">
import type { Provider, Model } from '@/api/type/model'
2023-12-01 09:30:06 +00:00
import ModelApi from '@/api/model'
2024-03-22 09:56:56 +00:00
import { computed, ref, onMounted, onBeforeUnmount } from 'vue'
2023-12-01 09:30:06 +00:00
import EditModel from '@/views/template/component/EditModel.vue'
2024-07-19 06:58:08 +00:00
import DownloadLoading from '@/components/loading/DownloadLoading.vue'
2024-03-25 08:33:30 +00:00
import { MsgConfirm } from '@/utils/message'
2024-07-23 10:43:07 +00:00
import { modelType } from '@/enums/model'
import useStore from '@/stores'
import ParamSettingDialog from './ParamSettingDialog.vue'
2023-11-28 10:26:31 +00:00
const props = defineProps<{
model: Model
provider_list: Array<Provider>
2024-03-25 08:33:30 +00:00
updateModelById: (model_id: string, model: Model) => void
2023-11-28 10:26:31 +00:00
}>()
const { user } = useStore()
2024-03-22 09:56:56 +00:00
const downModel = ref<Model>()
const is_permisstion = computed(() => {
return user.userInfo?.id == props.model.user_id
})
2024-03-22 11:58:18 +00:00
const currentModel = computed(() => {
2024-03-22 09:56:56 +00:00
if (downModel.value) {
2024-03-22 11:58:18 +00:00
return downModel.value
} else {
return props.model
}
})
const errMessage = computed(() => {
if (currentModel.value.meta && currentModel.value.meta.message) {
if (currentModel.value.meta.message === 'pull model manifest: file does not exist') {
return `${currentModel.value.model_name} 模型在Ollama不存在`
}
return currentModel.value.meta.message
}
2024-03-25 08:33:30 +00:00
return ''
2024-03-22 11:58:18 +00:00
})
2024-03-25 08:33:30 +00:00
const emit = defineEmits(['change', 'update:model'])
2024-08-15 09:17:25 +00:00
const editModelRef = ref<InstanceType<typeof EditModel>>()
2024-03-22 09:56:56 +00:00
let interval: any
2023-12-01 09:30:06 +00:00
const deleteModel = () => {
MsgConfirm(`删除模型 `, `是否删除模型:${props.model.name} ?`, {
confirmButtonText: '删除',
confirmButtonClass: 'danger'
})
.then(() => {
ModelApi.deleteModel(props.model.id).then(() => {
emit('change')
})
})
.catch(() => {})
}
2024-07-19 06:58:08 +00:00
2024-07-19 08:13:44 +00:00
const cancelDownload = () => {
ModelApi.pauseDownload(props.model.id).then(() => {
downModel.value = undefined
emit('change')
})
}
2023-12-01 09:30:06 +00:00
const openEditModel = () => {
const provider = props.provider_list.find((p) => p.provider === props.model.provider)
if (provider) {
2024-08-15 09:17:25 +00:00
editModelRef.value?.open(provider, props.model)
2023-12-01 09:30:06 +00:00
}
}
2023-11-28 10:26:31 +00:00
const icon = computed(() => {
return props.provider_list.find((p) => p.provider === props.model.provider)?.icon
})
2024-03-22 09:56:56 +00:00
/**
* 初始化轮询
*/
const initInterval = () => {
interval = setInterval(() => {
2024-03-22 11:58:18 +00:00
if (currentModel.value.status === 'DOWNLOAD') {
2024-03-22 09:56:56 +00:00
ModelApi.getModelMetaById(props.model.id).then((ok) => {
downModel.value = ok.data
})
2024-03-25 08:33:30 +00:00
} else {
if (downModel.value) {
props.updateModelById(props.model.id, downModel.value)
downModel.value = undefined
}
2024-03-22 09:56:56 +00:00
}
}, 6000)
}
2024-03-25 08:33:30 +00:00
2024-03-22 09:56:56 +00:00
/**
* 关闭轮询
*/
const closeInterval = () => {
if (interval) {
clearInterval(interval)
}
}
const paramSettingRef = ref<InstanceType<typeof ParamSettingDialog>>()
const openParamSetting = () => {
paramSettingRef.value?.open(props.model)
}
2024-03-22 09:56:56 +00:00
onMounted(() => {
initInterval()
})
onBeforeUnmount(() => {
// 清除定时任务
closeInterval()
})
2023-11-28 10:26:31 +00:00
</script>
<style lang="scss" scoped>
2023-12-04 07:23:09 +00:00
.model-card {
min-height: 135px;
2023-12-05 11:21:13 +00:00
min-width: auto;
2023-12-04 07:23:09 +00:00
.operation-button {
position: absolute;
right: 12px;
top: 18px;
height: auto;
.el-button + .el-button {
margin-left: 4px;
}
}
2024-03-22 10:52:49 +00:00
.progress-mask {
position: absolute;
top: 0;
left: 0;
2024-03-26 07:25:56 +00:00
background-color: rgba(255, 255, 255, 0.9);
2024-03-22 10:52:49 +00:00
width: 100%;
height: 100%;
2024-03-26 07:25:56 +00:00
z-index: 99;
2024-03-22 10:52:49 +00:00
text-align: center;
.percentage {
2024-07-19 06:58:08 +00:00
margin-top: 55px;
margin-bottom: 16px;
2024-03-22 10:52:49 +00:00
}
2024-07-19 06:58:08 +00:00
// .percentage-value {
// display: flex;
// font-size: 13px;
// align-items: center;
// color: var(--app-text-color-secondary);
// }
2024-03-22 10:52:49 +00:00
.percentage-label {
2024-07-19 06:58:08 +00:00
margin-top: 50px;
2024-03-26 07:25:56 +00:00
margin-left: 10px;
2024-07-19 06:58:08 +00:00
font-size: 13px;
color: var(--app-text-color-secondary);
2024-03-22 10:52:49 +00:00
}
}
2023-12-01 09:30:06 +00:00
}
2023-11-28 10:26:31 +00:00
</style>