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

315 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script setup lang="ts">
import type { FormInst } from 'naive-ui'
import { commonApi } from '@/api/common'
import { uploadImagesInBatches } from '@/utils/uploadImg.ts'
import { Asterisk, Trash } from 'lucide-vue-next'
import { computed, onMounted, ref, watch } from 'vue'
// 可接受的文件类型
const props = defineProps({
modelValue: {
type: Object,
required: true,
},
})
const emit = defineEmits(['update:modelValue', 'createModelsNext'])
const acceptTypes = '.safetensors,.ckpt,.pt,.bin,.pth,.zip,.json,.flow,.lightflow,.yaml,.yml,.onnx,.gguf,.sft'
const isDataReady = ref(false)
const modelVersionItem = {
versionName: '1.0', // 版本名称
modelId: 1, // 基础模型
versionDescription: '"<p>这是一个描述</p><p><img src=\"https://img1.baidu.com/it/u=3001150338,397170470&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1422\" /></p><p>这是两张图片之间的一些文字说明</p><p><img src=\"https://img12.iqilu.com/10339/clue/202405/29/68ec17f5-9621-461f-ad22-a6820a3f9cf5.jpg\" /></p>"', // 版本描述
filePath: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 文件路径
fileName: 'ddefd反问句说的', // 文档里没有 ,表里没有
sampleImagePaths: 'https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png,https://ybl2112.oss-cn-beijing.aliyuncs.com/2025/JANUARY/2/19/4/877e449c-3c0d-4630-a304-91ec110499f2.png', // 第三部的图片路径最多20张,切割
triggerWords: '触发词', // 触发词
isPublic: 1, // 权限是否公开权限 1公开 2自见
allowFusion: 1, // 待确定
allowDownloadImage: 1, // 允许下载生图
allowUsage: 1, // 是否允许使用
isFree: 0, // 是否免费 0免费 1会员
allowSoftwareUse: 1, // 允许在软件旗下使用
allowCommercialUse: 1, // 是否允许商用
// 允许模型转售或者融合手出售字段没找到?
isExclusiveModel: 1, // 是否为独家模型这个字段
}
const isPublicList = [
{
label: '公开',
value: 1,
},
{
label: '仅自己可见',
value: 2,
},
]
defineExpose({
addVersion,
})
onMounted(() => {
// 确保数据初始化完成
nextTick(() => {
isDataReady.value = true
})
})
const localForm = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
},
})
watch(
() => localForm.value,
(newVal) => {
console.log('newVal', newVal)
emit('update:modelValue', newVal)
},
{ immediate: true, deep: true },
)
const formRef = ref<FormInst | null>(null)
const rules = {
versionName: {
required: true,
message: '请输入模型名称',
trigger: 'blur',
},
modelId: {
required: true,
message: '请输入模型名称',
trigger: 'blur',
},
}
function addVersion() {
localForm.value.modelVersionList.push(modelVersionItem)
}
const originalBtnList = ref([
{
label: '免费',
value: 0,
},
{
label: '会员下载',
value: 1,
},
])
function handleIsFree(index: number, value: number) {
localForm.value.modelVersionList[index].isFree = value
}
const model_category = ref([])
function nextStep() {
formRef.value?.validate((errors) => {
if (!errors) {
emit('createModelsNext')
}
else {
console.log('error', errors)
}
})
}
// 上传文件
const uploadFileIndex = ref(0)
const fileInput = ref<HTMLInputElement | null>(null)
function triggerFileInput(index: number) {
(fileInput.value as HTMLInputElement)?.click()
uploadFileIndex.value = index
}
async function handleFileChange(event: Event) {
const target = event.target as HTMLInputElement
const files = target.files
if (files && files.length > 0) {
const res = await uploadImagesInBatches(files)
localForm.value.modelVersionList[uploadFileIndex.value].filePath = res[0].url
localForm.value.modelVersionList[uploadFileIndex.value].fileName = res[0].fileName
}
target.value = ''
}
</script>
<template>
<div v-for="(item, index) in localForm.modelVersionList" :key="index">
<n-form
ref="formRef"
:label-width="80"
:model="localForm"
: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="modelId">
<n-select
v-model:value="item.modelId"
label-field="dictLabel"
value-field="dictValue"
placeholder="请选择基础模型"
:options="model_category"
/>
</n-form-item>
<div class="flex">
上传文件 <Asterisk :size="10" color="#ff0000" class="mt-1" />
</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="item.fileName = '', item.filePath = ''" />
</div>
</div>
<div v-else>
<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>
</div>
</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.versionDesc" />
<!-- <WangEditor
ref="editorRef"
v-model="content"
:height="400"
placeholder="请输入文章内容..."
:disabled="isDisabled"
:upload-img-server="/model/file"
:upload-img-headers="uploadHeaders"
@change="handleContentChange"
@focus="handleFocus"
@blur="handleBlur"
@upload-success="handleUploadSuccess"
@upload-error="handleUploadError"
/> -->
</client-only>
</div>
<!-- <div class="mt-4 space-x-4">
<n-button @click="toggleDisabled">
{{ isDisabled ? '启用编辑器' : '禁用编辑器' }}
</n-button>
<n-button @click="clearContent">
清空内容
</n-button>
<n-button @click="setContent">
设置内容
</n-button>
</div> -->
<!-- 预览区域 -->
<!-- <div class="mt-4">
<h3 class="text-lg font-bold">
预览内容:
</h3>
<div
class="p-4 border rounded-lg mt-2"
v-html="content"
/>
</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 class="">
权限设置
</div>
<div class="mt-1 mb-2 text-gray-400 text-[12px]">
可见范围
</div>
<div>
<n-radio-group v-model:value="item.isPublic" name="radiogroup">
<n-space>
<n-radio v-for="(isPublicItem, isPublicIndex) in isPublicList" :key="isPublicIndex" :value="isPublicItem.value">
{{ isPublicItem.label }}
</n-radio>
</n-space>
</n-radio-group>
</div>
</n-form>
<div v-if="item.isPublic === 1">
<div class="mt-4 mb-1 text-gray-400 text-[12px]">
付费设置
</div>
<div class="flex justify-center items-center bg-white h-10 rounded-lg">
<div
v-for="(subItem, subIndex) in originalBtnList"
:key="subIndex"
:style="{
backgroundColor:
item.isFree === subItem.value
? 'rgba(49, 98, 255, 0.1)'
: '#fff',
color: item.isFree === subItem.value ? '#3162ff' : '#000',
}" class="flex-1 rounded-lg h-full flex items-center justify-center cursor-pointer" @click="handleIsFree(index, subItem.value)"
>
{{ subItem.label }}
</div>
</div>
<div class="mt-1 mb-2 text-gray-500 text-[12px]">
选择会员专属或会员下载视为您已经阅读 <span class="text-[#3162ff] cursor-pointer underline">《会员模型许可协议》</span> 并同意其中条款
</div>
<div v-if="item.isFree === 1" class="text-[12px]">
<div>会员下载模型</div>
<div class="text-gray-500">
下载模型需购买会员,在线生图对所有人开放,无生图次数限制;会员下载的模型版本生成图片默认可商用。
</div>
</div>
</div>
<div class="text-gray-400 text-[12px] mt-4">
许可范围
</div>
<div>
<div />
</div>
<div class="flex justify-center items-center mt-5 text-white w-30 h-10 rounded-lg bg-[#3162ff] cursor-pointer" @click="nextStep">
</div>
</div>
<input
ref="fileInput"
type="file"
class="hidden"
:accept="acceptTypes"
@change="handleFileChange"
>
</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>