mcwl-pc/app/components/publishModel/EditVersion.vue

448 lines
13 KiB
Vue

<script setup lang="ts">
import type { FormInst } from 'naive-ui'
import { commonApi } from '@/api/common'
import { cloneDeep } from 'lodash-es'
import { Asterisk, Trash } from 'lucide-vue-next'
import { computed, ref, watch } from 'vue'
// 可接受的文件类型
const props = defineProps({
modelValue: {
type: Object,
required: true,
},
})
const emit = defineEmits(['update:modelValue', 'nextStep', 'prevStep'])
const message = useMessage()
const acceptTypes
= '.safetensors,.ckpt,.pt,.bin,.pth,.zip,.json,.flow,.lightflow,.yaml,.yml,.onnx,.gguf,.sft'
const acceptTypesList = [
'safetensors',
'ckpt',
'pt',
'bin',
'pth',
'zip',
'json',
'flow',
'lightflow',
'yaml',
'yml',
'onnx',
'gguf',
'sft',
]
const modelVersionItem = {
objectKey: null,
isEncrypt: 0, // 0不加密
delFlag: '0', // 0代表存在 2代表删除
versionName: '', // 版本名称
modelVersionType: null, // 基础模型
versionDescription: '', // 版本描述
filePath: '', // 文件路径
fileName: '', //
sampleImagePaths: [], // 第三部的图片路径最多20张,切割
triggerWords: '', // 触发词
isPublic: null, // 权限是否公
isOnlineUse: 1, // 在线使用
allowFusion: 1, // 是否允许融合
isFree: 0, // 0免费
allowDownloadImage: 1, // 允许下载生图
allowSoftwareUse: 1, // 允许在软件旗下使用
allowCommercialUse: 1, // 是否允许商用
allowUsage: 1, // 允许模型转售或者融合出售
isExclusiveModel: 1, // 是否为独家模型这个字段
hideImageGenInfo: 0, // 隐藏图片生成信息
id: null,
}
defineExpose({
addVersion,
})
const loading = ref(false)
const localForm = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
},
})
watch(
() => localForm.value,
(newVal) => {
emit('update:modelValue', newVal)
},
{ immediate: true, deep: true },
)
const formRefs = ref<(FormInst | null)[]>([])
function setFormRef(el: FormInst | null, index: number) {
if (el) {
formRefs.value[index] = el
}
}
const rules = {
versionName: {
required: true,
message: '',
trigger: 'blur',
},
modelVersionType: {
required: true,
message: '',
trigger: 'blur',
},
}
function addVersion() {
const param = cloneDeep(modelVersionItem)
localForm.value.modelVersionList.unshift(param)
}
const originalBtnList = ref([
{
label: '免费',
value: 1,
},
{
label: '会员下载',
value: 0,
},
])
async function nextStep() {
for (let i = 0; i < localForm.value.modelVersionList.length; i++) {
if (
localForm.value.modelVersionList[i].delFlag === '0'
&& localForm.value.modelVersionList[i].fileName === ''
) {
return message.error('请上传文件')
}
const regex = /[\u4E00-\u9FA5]/ // 匹配汉字的正则表达式
if (
localForm.value.modelVersionList[i].delFlag === '0'
&& !regex.test(localForm.value.modelVersionList[i].versionDescription)
) {
return message.error('请用中文填写版本介绍')
}
}
try {
const promises = formRefs.value
.filter((form): form is FormInst => form !== null)
.map(form => form.validate())
await Promise.all(promises)
emit('nextStep')
}
catch (errors) {
console.error('部分表单验证失败:', errors)
}
}
// 获取上文件的信息
const uploadRef = ref<InstanceType<typeof FileUpload> | null>(null)
// const getFileInfo = () => {
// if (uploadRef.value[0].validRequired()) {
// const fileInfo = uploadRef.value[0].getUploadInfo()
// console.log(fileInfo)
// }
// }
function handleUploadSuccess(fileInfo: {
objectKey: string
objectUrl: string
fileName: string
currentFileSize: number
hashCode: string
}) {
localForm.value.modelVersionList[uploadFileIndex.value].filePath = fileInfo.objectUrl
localForm.value.modelVersionList[uploadFileIndex.value].objectKey = fileInfo.objectKey
localForm.value.modelVersionList[uploadFileIndex.value].fileName = fileInfo.fileName
localForm.value.modelVersionList[uploadFileIndex.value].fileSize = fileInfo.currentFileSize
localForm.value.modelVersionList[uploadFileIndex.value].fileHash = fileInfo.hashCode
// message.success('文件上传成功')
// 这里可以处理文件上传成功后的逻辑
// 比如更新表单数据等
}
// 上传文件
const uploadFileIndex = ref(0)
async function triggerFileInput(index: number) {
uploadRef.value[0].triggerFileSelect()
uploadFileIndex.value = index
}
function prevStep() {
emit('prevStep')
}
const baseModelTypeList = ref([])
async function getDictType() {
try {
const res = await commonApi.dictType({ type: 'mode_version_type' })
if (res.code === 200) {
baseModelTypeList.value = res.data
}
}
catch (error) {
console.log(error)
}
}
getDictType()
function computedDelFlag() {
return localForm.value.modelVersionList.filter(item => item.delFlag === '0')
}
function onDelete(index: number) {
if (computedDelFlag().length === 1)
return
localForm.value.modelVersionList[index].delFlag = '2'
}
function handledeleteFile(item: any) {
item.filePath = ''
item.fileKey = ''
item.fileName = ''
item.fileSize = ''
item.hashCode = ''
}
</script>
<template>
<div>
<template
v-for="(item, index) in localForm.modelVersionList"
:key="index"
class="bg-gray-100 p-4 mt-4 rounded-lg"
>
<div v-if="item.delFlag === '0'" class="bg-gray-100 p-4 rounded-lg mt-4 relative">
<div class="absolute -right-10 top-4 cursor-pointer">
<Trash class="cursor-pointer" @click="onDelete(index)" />
</div>
<n-form
:ref="(el) => setFormRef(el, index)"
:label-width="80"
:model="localForm.modelVersionList[index]"
:rules="rules"
size="large"
>
<n-form-item label="版本名称" path="versionName">
<n-input v-model:value="item.versionName" placeholder="请输入版本名" />
</n-form-item>
<n-form-item label="基础模型" path="modelVersionType">
<n-select
v-model:value="item.modelVersionType"
label-field="dictLabel"
value-field="dictValue"
placeholder="请选择基础模型"
:options="baseModelTypeList"
/>
</n-form-item>
<div class="flex items-center justify-between">
<div class="flex">
上传文件 <Asterisk :size="10" color="#ff0000" class="mt-1" />
</div>
<div>
<n-checkbox
v-model:checked="item.isEncrypt"
:checked-value="1"
:unchecked-value="0"
label="是否加密"
/>
</div>
</div>
<div
v-if="item.fileName"
class="flex justify-between items-center bg-white p-3 mt-2 rounded-lg"
>
<div
class="bg-[#d8e5fd] text-[12px] text-[#3162ff] w-16 h-7 rounded-lg flex justify-center items-center"
>
100%
</div>
<div class="flex-1 flex items-center line-clamp">
{{ item.fileName }}
</div>
<div>
<Trash
class="cursor-pointer"
@click="handledeleteFile(item)"
/>
</div>
</div>
<div v-else>
<n-spin :show="loading">
<div class="upload-content">
<div
class="flex flex-col justify-center items-center w-30 h-40 border border-dashed mt-2 rounded-lg bg-white"
>
<div
class="w-24 bg-gradient-to-r from-[#2D28FF] to-[#1A7DFF] h-8 text-white rounded-sm bg-[#3162ff] cursor-pointer flex justify-center items-center"
@click="triggerFileInput(index)"
>
上传文件
</div>
<div class="my-3">
点击上传文件
</div>
<div class="text-[#999999] text-xs">
.safetensors/.ckpt/.pt/.bin/.pth/.zip/.json/.flow/.lightflow/.yaml/.yml/.onnx/.gguf/.sft
</div>
</div>
<fileUpload
ref="uploadRef"
:verify-hash="true"
:verify-name="true"
type="model"
:accept="acceptTypesList"
:required="true"
:file-size="30000"
@upload-success="handleUploadSuccess"
/>
</div>
</n-spin>
</div>
<div class="flex mt-6">
版本介绍 <Asterisk :size="10" color="#ff0000" class="mt-1" />
</div>
<div class="bg-white p-3 mt-2 rounded-lg">
<client-only>
<WangEditor v-model="item.versionDescription" />
</client-only>
</div>
<div class="mt-6">
触发词
</div>
<div class="-mb-5 text-gray-400 text-[12px]">
请输入您用来训练的单词
</div>
<n-form-item path="triggerWords">
<n-input v-model:value="item.triggerWords" placeholder="例如: 1boy" />
</n-form-item>
<!-- <div v-if="localForm.modelProduct.modelType === '0'">
<n-form-item label="采样方法" path="modelVersionType">
<n-select
v-model:value="item.modelVersionType"
label-field="dictLabel"
value-field="dictValue"
placeholder="请选择采样方法"
:options="baseModelTypeList"
/>
</n-form-item>
<div>
<n-select v-model:value="item.modelVersionType" multiple :options="baseModelTypeList" />
</div>
</div> -->
</n-form>
<div class="text-gray-400 text-[12px] my-4">
许可范围
</div>
<div class="flex flex-wrap">
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.isOnlineUse"
:checked-value="1"
:unchecked-value="0"
:disabled="true"
label="允许在魔创未来在线使用"
/>
</div>
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.allowDownloadImage"
:checked-value="1"
:unchecked-value="0"
label="允许下载生图"
/>
</div>
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.allowSoftwareUse"
:checked-value="1"
:disabled="true"
:unchecked-value="0"
label="允许在魔创未来旗下其他产品在线使用"
/>
</div>
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.allowFusion"
:checked-value="1"
:unchecked-value="0"
label="允许进行融合"
/>
</div>
</div>
<div class="text-gray-400 text-[12px] my-4">
商用许可范围
</div>
<div class="flex flex-wrap">
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.allowCommercialUse"
:checked-value="1"
:unchecked-value="0"
label="生成图片可出售或用于商业目的"
/>
</div>
<div class="w-[50%] mb-2">
<n-checkbox
v-model:checked="item.allowUsage"
:checked-value="1"
:unchecked-value="0"
label="允许模型转售或融合后出售"
/>
</div>
</div>
<div class="mb-4">
<div class="text-gray-400 text-[12px] my-4">
独家设置
</div>
<div class="flex items-center mb-2">
<n-checkbox
v-model:checked="item.isExclusiveModel"
:checked-value="1"
:unchecked-value="0"
/>
<div class="ml-3 text-[12px] text-gray-500">
此版本为魔创未来独家模型 *获取更多流量:独家模型规则
</div>
</div>
</div>
</div>
</template>
<div class="flex items-center justify-center mt-5">
<div
class="flex justify-center items-center mt-5 w-[20%] mx-2 h-10 rounded-lg bg-[#f1f2f7] cursor-pointer"
@click="prevStep"
>
上一步
</div>
<div
class="flex justify-center items-center mt-5 text-white mx-2 w-[20%] h-10 rounded-lg bg-[#3162ff] cursor-pointer"
@click="nextStep"
>
下一步
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.line-clamp {
margin: 0 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: rgb(72, 71, 71);
}
</style>