315 lines
10 KiB
Vue
315 lines
10 KiB
Vue
<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>
|