refactor parameters for JimengCreate page

This commit is contained in:
GeekMaster
2025-09-11 15:48:07 +08:00
parent 896b5de0a4
commit 65fb58585c
58 changed files with 716 additions and 1174 deletions

View File

@@ -9,10 +9,8 @@ type JimengConfig struct {
// JimengPower 即梦AI算力配置
type JimengPower struct {
TextToImage int `json:"text_to_image"`
ImageToImage int `json:"image_to_image"`
ImageEdit int `json:"image_edit"`
ImageEffects int `json:"image_effects"`
TextToVideo int `json:"text_to_video"`
ImageToVideo int `json:"image_to_video"`
Image int `json:"image"` // 图片生成算力,单位:积分/张
Video int `json:"video"` // 视频生成算力,单位:积分/秒
VirtualHuman int `json:"virtual_human"` // 数字人视频生成算力,单位:积分/秒
ActionTransfer int `json:"action_transfer"` // 视频动作迁移算力,单位:积分/秒
}

View File

@@ -231,28 +231,20 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) {
}
// 验证算力配置
if req.Power.TextToImage <= 0 {
resp.ERROR(c, "文生图算力必须大于0")
if req.Power.Image <= 0 {
resp.ERROR(c, "图片生成算力必须大于0")
return
}
if req.Power.ImageToImage <= 0 {
resp.ERROR(c, "图生图算力必须大于0")
if req.Power.Video <= 0 {
resp.ERROR(c, "视频生成算力必须大于0")
return
}
if req.Power.ImageEdit <= 0 {
resp.ERROR(c, "图片编辑算力必须大于0")
if req.Power.VirtualHuman <= 0 {
resp.ERROR(c, "数字人生成算力必须大于0")
return
}
if req.Power.ImageEffects <= 0 {
resp.ERROR(c, "图片特效算力必须大于0")
return
}
if req.Power.TextToVideo <= 0 {
resp.ERROR(c, "文生视频算力必须大于0")
return
}
if req.Power.ImageToVideo <= 0 {
resp.ERROR(c, "图生视频算力必须大于0")
if req.Power.ActionTransfer <= 0 {
resp.ERROR(c, "视频动作迁移算力必须大于0")
return
}

View File

@@ -132,8 +132,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
switch req.TaskType {
case "text_to_image":
powerCost = h.getPowerFromConfig(model.JMTaskTypeTextToImage)
taskType = model.JMTaskTypeTextToImage
powerCost = h.getPowerFromConfig(model.JMTaskTypeImage)
taskType = model.JMTaskTypeImage
reqKey = jimeng.ReqKeyTextToImage
modelName = "即梦文生图"
if req.Scale == 0 {
@@ -147,8 +147,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
"use_pre_llm": req.UsePreLLM,
}
case "image_to_image":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageToImage)
taskType = model.JMTaskTypeImageToImage
powerCost = h.getPowerFromConfig(model.JMTaskTypeVideo)
taskType = model.JMTaskTypeVideo
reqKey = jimeng.ReqKeyImageToImagePortrait
modelName = "即梦图生图"
if req.Gpen == 0 {
@@ -175,8 +175,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
"seed": req.Seed,
}
case "image_edit":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageEdit)
taskType = model.JMTaskTypeImageEdit
powerCost = h.getPowerFromConfig(model.JMTaskTypeVirtualHuman)
taskType = model.JMTaskTypeVirtualHuman
reqKey = jimeng.ReqKeyImageEdit
modelName = "即梦图像编辑"
if req.Scale == 0 {
@@ -188,8 +188,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
}
params["image_urls"] = []string{req.ImageInput}
case "image_effects":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageEffects)
taskType = model.JMTaskTypeImageEffects
powerCost = h.getPowerFromConfig(model.JMTaskTypeActionTransfer)
taskType = model.JMTaskTypeActionTransfer
reqKey = jimeng.ReqKeyImageEffects
modelName = "即梦图像特效"
if req.Width == 0 {
@@ -205,8 +205,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
"height": req.Height,
}
case "text_to_video":
powerCost = h.getPowerFromConfig(model.JMTaskTypeTextToVideo)
taskType = model.JMTaskTypeTextToVideo
powerCost = h.getPowerFromConfig(model.JMTaskTypeVideo)
taskType = model.JMTaskTypeVideo
reqKey = jimeng.ReqKeyTextToVideo
modelName = "即梦文生视频"
if req.AspectRatio == "" {
@@ -217,8 +217,8 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
"aspect_ratio": req.AspectRatio,
}
case "image_to_video":
powerCost = h.getPowerFromConfig(model.JMTaskTypeImageToVideo)
taskType = model.JMTaskTypeImageToVideo
powerCost = h.getPowerFromConfig(model.JMTaskTypeVideo)
taskType = model.JMTaskTypeVideo
reqKey = jimeng.ReqKeyImageToVideo
modelName = "即梦图生视频"
params = map[string]any{
@@ -287,17 +287,9 @@ func (h *JimengHandler) Jobs(c *gin.Context) {
switch req.Filter {
case "image":
query = query.Where("type IN (?)", []model.JMTaskType{
model.JMTaskTypeTextToImage,
model.JMTaskTypeImageToImage,
model.JMTaskTypeImageEdit,
model.JMTaskTypeImageEffects,
})
query = query.Where("type = ?", model.JMTaskTypeImage)
case "video":
query = query.Where("type IN (?)", []model.JMTaskType{
model.JMTaskTypeTextToVideo,
model.JMTaskTypeImageToVideo,
})
query = query.Where("type = ?", model.JMTaskTypeVideo)
}
if len(req.Ids) > 0 {
@@ -438,18 +430,14 @@ func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int {
config := h.App.SysConfig.Jimeng
switch taskType {
case model.JMTaskTypeTextToImage:
return config.Power.TextToImage
case model.JMTaskTypeImageToImage:
return config.Power.ImageToImage
case model.JMTaskTypeImageEdit:
return config.Power.ImageEdit
case model.JMTaskTypeImageEffects:
return config.Power.ImageEffects
case model.JMTaskTypeTextToVideo:
return config.Power.TextToVideo
case model.JMTaskTypeImageToVideo:
return config.Power.ImageToVideo
case model.JMTaskTypeImage:
return config.Power.Image
case model.JMTaskTypeVideo:
return config.Power.Video
case model.JMTaskTypeVirtualHuman:
return config.Power.VirtualHuman
case model.JMTaskTypeActionTransfer:
return config.Power.ActionTransfer
default:
return 10
}
@@ -459,11 +447,9 @@ func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int {
func (h *JimengHandler) GetPowerConfig(c *gin.Context) {
config := h.App.SysConfig.Jimeng
resp.SUCCESS(c, gin.H{
"text_to_image": config.Power.TextToImage,
"image_to_image": config.Power.ImageToImage,
"image_edit": config.Power.ImageEdit,
"image_effects": config.Power.ImageEffects,
"text_to_video": config.Power.TextToVideo,
"image_to_video": config.Power.ImageToVideo,
"image": config.Power.Image,
"video": config.Power.Video,
"image_edit": config.Power.VirtualHuman,
"image_effects": config.Power.ActionTransfer,
})
}

View File

@@ -199,18 +199,14 @@ func (s *Service) buildTaskRequest(job *model.JimengJob) (*SubmitTaskRequest, er
// 根据任务类型设置特定参数
switch job.Type {
case model.JMTaskTypeTextToImage:
case model.JMTaskTypeImage:
s.setTextToImageParams(req, params)
case model.JMTaskTypeImageToImage:
case model.JMTaskTypeVideo:
s.setImageToImageParams(req, params)
case model.JMTaskTypeImageEdit:
case model.JMTaskTypeVirtualHuman:
s.setImageEditParams(req, params)
case model.JMTaskTypeImageEffects:
case model.JMTaskTypeActionTransfer:
s.setImageEffectsParams(req, params)
case model.JMTaskTypeTextToVideo:
s.setTextToVideoParams(req, params)
case model.JMTaskTypeImageToVideo:
s.setImageToVideoParams(req, params)
default:
return nil, fmt.Errorf("unsupported task type: %s", job.Type)
}

View File

@@ -41,12 +41,10 @@ const (
type JMTaskType string
const (
JMTaskTypeTextToImage = JMTaskType("text_to_image") // 文生图
JMTaskTypeImageToImage = JMTaskType("image_to_image") // 图生图
JMTaskTypeImageEdit = JMTaskType("image_edit") // 图像编辑
JMTaskTypeImageEffects = JMTaskType("image_effects") // 图像特效
JMTaskTypeTextToVideo = JMTaskType("text_to_video") // 文生视频
JMTaskTypeImageToVideo = JMTaskType("image_to_video") // 图生视频
JMTaskTypeImage = JMTaskType("image") // 文生图
JMTaskTypeVideo = JMTaskType("video") // 图生图
JMTaskTypeVirtualHuman = JMTaskType("virtual_human") // 图像编辑
JMTaskTypeActionTransfer = JMTaskType("action_transfer") // 图像特效
)
// TableName 返回数据表名称

View File

@@ -49,16 +49,17 @@
.category-btn {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 15px 10px;
padding: 10px 15px;
border: 2px solid var(--border-color, #f0f0f0);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
background: var(--card-bg-secondary, #fafafa);
/* 暗色主题支持 */
[data-theme="dark"] & {
[data-theme='dark'] & {
background: var(--card-bg-secondary-dark, #23242a);
border-color: var(--border-color-dark, #33343a);
}
@@ -66,7 +67,7 @@
&:hover {
border-color: var(--primary-color, #5865f2);
background: var(--card-bg-hover, #f8f9ff);
[data-theme="dark"] & {
[data-theme='dark'] & {
background: var(--card-bg-hover-dark, #2a2b31);
}
transform: translateY(-2px);
@@ -76,8 +77,11 @@
border-color: var(--primary-color, #5865f2);
background: var(--primary-gradient, linear-gradient(135deg, #5865f2 0%, #7289da 100%));
color: var(--primary-text-on-primary, #fff);
[data-theme="dark"] & {
background: var(--primary-gradient-dark, linear-gradient(135deg, #23242a 0%, #2a2b31 100%));
[data-theme='dark'] & {
background: var(
--primary-gradient-dark,
linear-gradient(135deg, #23242a 0%, #2a2b31 100%)
);
color: var(--primary-text-on-primary-dark, #fff);
}
transform: translateY(-2px);
@@ -96,108 +100,6 @@
}
}
}
// 功能开关
.function-switch {
margin-bottom: 25px;
.switch-label {
display: flex;
align-items: center;
margin-bottom: 15px;
font-size: 16px;
font-weight: 600;
color: var(--text-theme-color);
.el-icon {
margin-right: 8px;
color: var(--primary-color, #5865f2);
}
}
.switch-container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 15px;
border: 1px solid var(--border-color, #e0e0e0);
border-radius: 10px;
background: var(--card-bg-secondary, #f9f9f9);
[data-theme="dark"] & {
background: var(--card-bg-secondary-dark, #23242a);
border-color: var(--border-color-dark, #33343a);
}
.switch-info {
flex: 1;
.switch-title {
font-size: 14px;
font-weight: 600;
color: var(--text-theme-color);
margin-bottom: 4px;
}
.switch-desc {
font-size: 12px;
color: var(--text-sub-color, #666);
}
}
}
}
// 参数容器
.params-container {
.function-panel {
.param-line {
margin-bottom: 15px;
&.pt {
margin-top: 20px;
}
.label {
display: flex;
align-items: center;
margin-bottom: 8px;
font-weight: 600;
color: var(--text-theme-color);
}
}
.item-group {
display: flex;
align-items: center;
margin-bottom: 15px;
.label {
margin-right: 15px;
font-weight: 600;
color: var(--text-theme-color);
min-width: 80px;
}
}
.text-info {
margin: 20px 0;
padding: 15px;
background: var(--info-bg, #f0f8ff);
border-radius: 8px;
border-left: 4px solid var(--primary-color, #5865f2);
}
.submit-btn {
margin-top: 30px;
.el-button {
width: 100%;
height: 50px;
font-size: 16px;
font-weight: 600;
}
}
}
}
}
// 右侧主要内容区域
@@ -239,6 +141,23 @@
&:hover {
box-shadow: 0 4px 24px rgba(88, 101, 242, 0.12);
}
// 增强任务项悬停动画
transition: box-shadow 3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.5s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
border: 1.5px solid transparent;
border-radius: 12px;
background: #fff;
position: relative;
z-index: 1;
&:hover {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18), 0 1.5px 8px rgba(0, 0, 0, 0.1);
border-color: #a259ff;
transform: scale(1.025) translateY(-2px);
z-index: 10;
}
.task-left {
width: 100%;
flex: none;
@@ -253,18 +172,69 @@
display: flex;
align-items: center;
justify-content: center;
.preview-image, .preview-video {
.preview-image,
.preview-video {
width: 100%;
height: 100%;
object-fit: cover;
}
// 视频预览包装器
.preview-video-wrapper {
position: relative;
width: 100%;
height: 100%;
.video-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s;
z-index: 2;
}
&:hover .video-mask {
opacity: 1;
}
.play-btn {
width: 64px;
height: 64px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
cursor: pointer;
z-index: 3;
transition: background 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.4);
}
img {
width: 36px;
height: 36px;
}
}
}
.preview-placeholder {
display: flex;
flex-direction: column;
align-items: center;
color: var(--text-disabled-color, #999);
font-size: 16px;
.el-icon, .iconfont {
.el-icon,
.iconfont {
font-size: 32px;
margin-bottom: 5px;
}
@@ -346,4 +316,70 @@
max-height: 220px;
}
}
}
}
// 错误信息样式
.err-msg-clip {
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: normal;
}
// 模板选择器样式
.jimeng-template-select {
.el-select-dropdown__item {
height: 60px;
line-height: 60px;
}
}
// 提示词指南样式
.prompt-guide {
margin: 12px 0 16px;
.guide-title {
display: flex;
align-items: center;
font-weight: 600;
color: #666;
}
.guide-content {
max-height: 220px;
overflow: auto;
line-height: 1.6;
font-size: 12px;
color: #555;
padding-right: 4px;
}
.guide-section {
margin-bottom: 10px;
}
.guide-subtitle {
font-weight: 600;
margin-bottom: 6px;
color: #333;
}
ul {
list-style: disc;
padding-left: 18px;
margin: 4px 0;
}
.quote {
margin: 8px 0;
padding: 8px 10px;
border-left: 3px solid #a3a3a3;
background: #f8f8f8;
border-radius: 4px;
color: #444;
}
}

View File

@@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1757465848673') format('woff2'),
url('iconfont.woff?t=1757465848673') format('woff'),
url('iconfont.ttf?t=1757465848673') format('truetype');
src: url('iconfont.woff2?t=1757571432313') format('woff2'),
url('iconfont.woff?t=1757571432313') format('woff'),
url('iconfont.ttf?t=1757571432313') format('truetype');
}
.iconfont {
@@ -13,6 +13,26 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-resize:before {
content: "\e718";
}
.icon-template:before {
content: "\e8a6";
}
.icon-error-line:before {
content: "\e868";
}
.icon-success-line:before {
content: "\e88c";
}
.icon-yunjing:before {
content: "\e69b";
}
.icon-action:before {
content: "\e658";
}

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,41 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "10564356",
"name": "resize",
"font_class": "resize",
"unicode": "e718",
"unicode_decimal": 59160
},
{
"icon_id": "1727381",
"name": "34模板、框架",
"font_class": "template",
"unicode": "e8a6",
"unicode_decimal": 59558
},
{
"icon_id": "9626841",
"name": "错误",
"font_class": "error-line",
"unicode": "e868",
"unicode_decimal": 59496
},
{
"icon_id": "9626990",
"name": "正确",
"font_class": "success-line",
"unicode": "e88c",
"unicode_decimal": 59532
},
{
"icon_id": "40613765",
"name": "运镜控制",
"font_class": "yunjing",
"unicode": "e69b",
"unicode_decimal": 59035
},
{
"icon_id": "5215282",
"name": "动作",

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

Before

Width:  |  Height:  |  Size: 1022 KiB

After

Width:  |  Height:  |  Size: 1022 KiB

View File

Before

Width:  |  Height:  |  Size: 1016 KiB

After

Width:  |  Height:  |  Size: 1016 KiB

View File

Before

Width:  |  Height:  |  Size: 1002 KiB

After

Width:  |  Height:  |  Size: 1002 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Before

Width:  |  Height:  |  Size: 856 KiB

After

Width:  |  Height:  |  Size: 856 KiB

View File

Before

Width:  |  Height:  |  Size: 877 KiB

After

Width:  |  Height:  |  Size: 877 KiB

View File

Before

Width:  |  Height:  |  Size: 698 KiB

After

Width:  |  Height:  |  Size: 698 KiB

View File

Before

Width:  |  Height:  |  Size: 948 KiB

After

Width:  |  Height:  |  Size: 948 KiB

View File

Before

Width:  |  Height:  |  Size: 1021 KiB

After

Width:  |  Height:  |  Size: 1021 KiB

View File

Before

Width:  |  Height:  |  Size: 874 KiB

After

Width:  |  Height:  |  Size: 874 KiB

View File

Before

Width:  |  Height:  |  Size: 809 KiB

After

Width:  |  Height:  |  Size: 809 KiB

View File

@@ -2,10 +2,10 @@
<div class="foot-container">
<div class="footer text-base">
<div>
<a :href="gitURL" target="_blank">
<span>
{{ title }} -
{{ version }}
</a>
</span>
</div>
<div class="flex justify-center text-sm">
<span class="mr-2">{{ copyRight }}</span>

View File

@@ -29,7 +29,7 @@
<span class="label text-sm">{{ item.name }}</span>
<div class="whitespace-pre-line">
<span
class="text-xs text-gray-500 break-words whitespace-pre-line line-clamp-1"
class="text-xs text-gray-500 break-words line-clamp-1 max-w-[200px]"
:title="item.label"
>{{ item.label }}</span
>
@@ -90,7 +90,11 @@
v-model="modelValue[param.name]"
:placeholder="param.placeholder"
:popper-class="param.popperClass"
filterable
>
<template #prefix v-if="param.prefix">
<i class="iconfont !text-lg" :class="param.prefix"></i>
</template>
<el-option
v-for="option in param.options"
:key="option.value"
@@ -99,11 +103,18 @@
>
<div class="flex justify-start" v-if="option.image">
<span class="flex py-3 mr-2">
<img :src="option.image" class="w-[54px] h-[54px] rounded-lg"
<img
:src="option.image"
class="rounded-lg"
:style="{ width: param.imgSize, height: param.imgSize }"
/></span>
<div class="flex !items-start flex-col py-2 space-y-1">
<span class="label text-sm">{{ option.label }}</span>
<span class="text-xs text-gray-500">{{ option.value }}</span>
<span
class="text-xs text-gray-500 break-words line-clamp-1 max-w-[200px]"
:title="option.value"
>{{ option.value }}</span
>
</div>
</div>
<div class="flex justify-start items-center h-full" v-else>
@@ -148,6 +159,11 @@ const props = defineProps({
type: Object,
required: true,
},
requiredKeys: {
type: Object,
default: {},
required: false,
},
items: {
type: Array,
required: true,
@@ -159,15 +175,20 @@ const props = defineProps({
},
})
const selectedModel = ref({ label: '请选择模型' })
const selectedModel = ref(props.items[0])
const requiredKeys = ref(props.requiredKeys)
const emit = defineEmits(['update:modelValue'])
// 初始化 modelValue 默认值
const initModelValue = (model) => {
const defaultValues = {}
requiredKeys.value = {}
if (model && model.params) {
model.params.forEach((param) => {
if (param.required) {
requiredKeys.value[param.name] = { required: true, label: param.label }
}
// 根据参数类型设置默认值
switch (param.type) {
case 'text':
@@ -201,6 +222,7 @@ const initModelValue = (model) => {
}
})
}
defaultValues.model = selectedModel.value.key
return defaultValues
}
@@ -216,6 +238,22 @@ watch(
{ deep: true }
)
watch(
requiredKeys,
(newValue) => {
emit('update:requiredKeys', newValue)
},
{ deep: true }
)
watch(
() => props.items,
(newValue) => {
selectedModel.value = newValue[0]
},
{ deep: true }
)
// 组件挂载时初始化
onMounted(() => {
// 确保初始值被正确设置

View File

@@ -1,17 +1,107 @@
import central_orbit from '@/assets/img/jimeng/central_orbit.webp'
import clockwise_swivel from '@/assets/img/jimeng/clockwise_swivel.webp'
import counterclockwise_swivel from '@/assets/img/jimeng/counterclockwise_swivel.webp'
import crane_push from '@/assets/img/jimeng/crane_push.webp'
import dynamic_orbit from '@/assets/img/jimeng/dynamic_orbit.webp'
import handheld from '@/assets/img/jimeng/handheld.webp'
import hitchcock_dolly_in from '@/assets/img/jimeng/hitchcock_dolly_in.webp'
import hitchcock_dolly_out from '@/assets/img/jimeng/hitchcock_dolly_out.webp'
import quick_pull_back from '@/assets/img/jimeng/quick_pull_back.webp'
import rapid_push_pull from '@/assets/img/jimeng/rapid_push_pull.webp'
import robo_arm from '@/assets/img/jimeng/robo_arm.webp'
import central_orbit from '@/assets/img/jimeng/yunjing/central_orbit.webp'
import clockwise_swivel from '@/assets/img/jimeng/yunjing/clockwise_swivel.webp'
import counterclockwise_swivel from '@/assets/img/jimeng/yunjing/counterclockwise_swivel.webp'
import crane_push from '@/assets/img/jimeng/yunjing/crane_push.webp'
import dynamic_orbit from '@/assets/img/jimeng/yunjing/dynamic_orbit.webp'
import handheld from '@/assets/img/jimeng/yunjing/handheld.webp'
import hitchcock_dolly_in from '@/assets/img/jimeng/yunjing/hitchcock_dolly_in.webp'
import hitchcock_dolly_out from '@/assets/img/jimeng/yunjing/hitchcock_dolly_out.webp'
import quick_pull_back from '@/assets/img/jimeng/yunjing/quick_pull_back.webp'
import rapid_push_pull from '@/assets/img/jimeng/yunjing/rapid_push_pull.webp'
import robo_arm from '@/assets/img/jimeng/yunjing/robo_arm.webp'
import acrylic_ornaments from '@/assets/img/jimeng/texiao/acrylic_ornaments.png'
import angel_figurine from '@/assets/img/jimeng/texiao/angel_figurine.png'
import birthday_photo_gorgeous from '@/assets/img/jimeng/texiao/birthday_photo_gorgeous.jpeg'
import birthday_photo_party from '@/assets/img/jimeng/texiao/birthday_photo_party.jpeg'
import birthday_photo_red from '@/assets/img/jimeng/texiao/birthday_photo_red.jpeg'
import car_miniature_ornaments from '@/assets/img/jimeng/texiao/car_miniature_ornaments.jpeg'
import Christmas_green_background from '@/assets/img/jimeng/texiao/Christmas_green_background.jpeg'
import Christmas_tree from '@/assets/img/jimeng/texiao/Christmas_tree.jpeg'
import claw_machine_style from '@/assets/img/jimeng/texiao/claw_machine_style.jpeg'
import earphone_case_style from '@/assets/img/jimeng/texiao/earphone_case_style.jpeg'
import electronic_pet_egg_style from '@/assets/img/jimeng/texiao/electronic_pet_egg_style.jpeg'
import felt_3d_polaroid from '@/assets/img/jimeng/texiao/felt_3d_polaroid.png'
import felt_keychain from '@/assets/img/jimeng/texiao/felt_keychain.png'
import furry_dream_doll from '@/assets/img/jimeng/texiao/furry_dream_doll.png'
import glass_ball from '@/assets/img/jimeng/texiao/glass_ball.png'
import graduation_photo from '@/assets/img/jimeng/texiao/graduation_photo.png'
import lofi_pixel_character_mini_card from '@/assets/img/jimeng/texiao/lofi_pixel_character_mini_card.png'
import lying_in_fluffy_belly from '@/assets/img/jimeng/texiao/lying_in_fluffy_belly.png'
import micro_landscape_mini_world from '@/assets/img/jimeng/texiao/micro_landscape_mini_world.png'
import micro_landscape_mini_world_professional from '@/assets/img/jimeng/texiao/micro_landscape_mini_world_professional.png'
import Mid_Autumn_Festival_individual from '@/assets/img/jimeng/texiao/Mid-Autumn_Festival_individual.jpeg'
import Mid_Autumn_Festival_new_chinese_style from '@/assets/img/jimeng/texiao/Mid-Autumn_Festival_new_chinese_style.jpeg'
import my_world from '@/assets/img/jimeng/texiao/my_world.png'
import my_world_universal from '@/assets/img/jimeng/texiao/my_world_universal.png'
import patchwork_collage_style from '@/assets/img/jimeng/texiao/patchwork_collage_style.jpeg'
import plastic_bubble_figure from '@/assets/img/jimeng/texiao/plastic_bubble_figure.png'
import plastic_bubble_figure_cartoon_text from '@/assets/img/jimeng/texiao/plastic_bubble_figure_cartoon_text.png'
import Spring_Festival_traditional_Chinese_architecture from '@/assets/img/jimeng/texiao/Spring_Festival_traditional_Chinese_architecture.png'
export const JimengParams = {
image: [
{
name: '图片 4.0 文/图生图',
version: '4.0',
label: '支持文本、单图和多图输入,实现基于主体一致性的多图融合创作、图像编辑等多样玩法',
key: 'doubao-seedream-4-0-250828',
params: [
{
name: 'prompt',
label: '提示词',
type: 'textarea',
required: true,
showWordLimit: true,
maxlength: 800,
autosize: { minRows: 3, maxRows: 5 },
placeholder: '请输入用于编辑图像的提示词把xxx改成xxx删除xxx添加xxx等',
info: '建议不超过300个汉字或600个英文单词。字数过多信息容易分散模型可能因此忽略细节。',
},
{
name: 'image_urls',
label: '参考图片',
type: 'image',
required: false,
placeholder: '请上传图片',
maxSize: 5,
multiple: true,
maxCount: 10,
accept: '.png,.jpg,.jpeg',
info: '支持编辑单张图片,或者一次融合多张图片',
},
{
name: 'size',
type: 'select',
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '1:1 (1328 * 1328)',
value: '1328x1328',
},
{
label: '4:3 (1472 * 1104)',
value: '1472x1104',
},
{
label: '3:2 (1584 * 1056)',
value: '1584x1056',
},
{
label: '16:9 (1664 * 936)',
value: '1664x936',
},
{
label: '21:9 (2016 * 864)',
value: '2016x864',
},
],
},
],
},
{
name: '图片 2.1 文生图',
version: '2.1',
@@ -36,6 +126,7 @@ export const JimengParams = {
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '21:9 (1195 * 512)',
@@ -104,7 +195,7 @@ export const JimengParams = {
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '1:1 (1328 * 1328)',
@@ -181,7 +272,7 @@ export const JimengParams = {
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '1:1 (1328 * 1328)',
@@ -263,54 +354,56 @@ export const JimengParams = {
accept: '.png,.jpg,.jpeg',
info: '长边与短边比例在3以内超出此比例或比例相对极端会导致报错。',
},
{
name: 'scale',
label: '文本描述影响的程度',
type: 'slider',
min: 0,
max: 1,
step: 0.1,
value: 0.5,
info: '该值越大代表文本描述影响程度越大,且输入图片影响程度越小',
},
{
name: 'size',
type: 'select',
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '11 (1328 * 1328)',
value: '1328x1328',
label: '1:1 (2048 * 2048)',
value: '2048x2048',
},
{
label: '43 (1472 * 1104)',
value: '1472x1104',
label: '4:3 (2304 * 1728)',
value: '2304x1728',
},
{
label: '32 (1584 * 1056)',
value: '1584x1056',
label: '3:4 (1728 * 2304)',
value: '1728x2304',
},
{
label: '169 (1664 * 936)',
value: '1664x936',
label: '16:9 (2560 * 1440)',
value: '2560x1440',
},
{
label: '219 (2016 * 864)',
value: '2016x864',
label: '9:16 (1440 * 2560)',
value: '1440x2560',
},
{
label: '3:2 (2496 * 1664)',
value: '2496x1664',
},
{
label: '2:3 (1664 * 2496)',
value: '1664x2496',
},
{
label: '21:9 (3024 * 1296)',
value: '3024x1296',
},
],
},
],
},
{
name: '图片 4.0 文/图生图',
version: '4.0',
label:
'支持文本、单图和多图输入,实现基于主体一致性的多图融合创作、图像编辑、组图生成等多样玩法',
key: 'jimeng_i2i_v30',
name: '图片 3.0 图像特效',
version: '3.0',
label: '将输入的单人写真图片,进行有创意的特效化处理。',
key: 'i2i_multi_style_zx2x',
params: [
{
name: 'prompt',
@@ -331,17 +424,159 @@ export const JimengParams = {
placeholder: '请上传图片',
maxSize: 5,
accept: '.png,.jpg,.jpeg',
info: '长边与短边比例在3以内超出此比例或比例相对极端会导致报错。',
info: '支持输入人像写真图片。',
},
{
name: 'scale',
label: '文本描述影响的程度',
type: 'slider',
min: 0,
max: 1,
step: 0.1,
value: 0.5,
info: '该值越大代表文本描述影响程度越大,且输入图片影响程度越小',
name: 'template_id',
label: '特效模板ID',
type: 'select',
required: true,
placeholder: '请选择特效模板ID',
imgSize: '40px',
popperClass: 'model-select',
prefix: 'icon-sd',
options: [
{
label: '毛毡3D拍立得风格',
value: 'felt_3d_polaroid',
image: felt_3d_polaroid,
},
{
label: '像素世界风',
value: 'my_world',
image: my_world,
},
{
label: '像素世界-万物通用版',
value: 'my_world_universal',
image: my_world_universal,
},
{
label: '盲盒玩偶风',
value: 'plastic_bubble_figure',
image: plastic_bubble_figure,
},
{
label: '塑料泡罩人偶-文字卡头版',
value: 'plastic_bubble_figure_cartoon_text',
image: plastic_bubble_figure_cartoon_text,
},
{
label: '毛绒玩偶风',
value: 'furry_dream_doll',
image: furry_dream_doll,
},
{
label: '迷你世界玩偶风',
value: 'micro_landscape_mini_world',
image: micro_landscape_mini_world,
},
{
label: '微型景观小世界-职业版',
value: 'micro_landscape_mini_world_professional',
image: micro_landscape_mini_world_professional,
},
{
label: '亚克力挂饰',
value: 'acrylic_ornaments',
image: acrylic_ornaments,
},
{
label: '毛毡钥匙扣',
value: 'felt_keychain',
image: felt_keychain,
},
{
label: 'Lofi 像素人物小卡',
value: 'lofi_pixel_character_mini_card',
image: lofi_pixel_character_mini_card,
},
{
label: '天使形象手办',
value: 'angel_figurine',
image: angel_figurine,
},
{
label: '躺在毛茸茸肚皮里',
value: 'lying_in_fluffy_belly',
image: lying_in_fluffy_belly,
},
{
label: '玻璃球',
value: 'glass_ball',
image: glass_ball,
},
{
label: '耳机盒',
value: 'earphone_case_style',
image: earphone_case_style,
},
{
label: '电子宠物蛋',
value: 'electronic_pet_egg_style',
image: electronic_pet_egg_style,
},
{
label: '拼贴缝布',
value: 'patchwork_collage_style',
image: patchwork_collage_style,
},
{
label: '抓娃娃机',
value: 'claw_machine_style',
image: claw_machine_style,
},
{
label: '车内微缩摆件',
value: 'car_miniature_ornaments',
image: car_miniature_ornaments,
},
{
label: '中秋节-新中式',
value: 'Mid-Autumn_Festival_new_chinese_style',
image: Mid_Autumn_Festival_new_chinese_style,
},
{
label: '中秋单人',
value: 'Mid-Autumn_Festival_individual',
image: Mid_Autumn_Festival_individual,
},
{
label: '圣诞节绿背景',
value: 'Christmas_green_background',
image: Christmas_green_background,
},
{
label: '圣诞节圣诞树',
value: 'Christmas_tree',
image: Christmas_tree,
},
{
label: '春节红墙',
value: 'Spring_Festival_traditional_Chinese_architecture',
image: Spring_Festival_traditional_Chinese_architecture,
},
{
label: '生日照华丽',
value: 'birthday_photo_gorgeous',
image: birthday_photo_gorgeous,
},
{
label: '生日照红色',
value: 'birthday_photo_red',
image: birthday_photo_red,
},
{
label: '生日照派对',
value: 'birthday_photo_party',
image: birthday_photo_party,
},
{
label: '毕业照',
value: 'graduation_photo',
image: graduation_photo,
},
],
},
{
name: 'size',
@@ -349,26 +584,26 @@ export const JimengParams = {
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '11 (1328 * 1328)',
label: '1:1 (1328 * 1328)',
value: '1328x1328',
},
{
label: '43 (1472 * 1104)',
label: '4:3 (1472 * 1104)',
value: '1472x1104',
},
{
label: '32 (1584 * 1056)',
label: '3:2 (1584 * 1056)',
value: '1584x1056',
},
{
label: '169 (1664 * 936)',
label: '16:9 (1664 * 936)',
value: '1664x936',
},
{
label: '219 (2016 * 864)',
label: '21:9 (2016 * 864)',
value: '2016x864',
},
],
@@ -377,6 +612,7 @@ export const JimengParams = {
},
],
video: [
// 视频 3.0 720P-文生视频
{
name: '视频 3.0 720P-文生视频',
version: '3.0',
@@ -398,8 +634,9 @@ export const JimengParams = {
name: 'aspect_ratio',
label: '视频比例',
type: 'select',
required: false,
required: true,
placeholder: '请选择视频比例',
prefix: 'icon-resize',
options: [
{
label: '16:9 (横版)',
@@ -431,6 +668,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -444,7 +682,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 图生视频-首帧
{
name: '视频 3.0 720P-图生视频-首帧',
version: '3.0',
@@ -466,7 +704,7 @@ export const JimengParams = {
name: 'image_urls',
label: '首帧图片',
type: 'image',
required: false,
required: true,
multiple: false,
maxCount: 1,
maxSize: 5,
@@ -476,6 +714,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -489,7 +728,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 图生视频-首尾帧
{
name: '视频 3.0 720P-图生视频-首尾帧',
version: '3.0',
@@ -511,7 +750,7 @@ export const JimengParams = {
name: 'image_urls',
label: '首尾帧图片',
type: 'image',
required: false,
required: true,
multiple: true,
maxCount: 2,
maxSize: 5,
@@ -522,6 +761,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -535,7 +775,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 图生视频-运镜
{
name: '视频 3.0 720P-图生视频-运镜',
version: '3.0',
@@ -557,7 +797,7 @@ export const JimengParams = {
name: 'image_urls',
label: '运镜图片',
type: 'image',
required: false,
required: true,
placeholder: '请上传图片',
maxSize: 5,
multiple: true,
@@ -571,6 +811,8 @@ export const JimengParams = {
required: true,
placeholder: '请选择运镜控制',
popperClass: 'model-select',
prefix: 'icon-yunjing',
imgSize: '54px',
options: [
{
label: '希区柯克推进',
@@ -656,6 +898,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -670,7 +913,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 1080P-文生视频
{
name: '视频 3.0 1080P-文生视频',
version: '3.0',
@@ -694,6 +937,7 @@ export const JimengParams = {
type: 'select',
required: false,
placeholder: '请选择视频比例',
prefix: 'icon-resize',
options: [
{
label: '16:9 (横版)',
@@ -725,6 +969,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -738,7 +983,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 1080P-图生视频-首帧
{
name: '视频 3.0 1080P-图生视频-首帧',
version: '3.0',
@@ -760,7 +1005,7 @@ export const JimengParams = {
name: 'image_urls',
label: '首帧图片',
type: 'image',
required: false,
required: true,
multiple: false,
maxCount: 1,
maxSize: 5,
@@ -770,6 +1015,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -783,7 +1029,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0 1080P-图生视频-首尾帧
{
name: '视频 3.0 1080P-图生视频-首尾帧',
version: '3.0',
@@ -805,7 +1051,7 @@ export const JimengParams = {
name: 'image_urls',
label: '首尾帧图片',
type: 'image',
required: false,
required: true,
multiple: true,
maxCount: 2,
maxSize: 5,
@@ -816,6 +1062,7 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
options: [
{
label: '5秒',
@@ -829,7 +1076,7 @@ export const JimengParams = {
},
],
},
// 视频 3.0Pro 1080P-图生视频
{
name: '视频 3.0Pro 1080P-图生视频',
version: '3.0',
@@ -866,6 +1113,7 @@ export const JimengParams = {
required: false,
placeholder: '请选择视频比例',
info: '只在文生视频场景下生效,图生视频场景会根据输入图的长宽比自动适配',
prefix: 'icon-resize',
options: [
{
label: '21:9 (2176 * 928)',
@@ -897,6 +1145,8 @@ export const JimengParams = {
name: 'duration',
type: 'select',
label: '视频时长',
prefix: 'icon-clock',
placeholder: '请选择视频时长',
options: [
{
label: '5秒',
@@ -919,7 +1169,7 @@ export const JimengParams = {
export const JimengFunctions = [
{
key: 'image',
icon: 'iconfont icon-image',
icon: 'icon-image',
name: '图片生成',
},
{

View File

@@ -6,24 +6,16 @@
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
import { checkSession } from '@/store/cache'
import { JimengParams } from '@/store/data'
import { JimengFunctions, JimengParams } from '@/store/data/jimeng_data'
import { useSharedStore } from '@/store/sharedata'
import { showMessageError, showMessageOK } from '@/utils/dialog'
import { httpDownload, httpGet, httpPost } from '@/utils/http'
import { replaceImg, substr } from '@/utils/libs'
import { ElMessageBox } from 'element-plus'
import { defineStore } from 'pinia'
import { computed, reactive, ref } from 'vue'
import { reactive, ref } from 'vue'
export const useJimengStore = defineStore('jimeng', () => {
// 当前激活的功能分类和具体功能
const activeCategory = ref('image_generation')
const activeFunction = ref('text_to_image')
const useImageInput = ref(false)
// 新增:全局提示词
const currentPrompt = ref('')
// 共同状态
const loading = ref(false)
const submitting = ref(false)
@@ -45,73 +37,8 @@ export const useJimengStore = defineStore('jimeng', () => {
// 登录弹窗
const shareStore = useSharedStore()
const paramsMap = JimengParams
// 功能分类配置
const categories = [
{ key: 'image_generation', name: '图片生成' },
{ key: 'image_editing', name: 'AI修图' },
{ key: 'image_effects', name: '图像特效' },
{ key: 'video_generation', name: '视频生成' },
]
// 新增:动态获取算力消耗配置
const powerConfig = reactive({})
// 功能配置
const functions = reactive([
{
key: 'text_to_image',
name: '文生图',
category: 'image_generation',
needsPrompt: true,
needsImage: false,
power: 20,
},
{
key: 'image_to_image',
name: '图生图',
category: 'image_generation',
needsPrompt: true,
needsImage: true,
power: 30,
},
{
key: 'image_edit',
name: '图像编辑',
category: 'image_editing',
needsPrompt: true,
needsImage: true,
multiple: true,
power: 25,
},
{
key: 'image_effects',
name: '图像特效',
category: 'image_effects',
needsPrompt: false,
needsImage: true,
power: 15,
},
{
key: 'text_to_video',
name: '文生视频',
category: 'video_generation',
needsPrompt: true,
needsImage: false,
power: 100,
},
{
key: 'image_to_video',
name: '图生视频',
category: 'video_generation',
needsPrompt: true,
needsImage: true,
multiple: true,
power: 120,
},
])
// 动态设置算力消耗
const setFunctionPowers = (config) => {
functions.forEach((f) => {
@@ -121,114 +48,32 @@ export const useJimengStore = defineStore('jimeng', () => {
})
}
// 功能的参数
const textToImageParams = reactive({
size: '1328x1328',
scale: 2.5,
seed: -1,
use_pre_llm: true,
// 功能配置
const functions = JimengFunctions
// 当前激活的功能
const activeFunction = ref('image')
// 参数配置
const functionParams = JimengParams
// 表单数据
const formData = ref({})
// 必填参数
const requiredKeys = ref({})
// 进度
const progress = ref({
image: 100,
video: 100,
virtualHuman: 38,
actionTransfer: 65,
})
const imageToImageParams = reactive({
image_input: '',
size: '1328x1328',
gpen: 0.4,
skin: 0.3,
skin_unifi: 0,
gen_mode: 'creative',
seed: -1,
})
const imageEditParams = reactive({
image_input: '',
scale: 0.5,
seed: -1,
})
const imageEffectsParams = reactive({
image_input: '',
template_id: '',
size: '1328x1328',
})
const textToVideoParams = reactive({
aspect_ratio: '16:9',
seed: -1,
})
const imageToVideoParams = reactive({
image_input: [],
aspect_ratio: '16:9',
seed: -1,
})
// 计算属性
const currentFunction = computed(() => {
return functions.find((f) => f.key === activeFunction.value) || functions[0]
})
const currentFunctions = computed(() => {
return functions.filter((f) => f.category === activeCategory.value)
})
const needsPrompt = computed(() => currentFunction.value.needsPrompt)
const needsImage = computed(() => currentFunction.value.needsImage)
const needsMultipleImages = computed(() => currentFunction.value.multiple)
const currentPowerCost = computed(() => currentFunction.value.power)
// 初始化方法
const init = async () => {
try {
// 获取算力消耗配置
const powerRes = await httpGet('/api/jimeng/power-config')
if (powerRes.data) {
Object.assign(powerConfig, powerRes.data)
setFunctionPowers(powerRes.data)
}
const user = await checkSession()
isLogin.value = true
userPower.value = user.power
// 获取任务列表
await fetchData(1)
// 开始轮询
startPolling()
} catch (error) {
console.error('初始化失败:', error)
}
}
// 切换功能分类
const switchCategory = (category) => {
activeCategory.value = category
const categoryFunctions = functions.filter((f) => f.category === category)
if (categoryFunctions.length > 0) {
if (category === 'image_generation') {
activeFunction.value = useImageInput.value ? 'image_to_image' : 'text_to_image'
} else if (category === 'video_generation') {
activeFunction.value = useImageInput.value ? 'image_to_video' : 'text_to_video'
} else {
activeFunction.value = categoryFunctions[0].key
}
}
}
// 切换输入模式
const switchInputMode = () => {
if (activeCategory.value === 'image_generation') {
activeFunction.value = useImageInput.value ? 'image_to_image' : 'text_to_image'
} else if (activeCategory.value === 'video_generation') {
activeFunction.value = useImageInput.value ? 'image_to_video' : 'text_to_video'
}
}
// 切换功能
const switchFunction = (functionKey) => {
activeFunction.value = functionKey
const switchFunction = (f) => {
activeFunction.value = f.key
formData.value = {}
}
// 获取当前算力消耗
const getCurrentPowerCost = () => {
return currentFunction.value.power
return activeFunction.value.power
}
// 获取功能名称
@@ -353,71 +198,21 @@ export const useJimengStore = defineStore('jimeng', () => {
shareStore.setShowLoginDialog(true)
return
}
if (userPower.value < currentPowerCost.value) {
showMessageError('算力不足')
return
}
// 新增:除图像特效外,其他任务类型必须有提示词
if (activeFunction.value !== 'image_effects' && !currentPrompt.value) {
showMessageError('提示词不能为空')
return
// if (userPower.value < currentPowerCost.value) {
// showMessageError('算力不足')
// return
// }
for (const key in requiredKeys.value) {
if (!formData.value[key].required) {
showMessageError('缺少参数:' + requiredKeys.value[key].label)
return
}
}
try {
submitting.value = true
let requestData = { task_type: activeFunction.value, prompt: currentPrompt.value }
switch (activeFunction.value) {
case 'text_to_image':
Object.assign(requestData, {
width: parseInt(textToImageParams.size.split('x')[0]),
height: parseInt(textToImageParams.size.split('x')[1]),
scale: textToImageParams.scale,
seed: textToImageParams.seed,
use_pre_llm: textToImageParams.use_pre_llm,
})
break
case 'image_to_image':
Object.assign(requestData, {
image_input: imageToImageParams.image_input[0],
width: parseInt(imageToImageParams.size.split('x')[0]),
height: parseInt(imageToImageParams.size.split('x')[1]),
gpen: imageToImageParams.gpen,
skin: imageToImageParams.skin,
skin_unifi: imageToImageParams.skin_unifi,
gen_mode: imageToImageParams.gen_mode,
seed: imageToImageParams.seed,
})
break
case 'image_edit':
Object.assign(requestData, {
image_input: imageEditParams.image_input[0],
scale: imageEditParams.scale,
seed: imageEditParams.seed,
})
break
case 'image_effects':
Object.assign(requestData, {
image_input: imageEffectsParams.image_input[0],
template_id: imageEffectsParams.template_id,
width: parseInt(imageEffectsParams.size.split('x')[0]),
height: parseInt(imageEffectsParams.size.split('x')[1]),
prompt: imageEffectsParams.prompt,
})
break
case 'text_to_video':
Object.assign(requestData, {
aspect_ratio: textToVideoParams.aspect_ratio,
seed: textToVideoParams.seed,
})
break
case 'image_to_video':
Object.assign(requestData, {
image_urls: imageToVideoParams.image_input,
aspect_ratio: imageToVideoParams.aspect_ratio,
seed: imageToVideoParams.seed,
})
break
}
const response = await httpPost('/api/jimeng/task', requestData)
const response = await httpPost('/api/jimeng/task', formData.value)
if (response.data) {
showMessageOK('任务提交成功')
isOver.value = false
@@ -501,6 +296,27 @@ export const useJimengStore = defineStore('jimeng', () => {
showDialog.value = true
}
// 初始化方法
const init = async () => {
try {
// 获取算力消耗配置
const powerRes = await httpGet('/api/jimeng/power-config')
if (powerRes.data) {
Object.assign(powerConfig, powerRes.data)
setFunctionPowers(powerRes.data)
}
const user = await checkSession()
isLogin.value = true
userPower.value = user.power
// 获取任务列表
await fetchData(1)
// 开始轮询
startPolling()
} catch (error) {
console.error('初始化失败:', error)
}
}
// 页面卸载时清理轮询
const cleanup = () => {
stopPolling()
@@ -509,9 +325,7 @@ export const useJimengStore = defineStore('jimeng', () => {
// 返回所有状态和方法
return {
// 状态
activeCategory,
activeFunction,
useImageInput,
loading,
submitting,
page,
@@ -526,31 +340,16 @@ export const useJimengStore = defineStore('jimeng', () => {
currentVideoUrl,
// 配置
categories,
functions,
currentFunctions,
// 参数
currentPrompt,
textToImageParams,
imageToImageParams,
imageEditParams,
imageEffectsParams,
textToVideoParams,
imageToVideoParams,
// 计算属性
currentFunction,
needsPrompt,
needsImage,
needsMultipleImages,
currentPowerCost,
activeFunction,
functionParams,
formData,
requiredKeys,
progress,
// 方法
init,
switchCategory,
switchFunction,
switchInputMode,
getCurrentPowerCost,
getFunctionName,
getTaskStatusText,
@@ -569,85 +368,3 @@ export const useJimengStore = defineStore('jimeng', () => {
replaceImg,
}
})
export const imageSizeOptions = [
{ label: '1:1 (1328x1328)', value: '1328x1328' },
{ label: '3:2 (1584x1056)', value: '1584x1056' },
{ label: '2:3 (1056x1584)', value: '1056x1584' },
{ label: '4:3 (1472x1104)', value: '1472x1104' },
{ label: '3:4 (1104x1472)', value: '1104x1472' },
{ label: '16:9 (1664x936)', value: '1664x936' },
{ label: '9:16 (936x1664)', value: '936x1664' },
{ label: '21:9 (2016x864)', value: '2016x864' },
{ label: '9:21 (864x2016)', value: '864x2016' },
]
export const videoAspectRatioOptions = [
{ label: '1:1 (正方形)', value: '1:1' },
{ label: '16:9 (横版)', value: '16:9' },
{ label: '9:16 (竖版)', value: '9:16' },
]
export const imageEffectsTemplateOptions = [
{
label: '毛毡3D拍立得风格',
value: 'felt_3d_polaroid',
preview: '/images/jimeng/templates/felt_3d_polaroid.png',
},
{ label: '像素世界风', value: 'my_world', preview: '/images/jimeng/templates/my_world.png' },
{
label: '像素世界-万物通用版',
value: 'my_world_universal',
preview: '/images/jimeng/templates/my_world_universal.png',
},
{
label: '盲盒玩偶风',
value: 'plastic_bubble_figure',
preview: '/images/jimeng/templates/plastic_bubble_figure.png',
},
{
label: '塑料泡罩人偶-文字卡头版',
value: 'plastic_bubble_figure_cartoon_text',
preview: '/images/jimeng/templates/plastic_bubble_figure_cartoon_text.png',
},
{
label: '毛绒玩偶风',
value: 'furry_dream_doll',
preview: '/images/jimeng/templates/furry_dream_doll.png',
},
{
label: '迷你世界玩偶风',
value: 'micro_landscape_mini_world',
preview: '/images/jimeng/templates/micro_landscape_mini_world.png',
},
{
label: '微型景观小世界-职业版',
value: 'micro_landscape_mini_world_professional',
preview: '/images/jimeng/templates/micro_landscape_mini_world_professional.png',
},
{
label: '亚克力挂饰',
value: 'acrylic_ornaments',
preview: '/images/jimeng/templates/acrylic_ornaments.png',
},
{
label: '毛毡钥匙扣',
value: 'felt_keychain',
preview: '/images/jimeng/templates/felt_keychain.png',
},
{
label: 'Lofi 像素人物小卡',
value: 'lofi_pixel_character_mini_card',
preview: '/images/jimeng/templates/lofi_pixel_character_mini_card.png',
},
{
label: '天使形象手办',
value: 'angel_figurine',
preview: '/images/jimeng/templates/angel_figurine.png',
},
{
label: '躺在毛茸茸肚皮里',
value: 'lying_in_fluffy_belly',
preview: '/images/jimeng/templates/lying_in_fluffy_belly.png',
},
{ label: '玻璃球', value: 'glass_ball', preview: '/images/jimeng/templates/glass_ball.png' },
]

View File

@@ -5,17 +5,16 @@
<!-- 功能分类按钮组 -->
<div class="category-buttons">
<div class="category-grid">
<div
v-for="category in store.categories"
:key="category.key"
:class="['category-btn', { active: store.activeCategory === category.key }]"
@click="store.switchCategory(category.key)"
<button
v-for="f in store.functions"
:key="f.key"
class="category-btn text-base"
:class="{ active: store.activeFunction === f.key }"
@click="store.switchFunction(f)"
>
<div class="category-icon">
<i :class="getCategoryIcon(category.key)"></i>
</div>
<div class="category-name">{{ category.name }}</div>
</div>
<i class="iconfont mr-2 !text-xl" :class="f.icon"></i>
{{ f.name }}
</button>
</div>
</div>
@@ -139,290 +138,17 @@
</div>
<!-- 功能开关 -->
<div
class="function-switch"
v-if="
store.activeCategory === 'image_generation' || store.activeCategory === 'video_generation'
"
>
<div class="switch-label">
<el-icon><Switch /></el-icon>
生成模式
</div>
<div class="switch-container">
<div class="switch-info">
<div class="switch-title">
{{ store.activeCategory === 'image_generation' ? '图生图人像写真' : '图生视频' }}
</div>
</div>
<el-switch v-model="store.useImageInput" @change="store.switchInputMode" />
</div>
</div>
<!-- 参数容器 -->
<div class="params-container">
<!-- 文生图 -->
<div v-if="store.activeFunction === 'text_to_image'" class="function-panel">
<div class="param-line pt">
<span class="label">提示词:</span>
</div>
<div class="param-line">
<el-input
v-model="store.currentPrompt"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="请输入图片描述,越详细越好"
maxlength="2000"
show-word-limit
/>
</div>
<div class="param-line pt">
<span class="label">图片尺寸:</span>
</div>
<div class="param-line">
<el-select v-model="store.textToImageParams.size" placeholder="选择尺寸">
<el-option
v-for="opt in imageSizeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</div>
<div class="param-line">
<span class="label"
>创意度
<el-tooltip content="创意度越高,影响文本描述的程度越高" placement="top">
<i class="iconfont icon-info cursor-pointer ml-1"></i> </el-tooltip
></span>
</div>
<div class="item-group">
<el-slider v-model="store.textToImageParams.scale" :min="1" :max="10" :step="0.5" />
</div>
<div class="item-group flex justify-between">
<span class="label">智能优化提示词</span>
<el-switch v-model="store.textToImageParams.use_pre_llm" />
</div>
</div>
<!-- 图生图 -->
<div v-if="store.activeFunction === 'image_to_image'" class="function-panel">
<div class="param-line pt">
<span class="label">上传图片:</span>
</div>
<div class="param-line">
<ImageUpload
v-model="store.imageToImageParams.image_input"
:max-count="1"
:multiple="false"
/>
</div>
<div class="param-line pt">
<span class="label">提示词:</span>
</div>
<div class="param-line">
<el-input
v-model="store.currentPrompt"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="描述你想要的图片效果"
maxlength="2000"
show-word-limit
/>
</div>
<div class="param-line pt">
<span class="label">图片尺寸:</span>
</div>
<div class="param-line">
<el-select v-model="store.imageToImageParams.size" placeholder="选择尺寸">
<el-option
v-for="opt in imageSizeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</div>
</div>
<!-- 图像编辑 -->
<div v-if="store.activeFunction === 'image_edit'" class="function-panel">
<div class="param-line pt">
<span class="label">上传图片:</span>
</div>
<div class="param-line">
<ImageUpload
v-model="store.imageEditParams.image_input"
:max-count="1"
:multiple="false"
/>
</div>
<div class="param-line pt">
<span class="label">编辑提示词:</span>
</div>
<div class="param-line">
<el-input
v-model="store.currentPrompt"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="描述你想要的编辑效果"
maxlength="2000"
show-word-limit
/>
</div>
<div class="item-group">
<span class="label">编辑强度:</span>
<el-slider v-model="store.imageEditParams.scale" :min="0" :max="1" :step="0.1" />
</div>
</div>
<!-- 图像特效 -->
<div v-if="store.activeFunction === 'image_effects'" class="function-panel">
<div class="param-line pt">
<span class="label">上传图片:</span>
</div>
<div class="param-line">
<ImageUpload
v-model="store.imageEffectsParams.image_input"
:max-count="1"
:multiple="false"
/>
</div>
<div class="param-line pt">
<span class="label">特效模板:</span>
</div>
<div class="param-line">
<el-select
v-model="store.imageEffectsParams.template_id"
placeholder="选择特效模板"
popper-class="jimeng-template-select"
@change="handleTemplateChange($event)"
>
<template #prefix>
<div class="flex items-center py-1">
<el-image
v-if="templatePreview"
:src="templatePreview"
class="w-[50px] h-[50px] object-cover rounded-md"
:preview-src-list="[templatePreview]"
:preview-teleported="true"
@click.stop
/>
</div>
</template>
<el-option
v-for="opt in imageEffectsTemplateOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
>
<div class="flex flex-row justify-between">
<span class="template-label">{{ opt.label }}</span>
<img
v-if="opt.preview"
:src="opt.preview"
:alt="opt.label"
class="w-[50px] h-[50px] object-cover rounded-md"
/>
</div>
</el-option>
</el-select>
</div>
<div class="param-line pt">
<span class="label">输出尺寸:</span>
</div>
<div class="param-line">
<el-select v-model="store.imageEffectsParams.size" placeholder="选择尺寸">
<el-option
v-for="opt in imageSizeOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</div>
</div>
<!-- 文生视频 -->
<div v-if="store.activeFunction === 'text_to_video'" class="function-panel">
<div class="param-line pt">
<span class="label">提示词:</span>
</div>
<div class="param-line">
<el-input
v-model="store.currentPrompt"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="描述你想要的视频内容"
maxlength="2000"
show-word-limit
/>
</div>
<div class="param-line pt">
<span class="label">视频比例:</span>
</div>
<div class="param-line">
<el-select v-model="store.textToVideoParams.aspect_ratio" placeholder="选择比例">
<el-option
v-for="opt in videoAspectRatioOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
</div>
</div>
<!-- 图生视频 -->
<div v-if="store.activeFunction === 'image_to_video'" class="function-panel">
<div class="param-line pt">
<span class="label">上传图片:</span>
</div>
<div class="param-line">
<ImageUpload
v-model="store.imageToVideoParams.image_input"
:max-count="2"
:multiple="true"
/>
</div>
<div class="param-line pt">
<span class="label">提示词:</span>
</div>
<div class="param-line">
<el-input
v-model="store.currentPrompt"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
placeholder="描述你想要的视频效果"
maxlength="2000"
show-word-limit
/>
</div>
<div class="param-line pt">
<span class="label">视频比例:</span>
</div>
<div class="param-line">
<el-select v-model="store.imageToVideoParams.aspect_ratio" placeholder="选择比例">
<el-option
v-for="opt in videoAspectRatioOptions"
:key="opt.value"
:label="opt.label"
:value="opt.value"
/>
</el-select>
<div class="function-params">
<div class="mb-3">
<div class="mb-2">
<label class="label text-left font-bold">模型选择</label>
</div>
<param-builder
v-model="store.formData"
v-model:required-keys="store.requiredKeys"
:items="store.functionParams[store.activeFunction]"
:progress="store.progress[store.activeFunction]"
/>
</div>
<!-- 提交按钮 -->
@@ -660,20 +386,13 @@
</template>
<script setup>
import '@/assets/css/jimeng.scss'
import loadingIcon from '@/assets/img/loading.gif'
import ImageUpload from '@/components/ImageUpload.vue'
import ParamBuilder from '@/components/ParamBuilder.vue'
import Generating from '@/components/ui/Generating.vue'
import {
imageEffectsTemplateOptions,
imageSizeOptions,
useJimengStore,
videoAspectRatioOptions,
} from '@/store/jimeng'
import { useJimengStore } from '@/store/jimeng'
import { useSharedStore } from '@/store/sharedata'
import { dateFormat } from '@/utils/libs'
import { Switch } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { onMounted, onUnmounted, ref, watch } from 'vue'
import { Waterfall } from 'vue-waterfall-plugin-next'
@@ -682,17 +401,6 @@ import 'vue-waterfall-plugin-next/dist/style.css'
const sharedStore = useSharedStore()
const waterfallOptions = sharedStore.waterfallOptions
// 获取分类图标
const getCategoryIcon = (category) => {
const iconMap = {
image_generation: 'iconfont icon-image',
image_editing: 'iconfont icon-edit',
image_effects: 'iconfont icon-chuangzuo',
video_generation: 'iconfont icon-video',
}
return iconMap[category] || 'iconfont icon-image'
}
const store = useJimengStore()
// 新增:瀑布流渲染完成状态
@@ -730,13 +438,6 @@ watch(
}
)
function handleTemplateChange(value) {
templatePreview.value = imageEffectsTemplateOptions.find((opt) => opt.value === value)?.preview
store.imageEffectsParams.prompt = imageEffectsTemplateOptions.find(
(opt) => opt.value === value
)?.label
}
function onWaterfallAfterRender() {
waterfallRendered.value = true
if (!store.loading && !store.isOver) {
@@ -768,150 +469,5 @@ function copyErrorMsg(msg) {
</script>
<style lang="scss" scoped>
.task-list {
.task-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 20px;
padding: 10px 0;
}
// 新增:增强任务项悬停动画
.task-item {
transition: box-shadow 3s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.5s cubic-bezier(0.4, 0, 0.2, 1), border-color 0.5s;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
border: 1.5px solid transparent;
border-radius: 12px;
background: #fff;
position: relative;
z-index: 1;
}
.task-item:hover {
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18), 0 1.5px 8px rgba(0, 0, 0, 0.1);
border-color: #a259ff;
transform: scale(1.025) translateY(-2px);
z-index: 10;
}
}
@media (max-width: 1200px) {
.task-list .task-grid {
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
}
@media (max-width: 768px) {
.task-list .task-grid {
grid-template-columns: 1fr;
}
}
.preview-video-wrapper {
position: relative;
width: 100%;
height: 100%;
.video-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.2s;
z-index: 2;
}
&:hover .video-mask {
opacity: 1;
}
.play-btn {
width: 64px;
height: 64px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
cursor: pointer;
z-index: 3;
transition: background 0.2s;
&:hover {
background: rgba(255, 255, 255, 0.4);
}
img {
width: 36px;
height: 36px;
}
}
}
.err-msg-clip {
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: normal;
}
.jimeng-template-select {
.el-select-dropdown__item {
height: 60px;
line-height: 60px;
}
}
// 新增:提示词指南样式
.prompt-guide {
margin: 12px 0 16px;
.guide-title {
display: flex;
align-items: center;
font-weight: 600;
color: #666;
}
.guide-content {
max-height: 220px;
overflow: auto;
line-height: 1.6;
font-size: 12px;
color: #555;
padding-right: 4px;
}
.guide-section {
margin-bottom: 10px;
}
.guide-subtitle {
font-weight: 600;
margin-bottom: 6px;
color: #333;
}
ul {
list-style: disc;
padding-left: 18px;
margin: 4px 0;
}
.quote {
margin: 8px 0;
padding: 8px 10px;
border-left: 3px solid #a3a3a3;
background: #f8f8f8;
border-radius: 4px;
color: #444;
}
}
@use '@/assets/css/jimeng.scss' as *;
</style>

View File

@@ -56,134 +56,55 @@
<h3 class="heading-3 mb-3">算力配置</h3>
<el-form-item>
<template #label>
<div class="label-title">
文生图算力
<el-tooltip
effect="dark"
content="用户使用文生图功能时消耗的算力"
raw-content
placement="right"
<div class="text-gray-500 text-sm">
生成图片消耗的积分包括文生图图生图图片编辑图片特效<span
class="text-red-500"
>单位积分/</span
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.text_to_image"
v-model="jimengConfig.power.image"
:min="1"
placeholder="请输入文生图算力消耗"
placeholder="请输入图片生成算力消耗"
/>
</el-form-item>
<el-form-item>
<template #label>
<div class="label-title">
图生图算力
<el-tooltip
effect="dark"
content="用户使用图生图功能时消耗的算力"
raw-content
placement="right"
<div class="text-gray-500 text-sm">
生成视频消耗的积分包括文生视频图生视频<span class="text-red-500"
>单位积分/</span
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.image_to_image"
v-model="jimengConfig.power.video"
:min="1"
placeholder="请输入图生图算力消耗"
placeholder="请输入视频生成算力消耗"
/>
</el-form-item>
<el-form-item>
<template #label>
<div class="label-title">
图片编辑算力
<el-tooltip
effect="dark"
content="用户使用图片编辑功能时消耗的算力"
raw-content
placement="right"
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
<div class="text-gray-500 text-sm">
生成数字人视频消耗的积分<span class="text-red-500">单位积分/</span>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.image_edit"
v-model="jimengConfig.power.virtual_human"
:min="1"
placeholder="请输入图片编辑算力消耗"
placeholder="请输入数字人视频生成算力消耗"
/>
</el-form-item>
<el-form-item>
<template #label>
<div class="label-title">
图片特效算力
<el-tooltip
effect="dark"
content="用户使用图片特效功能时消耗的算力"
raw-content
placement="right"
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
<div class="text-gray-500 text-sm">
生成视频动作迁移消耗的积分<span class="text-red-500">单位积分/</span>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.image_effects"
v-model="jimengConfig.power.action_transfer"
:min="1"
placeholder="请输入图片特效算力消耗"
/>
</el-form-item>
<el-form-item>
<template #label>
<div class="label-title">
文生视频算力
<el-tooltip
effect="dark"
content="用户使用文生视频功能时消耗的算力"
raw-content
placement="right"
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.text_to_video"
:min="1"
placeholder="请输入文生视频算力消耗"
/>
</el-form-item>
<el-form-item>
<template #label>
<div class="label-title">
图生视频算力
<el-tooltip
effect="dark"
content="用户使用图生视频功能时消耗的算力"
raw-content
placement="right"
>
<el-icon>
<InfoFilled />
</el-icon>
</el-tooltip>
</div>
</template>
<el-input-number
v-model="jimengConfig.power.image_to_video"
:min="1"
placeholder="请输入图生视频算力消耗"
placeholder="请输入视频动作迁移算力消耗"
/>
</el-form-item>
</div>
@@ -201,7 +122,6 @@
<script setup>
import Alert from '@/components/ui/Alert.vue'
import { httpGet, httpPost } from '@/utils/http'
import { InfoFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { onMounted, ref } from 'vue'

View File

@@ -41,7 +41,7 @@
<script setup>
import ParamBuilder from '@/components/ParamBuilder.vue'
import { JimengFunctions, JimengParams } from '@/store/data'
import { JimengFunctions, JimengParams } from '@/store/data/jimeng_data'
import { ref } from 'vue'
const functions = JimengFunctions