mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-12 06:04:26 +08:00
手机端视频生成页面调整完成
This commit is contained in:
@@ -334,6 +334,12 @@ func (h *JimengHandler) Remove(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 正在运行中的任务不能删除
|
||||
if job.Status == model.JMTaskStatusGenerating || job.Status == model.JMTaskStatusInQueue {
|
||||
resp.ERROR(c, "正在运行中的任务不能删除,否则无法退回算力")
|
||||
return
|
||||
}
|
||||
|
||||
tx := h.DB.Begin()
|
||||
if err := tx.Where("id = ? AND user_id = ?", jobId, user.Id).Delete(&model.JimengJob{}).Error; err != nil {
|
||||
logger.Errorf("delete jimeng job failed: %v", err)
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
@font-face {
|
||||
font-family: "OPlusSans3-Regular";
|
||||
src: url("../fonts/OPlusSans3-Regular.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "OPlusSans3-Medium";
|
||||
src: url("../fonts/OPlusSans3-Medium.ttf") format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
$font-regular: "OPlusSans3-Regular", "PingFangSC-Regular", "Roboto", "sans-serif";
|
||||
$font-medium: "OPlusSans3-Medium", "PingFangSC-Medium", "Roboto", "sans-serif";
|
||||
$font-regular: 'PingFangSC-Regular', 'Roboto', 'sans-serif', 'ui-sans-serif', '-apple-system',
|
||||
'system-ui', 'Segoe UI', 'Helvetica', 'Apple Color Emoji', 'Arial', 'sans-serif', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol';
|
||||
$font-medium: 'PingFangSC-Medium', 'Roboto', 'sans-serif', 'ui-sans-serif', '-apple-system',
|
||||
'system-ui', 'Segoe UI', 'Helvetica', 'Apple Color Emoji', 'Arial', 'sans-serif', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol';
|
||||
|
||||
.font-regular {
|
||||
font-family: $font-regular;
|
||||
@@ -21,4 +13,4 @@ $font-medium: "OPlusSans3-Medium", "PingFangSC-Medium", "Roboto", "sans-serif";
|
||||
.font-medium {
|
||||
font-family: $font-medium;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.mobile-mj {
|
||||
.text-line {
|
||||
padding: 6px;
|
||||
padding: 6px 0;
|
||||
font-size: 14px;
|
||||
|
||||
.van-row {
|
||||
@@ -91,7 +91,8 @@
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
.van-image, .task-in-queue {
|
||||
.van-image,
|
||||
.task-in-queue {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
@@ -213,4 +214,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.mobile-sd {
|
||||
.text-line {
|
||||
padding: 0 6px;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
|
||||
.van-row {
|
||||
@@ -98,7 +98,8 @@
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
.van-image, .task-in-queue {
|
||||
.van-image,
|
||||
.task-in-queue {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
@@ -201,4 +202,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,40 +498,10 @@
|
||||
.custom-upload {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-upload-dragger) {
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 2px dashed var(--el-border-color);
|
||||
border-radius: 12px;
|
||||
background: var(--el-bg-color);
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
|
||||
}
|
||||
|
||||
&.is-dragover {
|
||||
border-color: var(--el-color-primary);
|
||||
background: var(--el-color-primary-light-9);
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
background: var(--el-color-primary) !important;
|
||||
border-color: var(--el-color-primary) !important;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-color-primary-dark-2) !important;
|
||||
border-color: var(--el-color-primary-dark-2) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
@use 'font.scss' as *;
|
||||
:root[data-theme="light"] {
|
||||
:root[data-theme='light'] {
|
||||
--text-fb: #000;
|
||||
--text-color: #5b62ce; // 主要的文本颜色
|
||||
--normal-color: rgba(43, 54, 116, 1); // 普通颜色
|
||||
--theme-textcolor-normal: #5b62ce;
|
||||
p, h1, h2, h3, h4, h5, h6, article {
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
article {
|
||||
font-family: $font-regular;
|
||||
}
|
||||
html,
|
||||
@@ -55,4 +62,4 @@
|
||||
--quote-text-color: #333;
|
||||
// 面板背景
|
||||
--panel-bg: linear-gradient(135deg, #f5eafe 0%, #e9e6fc 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4125778 */
|
||||
src: url('iconfont.woff2?t=1752831319382') format('woff2'),
|
||||
url('iconfont.woff?t=1752831319382') format('woff'),
|
||||
url('iconfont.ttf?t=1752831319382') format('truetype');
|
||||
src: url('iconfont.woff2?t=1754626711656') format('woff2'),
|
||||
url('iconfont.woff?t=1754626711656') format('woff'),
|
||||
url('iconfont.ttf?t=1754626711656') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,26 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-back-bold:before {
|
||||
content: "\e654";
|
||||
}
|
||||
|
||||
.icon-back-circle:before {
|
||||
content: "\e653";
|
||||
}
|
||||
|
||||
.icon-back:before {
|
||||
content: "\e6c8";
|
||||
}
|
||||
|
||||
.icon-openai:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.icon-suanli:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
.icon-jimeng2:before {
|
||||
content: "\eabc";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,41 @@
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "27025075",
|
||||
"name": "返回",
|
||||
"font_class": "back-bold",
|
||||
"unicode": "e654",
|
||||
"unicode_decimal": 58964
|
||||
},
|
||||
{
|
||||
"icon_id": "6237605",
|
||||
"name": "返回",
|
||||
"font_class": "back-circle",
|
||||
"unicode": "e653",
|
||||
"unicode_decimal": 58963
|
||||
},
|
||||
{
|
||||
"icon_id": "18952076",
|
||||
"name": "返回",
|
||||
"font_class": "back",
|
||||
"unicode": "e6c8",
|
||||
"unicode_decimal": 59080
|
||||
},
|
||||
{
|
||||
"icon_id": "33483666",
|
||||
"name": "openai",
|
||||
"font_class": "openai",
|
||||
"unicode": "e652",
|
||||
"unicode_decimal": 58962
|
||||
},
|
||||
{
|
||||
"icon_id": "25677845",
|
||||
"name": "算力",
|
||||
"font_class": "suanli",
|
||||
"unicode": "e651",
|
||||
"unicode_decimal": 58961
|
||||
},
|
||||
{
|
||||
"icon_id": "42693930",
|
||||
"name": "即梦AI-02",
|
||||
@@ -434,7 +469,7 @@
|
||||
},
|
||||
{
|
||||
"icon_id": "39584617",
|
||||
"name": "MidJourney-copy",
|
||||
"name": "MidJourney",
|
||||
"font_class": "MidJourney",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import { closeLoading, showLoading, showMessageError, showMessageOK } from '@/utils/dialog'
|
||||
import { httpGet, httpPost } from '@/utils/http'
|
||||
import { showMessageError, showMessageOK, showLoading, closeLoading } from '@/utils/dialog'
|
||||
import { defineStore } from 'pinia'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export const useJimengStore = defineStore('mobile-jimeng', () => {
|
||||
// 响应式数据
|
||||
@@ -209,7 +209,7 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
||||
}
|
||||
listLoading.value = true
|
||||
|
||||
return httpGet('/api/jimeng/list', { page: page.value, page_size: pageSize.value })
|
||||
return httpPost('/api/jimeng/jobs', { page: page.value, page_size: pageSize.value })
|
||||
.then((res) => {
|
||||
total.value = res.data.total
|
||||
let needPull = false
|
||||
|
||||
@@ -441,8 +441,6 @@ export const useVideoStore = defineStore('video', () => {
|
||||
}
|
||||
|
||||
isGenerating.value = true
|
||||
showLoading('正在生成视频脚本...')
|
||||
|
||||
try {
|
||||
const res = await httpPost('/api/prompt/video', { prompt })
|
||||
if (activeVideoType.value === 'luma') {
|
||||
@@ -450,10 +448,8 @@ export const useVideoStore = defineStore('video', () => {
|
||||
} else {
|
||||
kelingParams.prompt = res.data
|
||||
}
|
||||
closeLoading()
|
||||
} catch (error) {
|
||||
showMessageError('生成提示词失败:' + error.message)
|
||||
closeLoading()
|
||||
} finally {
|
||||
isGenerating.value = false
|
||||
}
|
||||
|
||||
@@ -308,15 +308,15 @@
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<div class="submit-btn flex justify-center pt-4">
|
||||
<el-button
|
||||
type="primary"
|
||||
<button
|
||||
@click="store.submitTask"
|
||||
:loading="store.submitting"
|
||||
class="w-full"
|
||||
size="large"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2 text-base"
|
||||
>
|
||||
立即生成 ({{ store.currentPowerCost }} <i class="iconfont icon-vip2 !text-xs"></i>)
|
||||
</el-button>
|
||||
<i v-if="store.submitting" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>立即生成 ({{ store.currentPowerCost }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -201,13 +201,15 @@
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<div class="setting-card">
|
||||
<button @click="store.create" :disabled="store.loading" class="create-btn">
|
||||
<button
|
||||
@click="store.create"
|
||||
:disabled="store.loading"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="store.loading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span
|
||||
>{{ store.loading ? '创作中...' : store.btnText }} ({{ store.sunoPower }}
|
||||
<i class="iconfont icon-vip2 !text-xs"></i>
|
||||
)</span
|
||||
>{{ store.loading ? '创作中...' : store.btnText }} ({{ store.sunoPower }}算力)</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
@@ -233,7 +235,6 @@
|
||||
<!-- 上传区域 -->
|
||||
<el-upload
|
||||
class="custom-upload"
|
||||
drag
|
||||
:auto-upload="true"
|
||||
:show-file-list="false"
|
||||
:http-request="store.uploadAudio"
|
||||
@@ -241,11 +242,13 @@
|
||||
:limit="1"
|
||||
>
|
||||
<template #trigger>
|
||||
<div class="p-2">
|
||||
<el-button class="upload-btn" size="large" type="primary">
|
||||
<div class="w-full py-2">
|
||||
<button
|
||||
class="w-full py-3 bg-gradient-to-r from-orange-300 to-purple-500 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-orange-300 hover:to-red-500 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i class="iconfont icon-upload mr-2"></i>
|
||||
<span>上传音乐</span>
|
||||
</el-button>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
@@ -22,17 +22,13 @@
|
||||
</div>
|
||||
|
||||
<!-- 提示词生成按钮 -->
|
||||
<div class="param-line pt">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
@click="store.generatePrompt"
|
||||
:loading="store.isGenerating"
|
||||
size="small"
|
||||
color="#5865f2"
|
||||
style="width: 100%"
|
||||
>
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
生成AI视频提示词
|
||||
<div class="flex justify-end pt-1">
|
||||
<el-button @click="store.generatePrompt" type="primary" :loading="store.isGenerating">
|
||||
<span v-if="!store.isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
生成提示词
|
||||
</span>
|
||||
<span v-else>生成中...</span>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -126,16 +122,28 @@
|
||||
</div>
|
||||
|
||||
<!-- 算力显示 -->
|
||||
<el-row class="text-info">
|
||||
<el-text type="primary"
|
||||
>当前可用算力:<el-text type="warning">{{ store.availablePower }}</el-text></el-text
|
||||
<div
|
||||
class="power-info flex items-center justify-between mb-4 mt-3 p-3 rounded-lg bg-gradient-to-r from-blue-50 to-purple-50 border border-blue-200 shadow-sm"
|
||||
>
|
||||
<div class="flex items-center space-x-2">
|
||||
<el-icon color="#f59e42" size="20"><i class="iconfont icon-lightning"></i></el-icon>
|
||||
<span class="font-medium text-gray-700">当前可用算力:</span>
|
||||
<span class="font-bold text-lg text-yellow-500">{{ store.availablePower }}</span>
|
||||
</div>
|
||||
<el-tooltip content="算力用于生成视频,每次生成会消耗对应算力" placement="left">
|
||||
<el-icon color="#a78bfa" size="18"><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
@click="store.createLumaVideo"
|
||||
:loading="store.generating"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2 text-base"
|
||||
>
|
||||
</el-row>
|
||||
<!-- 生成按钮 -->
|
||||
<div class="submit-btn">
|
||||
<el-button type="primary" :dark="false" @click="store.createLumaVideo" round>
|
||||
立即生成 ({{ store.lumaPowerCost }}<i class="iconfont icon-vip2"></i>)
|
||||
</el-button>
|
||||
<i v-if="store.generating" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>立即生成 ({{ store.lumaPowerCost }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
@@ -398,17 +406,17 @@
|
||||
</div>
|
||||
|
||||
<!-- 提示词生成按钮 -->
|
||||
<div class="param-line pt">
|
||||
<div class="flex justify-end">
|
||||
<el-button
|
||||
class="generate-btn"
|
||||
@click="store.generatePrompt"
|
||||
:loading="store.isGenerating"
|
||||
size="small"
|
||||
color="#5865f2"
|
||||
style="width: 100%"
|
||||
>
|
||||
<i class="iconfont icon-chuangzuo"></i>
|
||||
生成专业视频提示词
|
||||
<span v-if="!store.isGenerating">
|
||||
<i class="iconfont icon-chuangzuo"></i> 生成提示词
|
||||
</span>
|
||||
<span v-else>生成中...</span>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -429,23 +437,31 @@
|
||||
</div>
|
||||
|
||||
<!-- 算力显示 -->
|
||||
<el-row class="text-info">
|
||||
<el-text type="primary"
|
||||
>当前可用算力:<el-text type="warning">{{ store.availablePower }}</el-text></el-text
|
||||
>
|
||||
</el-row>
|
||||
<!-- 算力显示 -->
|
||||
<div
|
||||
class="power-info flex items-center justify-between mb-4 mt-2 p-3 rounded-lg bg-gradient-to-r from-blue-50 to-purple-50 border border-blue-200 shadow-sm"
|
||||
>
|
||||
<div class="flex items-center space-x-2">
|
||||
<el-icon color="#f59e42" size="20"><i class="iconfont icon-lightning"></i></el-icon>
|
||||
<span class="font-medium text-gray-700">当前可用算力:</span>
|
||||
<span class="font-bold text-lg text-yellow-500">{{ store.availablePower }}</span>
|
||||
</div>
|
||||
<el-tooltip content="算力用于生成视频,每次生成会消耗对应算力" placement="left">
|
||||
<el-icon color="#a78bfa" size="18"><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<div class="submit-btn">
|
||||
<el-button
|
||||
type="primary"
|
||||
:dark="false"
|
||||
<div class="flex justify-center">
|
||||
<button
|
||||
@click="store.createKelingVideo"
|
||||
round
|
||||
:loading="store.generating"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2 text-base"
|
||||
>
|
||||
立即生成 ({{ store.kelingPowerCost }}<i class="iconfont icon-vip2"></i>)
|
||||
</el-button>
|
||||
<i v-if="store.generating" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>立即生成 ({{ store.kelingPowerCost }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
@@ -599,12 +615,11 @@
|
||||
</div>
|
||||
|
||||
<!-- 视频预览对话框 -->
|
||||
<black-dialog
|
||||
:show="store.showDialog"
|
||||
<el-dialog
|
||||
v-model="store.showDialog"
|
||||
title="预览视频"
|
||||
hide-footer
|
||||
@cancal="store.showDialog = false"
|
||||
@update:show="store.showDialog = $event"
|
||||
@close="store.showDialog = false"
|
||||
width="auto"
|
||||
>
|
||||
<video
|
||||
@@ -614,16 +629,14 @@
|
||||
:autoplay="true"
|
||||
loop="loop"
|
||||
muted="muted"
|
||||
v-show="store.showDialog"
|
||||
>
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
</black-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import BlackDialog from '@/components/ui/BlackDialog.vue'
|
||||
import Generating from '@/components/ui/Generating.vue'
|
||||
import { useVideoStore } from '@/store/video'
|
||||
import { CircleCloseFilled, InfoFilled, Plus } from '@element-plus/icons-vue'
|
||||
|
||||
@@ -73,15 +73,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AppCard from '@/components/mobile/AppCard.vue'
|
||||
import EmptyState from '@/components/mobile/EmptyState.vue'
|
||||
import CustomTabPane from '@/components/ui/CustomTabPane.vue'
|
||||
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
||||
import AppCard from './components/AppCard.vue'
|
||||
import EmptyState from './components/EmptyState.vue'
|
||||
import { checkSession } from '@/store/cache'
|
||||
import { httpGet, httpPost } from '@/utils/http'
|
||||
import { arrayContains, removeArrayItem, showLoginDialog, substr } from '@/utils/libs'
|
||||
import { showNotify } from 'vant'
|
||||
import { onMounted, ref, computed, nextTick } from 'vue'
|
||||
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -6,36 +6,33 @@
|
||||
@update:model-value="activeTab = $event"
|
||||
@tab-click="onTabChange"
|
||||
>
|
||||
<CustomTabPane name="mj" label="MJ绘画">
|
||||
<CustomTabPane name="mj">
|
||||
<template #label>
|
||||
<i class="iconfont icon-mj mr-1"></i>
|
||||
<span>Midjourney</span>
|
||||
</template>
|
||||
<div class="tab-content">
|
||||
<image-mj />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
<CustomTabPane name="sd" label="SD绘画">
|
||||
<CustomTabPane name="sd">
|
||||
<template #label>
|
||||
<i class="iconfont icon-sd mr-1"></i>
|
||||
<span>StableDiffusion</span>
|
||||
</template>
|
||||
<div class="tab-content">
|
||||
<image-sd />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
<CustomTabPane name="dalle" label="DALL·E">
|
||||
<CustomTabPane name="dalle">
|
||||
<template #label>
|
||||
<i class="iconfont icon-dalle mr-1"></i>
|
||||
<span>Dalle</span>
|
||||
</template>
|
||||
<div class="tab-content">
|
||||
<image-dall />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
<CustomTabPane name="suno" label="音乐创作">
|
||||
<div class="tab-content">
|
||||
<suno-create />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
<CustomTabPane name="video" label="视频生成">
|
||||
<div class="tab-content">
|
||||
<video-create />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
<CustomTabPane name="jimeng" label="即梦AI">
|
||||
<div class="tab-content">
|
||||
<jimeng-create />
|
||||
</div>
|
||||
</CustomTabPane>
|
||||
</CustomTabs>
|
||||
</div>
|
||||
</div>
|
||||
@@ -44,16 +41,12 @@
|
||||
<script setup>
|
||||
import CustomTabPane from '@/components/ui/CustomTabPane.vue'
|
||||
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
||||
import { httpGet } from '@/utils/http'
|
||||
import ImageDall from '@/views/mobile/pages/ImageDall.vue'
|
||||
import ImageMj from '@/views/mobile/pages/ImageMj.vue'
|
||||
import ImageSd from '@/views/mobile/pages/ImageSd.vue'
|
||||
import { Button, Field, Image, showNotify } from 'vant'
|
||||
import { h, onMounted, ref, watch } from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
// 删除 SunoCreate、VideoCreate、JimengCreate 相关 setup 代码和渲染逻辑
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const activeTab = ref(route.query.tab || 'mj')
|
||||
@@ -62,22 +55,8 @@ const activeMenu = ref({
|
||||
mj: false,
|
||||
sd: false,
|
||||
dall: false,
|
||||
suno: false,
|
||||
video: false,
|
||||
jimeng: false,
|
||||
})
|
||||
|
||||
// 监听路由参数变化
|
||||
watch(
|
||||
() => route.query.tab,
|
||||
(newTab) => {
|
||||
if (newTab && activeMenu.value[newTab]) {
|
||||
activeTab.value = newTab
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// Tab切换处理
|
||||
const onTabChange = (name) => {
|
||||
router.replace({
|
||||
@@ -85,48 +64,6 @@ const onTabChange = (name) => {
|
||||
query: { ...route.query, tab: name },
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchMenus()
|
||||
})
|
||||
|
||||
const fetchMenus = () => {
|
||||
httpGet('/api/menu/list')
|
||||
.then((res) => {
|
||||
menus.value = res.data
|
||||
activeMenu.value = {
|
||||
mj: menus.value.some((item) => item.url === '/mj'),
|
||||
sd: menus.value.some((item) => item.url === '/sd'),
|
||||
dall: menus.value.some((item) => item.url === '/dalle'),
|
||||
suno: menus.value.some((item) => item.url === '/suno'),
|
||||
video: menus.value.some((item) => item.url === '/video'),
|
||||
jimeng: menus.value.some((item) => item.url === '/jimeng'),
|
||||
}
|
||||
|
||||
// 如果没有指定tab,默认选择第一个可用的
|
||||
if (!route.query.tab) {
|
||||
const firstAvailable = Object.keys(activeMenu.value).find((key) => activeMenu.value[key])
|
||||
if (firstAvailable) {
|
||||
activeTab.value = firstAvailable
|
||||
}
|
||||
} else {
|
||||
// 如果当前选中的tab不可用,选择第一个可用的
|
||||
if (!activeMenu.value[route.query.tab]) {
|
||||
const firstAvailable = Object.keys(activeMenu.value).find((key) => activeMenu.value[key])
|
||||
if (firstAvailable) {
|
||||
activeTab.value = firstAvailable
|
||||
router.replace({
|
||||
path: route.path,
|
||||
query: { ...route.query, tab: firstAvailable },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error('获取菜单失败:', e.message)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
<template>
|
||||
<div class="jimeng-create">
|
||||
<!-- 页面头部 -->
|
||||
<div class="jimeng-create__header">
|
||||
<div class="jimeng-create__header-content">
|
||||
<button @click="goBack" class="jimeng-create__header-back-btn">
|
||||
<i class="iconfont icon-back"></i>
|
||||
<div class="sticky top-0 z-40 bg-white shadow-sm">
|
||||
<div class="flex items-center px-4 h-14">
|
||||
<button
|
||||
@click="goBack"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-full hover:bg-gray-100 transition-colors"
|
||||
>
|
||||
<i class="iconfont icon-back text-gray-600"></i>
|
||||
</button>
|
||||
<h1 class="jimeng-create__header-title">即梦AI</h1>
|
||||
<div class="jimeng-create__header-spacer"></div>
|
||||
<h1 class="flex-1 text-center text-lg text-gray-900">即梦AI</h1>
|
||||
<div class="w-8"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +21,7 @@
|
||||
v-model="jimengStore.activeCategory"
|
||||
@update:modelValue="jimengStore.switchCategory"
|
||||
>
|
||||
<!-- 文生图 -->
|
||||
<CustomTabPane
|
||||
v-for="category in jimengStore.categories"
|
||||
:key="category.key"
|
||||
@@ -27,422 +31,512 @@
|
||||
<template #label>
|
||||
<span>{{ category.name }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 生成模式切换 -->
|
||||
<div class="jimeng-create__mode-section">
|
||||
<div class="jimeng-create__mode-section-content">
|
||||
<div>
|
||||
<span class="jimeng-create__mode-section-title">生成模式</span>
|
||||
<p class="jimeng-create__mode-section-description">图生图人像写真</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="jimengStore.useImageInput"
|
||||
@change="jimengStore.switchInputMode"
|
||||
size="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="请输入图片描述,越详细越好"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.textToImageParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="图片尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
|
||||
<!-- 创意度 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__slider-section">
|
||||
<div class="jimeng-create__slider-section-header">
|
||||
<label>创意度</label>
|
||||
<el-tooltip content="创意度越高,影响文本描述的程度越高" placement="top">
|
||||
<i class="iconfont icon-info"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-slider
|
||||
v-model="jimengStore.textToImageParams.scale"
|
||||
:min="1"
|
||||
:max="10"
|
||||
:step="0.5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 智能优化提示词 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__switch-section">
|
||||
<span>智能优化提示词</span>
|
||||
<el-switch v-model="jimengStore.textToImageParams.use_pre_llm" size="default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图生图参数 -->
|
||||
<template v-if="jimengStore.activeFunction === 'image_to_image'">
|
||||
<!-- 生成模式切换 -->
|
||||
<div class="jimeng-create__mode-section">
|
||||
<div class="jimeng-create__mode-section-content">
|
||||
<div>
|
||||
<span class="jimeng-create__mode-section-title">生成模式</span>
|
||||
<p class="jimeng-create__mode-section-description">图生图人像写真</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="jimengStore.useImageInput"
|
||||
@change="jimengStore.switchInputMode"
|
||||
size="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageToImageInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div
|
||||
@click="$refs.imageToImageInput?.click()"
|
||||
class="jimeng-create__upload-content"
|
||||
>
|
||||
<i
|
||||
v-if="!jimengStore.imageToImageParams.image_input.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageToImageParams.image_input.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageToImageParams.image_input[0]?.url ||
|
||||
jimengStore.imageToImageParams.image_input[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageToImageParams.image_input = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的图片效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageToImageParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="图片尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 图像编辑参数 -->
|
||||
<template
|
||||
v-if="
|
||||
category.key === 'image_generation' && jimengStore.activeFunction === 'image_edit'
|
||||
"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageEditInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div
|
||||
@click="$refs.imageEditInput?.click()"
|
||||
class="jimeng-create__upload-content"
|
||||
>
|
||||
<i
|
||||
v-if="!jimengStore.imageEditParams.image_urls.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageEditParams.image_urls.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageEditParams.image_urls[0]?.url ||
|
||||
jimengStore.imageEditParams.image_urls[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageEditParams.image_urls = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑提示词 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">编辑提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的编辑效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑强度 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__slider-section">
|
||||
<label>编辑强度</label>
|
||||
<el-slider
|
||||
v-model="jimengStore.imageEditParams.scale"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 图像特效参数 -->
|
||||
<template
|
||||
v-if="
|
||||
category.key === 'image_generation' &&
|
||||
jimengStore.activeFunction === 'image_effects'
|
||||
"
|
||||
>
|
||||
<div class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageEffectsInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div
|
||||
@click="$refs.imageEffectsInput?.click()"
|
||||
class="jimeng-create__upload-content"
|
||||
>
|
||||
<i
|
||||
v-if="!jimengStore.imageEffectsParams.image_input1.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageEffectsParams.image_input1.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageEffectsParams.image_input1[0]?.url ||
|
||||
jimengStore.imageEffectsParams.image_input1[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageEffectsParams.image_input1 = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 特效模板 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageEffectsParams.template_id"
|
||||
:options="
|
||||
jimengStore.imageEffectsTemplateOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="特效模板"
|
||||
title="选择特效模板"
|
||||
/>
|
||||
|
||||
<!-- 输出尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageEffectsParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="输出尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 文生视频参数 -->
|
||||
<template
|
||||
v-if="
|
||||
category.key === 'video_generation' &&
|
||||
jimengStore.activeFunction === 'text_to_video'
|
||||
"
|
||||
>
|
||||
<!-- 生成模式切换 -->
|
||||
<div class="jimeng-create__mode-section">
|
||||
<div class="jimeng-create__mode-section-content">
|
||||
<div>
|
||||
<span class="jimeng-create__mode-section-title">生成模式</span>
|
||||
<p class="jimeng-create__mode-section-description">图生视频</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="jimengStore.useImageInput"
|
||||
@change="jimengStore.switchInputMode"
|
||||
size="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的视频内容"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频比例 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.textToVideoParams.aspect_ratio"
|
||||
:options="
|
||||
jimengStore.videoAspectRatioOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="视频比例"
|
||||
title="选择比例"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 图生视频参数 -->
|
||||
<template
|
||||
v-if="
|
||||
category.key === 'video_generation' &&
|
||||
jimengStore.activeFunction === 'image_to_video'
|
||||
"
|
||||
>
|
||||
<!-- 生成模式切换 -->
|
||||
<div class="jimeng-create__mode-section">
|
||||
<div class="jimeng-create__mode-section-content">
|
||||
<div>
|
||||
<span class="jimeng-create__mode-section-title">生成模式</span>
|
||||
<p class="jimeng-create__mode-section-description">图生视频</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="jimengStore.useImageInput"
|
||||
@change="jimengStore.switchInputMode"
|
||||
size="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片(最多2张)</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageToVideoInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
multiple
|
||||
@change="(e) => jimengStore.handleMultipleImageUpload(e)"
|
||||
class="hidden"
|
||||
/>
|
||||
<div
|
||||
@click="$refs.imageToVideoInput?.click()"
|
||||
class="jimeng-create__upload-content"
|
||||
>
|
||||
<i
|
||||
v-if="!jimengStore.imageToVideoParams.image_urls.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageToVideoParams.image_urls.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-multiple">
|
||||
<div
|
||||
v-for="(image, index) in jimengStore.imageToVideoParams.image_urls"
|
||||
:key="index"
|
||||
class="jimeng-create__upload-multiple-item"
|
||||
>
|
||||
<el-image
|
||||
:src="image?.url || image?.content"
|
||||
fit="cover"
|
||||
class="w-24 h-24 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.removeImage(index)"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="jimengStore.imageToVideoParams.image_urls.length < 2"
|
||||
@click.stop="$refs.imageToVideoInput?.click()"
|
||||
class="jimeng-create__upload-multiple-add"
|
||||
>
|
||||
<i class="iconfont icon-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的视频效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频比例 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageToVideoParams.aspect_ratio"
|
||||
:options="
|
||||
jimengStore.videoAspectRatioOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="视频比例"
|
||||
title="选择比例"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</CustomTabPane>
|
||||
</CustomTabs>
|
||||
</div>
|
||||
<!-- 生成模式切换 -->
|
||||
<div
|
||||
v-if="
|
||||
jimengStore.activeCategory === 'image_generation' ||
|
||||
jimengStore.activeCategory === 'video_generation'
|
||||
"
|
||||
class="jimeng-create__mode-section"
|
||||
>
|
||||
<div class="jimeng-create__mode-section-content">
|
||||
<div>
|
||||
<span class="jimeng-create__mode-section-title">生成模式</span>
|
||||
<p class="jimeng-create__mode-section-description">
|
||||
{{
|
||||
jimengStore.activeCategory === 'image_generation' ? '图生图人像写真' : '图生视频'
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<el-switch
|
||||
v-model="jimengStore.useImageInput"
|
||||
@change="jimengStore.switchInputMode"
|
||||
size="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文生图 -->
|
||||
<div v-if="jimengStore.activeFunction === 'text_to_image'" class="space-y-6">
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="请输入图片描述,越详细越好"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.textToImageParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({ label: opt.label, value: opt.value }))
|
||||
"
|
||||
label="图片尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
|
||||
<!-- 创意度 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__slider-section">
|
||||
<div class="jimeng-create__slider-section-header">
|
||||
<label>创意度</label>
|
||||
<el-tooltip content="创意度越高,影响文本描述的程度越高" placement="top">
|
||||
<i class="iconfont icon-info"></i>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-slider
|
||||
v-model="jimengStore.textToImageParams.scale"
|
||||
:min="1"
|
||||
:max="10"
|
||||
:step="0.5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 智能优化提示词 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__switch-section">
|
||||
<span>智能优化提示词</span>
|
||||
<el-switch v-model="jimengStore.textToImageParams.use_pre_llm" size="default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图生图 -->
|
||||
<div v-if="jimengStore.activeFunction === 'image_to_image'" class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageToImageInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div @click="$refs.imageToImageInput?.click()" class="jimeng-create__upload-content">
|
||||
<i
|
||||
v-if="!jimengStore.imageToImageParams.image_input.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageToImageParams.image_input.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageToImageParams.image_input[0]?.url ||
|
||||
jimengStore.imageToImageParams.image_input[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageToImageParams.image_input = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的图片效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图片尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageToImageParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({ label: opt.label, value: opt.value }))
|
||||
"
|
||||
label="图片尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 图像编辑 -->
|
||||
<div v-if="jimengStore.activeFunction === 'image_edit'" class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageEditInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div @click="$refs.imageEditInput?.click()" class="jimeng-create__upload-content">
|
||||
<i
|
||||
v-if="!jimengStore.imageEditParams.image_urls.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageEditParams.image_urls.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageEditParams.image_urls[0]?.url ||
|
||||
jimengStore.imageEditParams.image_urls[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageEditParams.image_urls = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑提示词 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">编辑提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的编辑效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑强度 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<div class="jimeng-create__slider-section">
|
||||
<label>编辑强度</label>
|
||||
<el-slider v-model="jimengStore.imageEditParams.scale" :min="0" :max="1" :step="0.1" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 图像特效 -->
|
||||
<div v-if="jimengStore.activeFunction === 'image_effects'" class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageEffectsInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
@change="
|
||||
(e) =>
|
||||
jimengStore.onImageUpload({
|
||||
file: e.target.files[0],
|
||||
name: e.target.files[0]?.name,
|
||||
})
|
||||
"
|
||||
class="hidden"
|
||||
/>
|
||||
<div @click="$refs.imageEffectsInput?.click()" class="jimeng-create__upload-content">
|
||||
<i
|
||||
v-if="!jimengStore.imageEffectsParams.image_input1.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageEffectsParams.image_input1.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-preview">
|
||||
<el-image
|
||||
:src="
|
||||
jimengStore.imageEffectsParams.image_input1[0]?.url ||
|
||||
jimengStore.imageEffectsParams.image_input1[0]?.content
|
||||
"
|
||||
fit="cover"
|
||||
class="w-32 h-32 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.imageEffectsParams.image_input1 = []"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 特效模板 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageEffectsParams.template_id"
|
||||
:options="
|
||||
jimengStore.imageEffectsTemplateOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="特效模板"
|
||||
title="选择特效模板"
|
||||
/>
|
||||
|
||||
<!-- 输出尺寸 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageEffectsParams.size"
|
||||
:options="
|
||||
jimengStore.imageSizeOptions.map((opt) => ({ label: opt.label, value: opt.value }))
|
||||
"
|
||||
label="输出尺寸"
|
||||
title="选择尺寸"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 文生视频 -->
|
||||
<div v-if="jimengStore.activeFunction === 'text_to_video'" class="space-y-6">
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的视频内容"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频比例 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.textToVideoParams.aspect_ratio"
|
||||
:options="
|
||||
jimengStore.videoAspectRatioOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="视频比例"
|
||||
title="选择比例"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 图生视频 -->
|
||||
<div v-if="jimengStore.activeFunction === 'image_to_video'" class="space-y-6">
|
||||
<!-- 上传图片 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">上传图片(最多2张)</label>
|
||||
<div class="jimeng-create__upload">
|
||||
<input
|
||||
ref="imageToVideoInput"
|
||||
type="file"
|
||||
accept=".jpg,.png,.jpeg"
|
||||
multiple
|
||||
@change="(e) => jimengStore.handleMultipleImageUpload(e)"
|
||||
class="hidden"
|
||||
/>
|
||||
<div @click="$refs.imageToVideoInput?.click()" class="jimeng-create__upload-content">
|
||||
<i
|
||||
v-if="!jimengStore.imageToVideoParams.image_urls.length"
|
||||
class="jimeng-create__upload-icon iconfont icon-upload"
|
||||
></i>
|
||||
<span
|
||||
v-if="!jimengStore.imageToVideoParams.image_urls.length"
|
||||
class="jimeng-create__upload-text"
|
||||
>上传图片</span
|
||||
>
|
||||
<div v-else class="jimeng-create__upload-multiple">
|
||||
<div
|
||||
v-for="(image, index) in jimengStore.imageToVideoParams.image_urls"
|
||||
:key="index"
|
||||
class="jimeng-create__upload-multiple-item"
|
||||
>
|
||||
<el-image
|
||||
:src="image?.url || image?.content"
|
||||
fit="cover"
|
||||
class="w-24 h-24 rounded"
|
||||
/>
|
||||
<button
|
||||
@click.stop="jimengStore.removeImage(index)"
|
||||
class="jimeng-create__upload-remove-btn"
|
||||
>
|
||||
<i class="iconfont icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="jimengStore.imageToVideoParams.image_urls.length < 2"
|
||||
@click.stop="$refs.imageToVideoInput?.click()"
|
||||
class="jimeng-create__upload-multiple-add"
|
||||
>
|
||||
<i class="iconfont icon-plus"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 提示词输入 -->
|
||||
<div class="jimeng-create__form-section">
|
||||
<label class="jimeng-create__form-section-label">提示词</label>
|
||||
<textarea
|
||||
v-model="jimengStore.currentPrompt"
|
||||
placeholder="描述你想要的视频效果"
|
||||
class="jimeng-create__form-section-textarea"
|
||||
rows="4"
|
||||
maxlength="2000"
|
||||
/>
|
||||
<div class="jimeng-create__form-section-counter">
|
||||
<span>{{ jimengStore.currentPrompt.length }}/2000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频比例 -->
|
||||
<CustomSelect
|
||||
v-model="jimengStore.imageToVideoParams.aspect_ratio"
|
||||
:options="
|
||||
jimengStore.videoAspectRatioOptions.map((opt) => ({
|
||||
label: opt.label,
|
||||
value: opt.value,
|
||||
}))
|
||||
"
|
||||
label="视频比例"
|
||||
title="选择比例"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 生成按钮 -->
|
||||
<div class="jimeng-create__submit-btn">
|
||||
@@ -539,19 +633,19 @@
|
||||
</p>
|
||||
</div>
|
||||
<!-- 任务状态 -->
|
||||
<div
|
||||
v-if="item.status !== 'completed'"
|
||||
class="jimeng-create__works-item-info-status"
|
||||
>
|
||||
<div v-if="item.status !== 'success'" class="jimeng-create__works-item-info-status">
|
||||
<div
|
||||
v-if="item.status === 'failed'"
|
||||
class="jimeng-create__works-item-info-status--failed"
|
||||
>
|
||||
<i class="iconfont icon-warning"></i>
|
||||
<span>失败</span>
|
||||
<el-tag type="danger">任务失败</el-tag>
|
||||
</div>
|
||||
<div v-else class="jimeng-create__works-item-info-status--processing">
|
||||
<div class="loading-spinner"></div>
|
||||
<div
|
||||
v-else
|
||||
class="flex items-center jimeng-create__works-item-info-status--processing"
|
||||
>
|
||||
<div class="loading-spinner mr-1"></div>
|
||||
<span>生成中</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -670,13 +764,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useJimengStore } from '@/store/mobile/jimeng'
|
||||
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
||||
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
||||
import CustomTabPane from '@/components/ui/CustomTabPane.vue'
|
||||
import CustomTabs from '@/components/ui/CustomTabs.vue'
|
||||
import { checkSession } from '@/store/cache'
|
||||
import { useJimengStore } from '@/store/mobile/jimeng'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const jimengStore = useJimengStore()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<i class="iconfont icon-back text-gray-600"></i>
|
||||
</button>
|
||||
<h1 class="flex-1 text-center text-lg font-semibold text-gray-900">音乐创作</h1>
|
||||
<h1 class="flex-1 text-center text-lg text-gray-900">音乐创作</h1>
|
||||
<div class="w-8"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -189,9 +189,10 @@
|
||||
<button
|
||||
@click="suno.create"
|
||||
:disabled="suno.loading"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="suno.loading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>{{ suno.loading ? '创作中...' : suno.btnText }}({{ suno.sunoPowerCost }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -210,7 +211,7 @@
|
||||
>
|
||||
<template #trigger>
|
||||
<button
|
||||
class="w-full py-3 bg-gradient-to-r from-purple-500 to-red-300 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
class="w-full py-3 bg-gradient-to-r from-purple-500 to-red-300 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i class="iconfont icon-upload mr-2"></i>
|
||||
<span>上传音乐</span>
|
||||
@@ -488,12 +489,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import '@/assets/css/mobile/suno.scss'
|
||||
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
||||
import { useSunoStore } from '@/store/mobile/suno'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useSunoStore } from '@/store/mobile/suno'
|
||||
import CustomSelect from '@/views/mobile/components/CustomSelect.vue'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import '@/assets/css/mobile/suno.scss'
|
||||
|
||||
const router = useRouter()
|
||||
const suno = useSunoStore()
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
>
|
||||
<i class="iconfont icon-back text-gray-600"></i>
|
||||
</button>
|
||||
<h1 class="flex-1 text-center text-lg font-semibold text-gray-900">视频创作</h1>
|
||||
<h1 class="flex-1 text-center text-lg text-gray-900">视频创作</h1>
|
||||
<div class="w-8"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,7 +186,7 @@
|
||||
<button
|
||||
@click="video.createLumaVideo"
|
||||
:disabled="video.generating"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="video.generating" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
@@ -413,7 +413,7 @@
|
||||
<button
|
||||
@click="video.createKelingVideo"
|
||||
:disabled="video.generating"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="video.generating" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
@@ -595,13 +595,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import '@/assets/css/mobile/video.scss'
|
||||
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
||||
import { useVideoStore } from '@/store/mobile/video'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useVideoStore } from '@/store/mobile/video'
|
||||
import CustomSelect from '@/views/mobile/components/CustomSelect.vue'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import '@/assets/css/mobile/video.scss'
|
||||
import CustomSelectOption from '../components/CustomSelectOption.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const video = useVideoStore()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mobile-sd">
|
||||
<van-form @submit="generate">
|
||||
<van-form>
|
||||
<van-cell-group class="px-3 pt-3 pb-4">
|
||||
<div>
|
||||
<van-field
|
||||
@@ -74,12 +74,16 @@
|
||||
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"
|
||||
/>
|
||||
|
||||
<div class="text-line pt-6">
|
||||
<el-tag>绘图消耗{{ dallPower }}算力,当前算力:{{ power }}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="text-line">
|
||||
<van-button round block type="primary" native-type="submit"> 立即生成 </van-button>
|
||||
<div class="sticky bottom-4 bg-white rounded-xl p-4 shadow-sm">
|
||||
<button
|
||||
@click="generate"
|
||||
:disabled="loading"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>{{ loading ? '创作中...' : '立即生成' }}({{ dallPower }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mobile-mj">
|
||||
<van-form @submit="generate">
|
||||
<van-form>
|
||||
<div class="text-line">图片比例</div>
|
||||
<div class="text-line">
|
||||
<van-row :gutter="10">
|
||||
@@ -191,16 +191,16 @@
|
||||
</van-collapse>
|
||||
</div>
|
||||
|
||||
<div class="text-line pt-6">
|
||||
<el-tag
|
||||
>绘图消耗{{ mjPower }}算力,U/V 操作消耗{{ mjActionPower }}算力,当前算力:{{
|
||||
power
|
||||
}}</el-tag
|
||||
<div class="sticky bottom-4 bg-white rounded-xl p-4 shadow-sm">
|
||||
<button
|
||||
@click="generate"
|
||||
:disabled="loading"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="text-line">
|
||||
<van-button round block type="primary" native-type="submit"> 立即生成 </van-button>
|
||||
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>{{ loading ? '创作中...' : '立即生成' }}({{ mjPower }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</van-form>
|
||||
|
||||
@@ -735,5 +735,5 @@ const tabChange = (tab) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@use '../../../assets/css/mobile/image-mj.scss' as *;
|
||||
@use '@/assets/css/mobile/image-mj.scss' as *;
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="mobile-sd">
|
||||
<van-form @submit="generate">
|
||||
<van-form>
|
||||
<van-cell-group class="px-3 pt-3 pb-4">
|
||||
<div>
|
||||
<van-field
|
||||
@@ -130,12 +130,16 @@
|
||||
</van-collapse-item>
|
||||
</van-collapse>
|
||||
|
||||
<div class="text-line pt-6">
|
||||
<el-tag>绘图消耗{{ sdPower }}算力,当前算力:{{ power }}</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="text-line">
|
||||
<van-button round block type="primary" native-type="submit"> 立即生成 </van-button>
|
||||
<div class="sticky bottom-4 bg-white rounded-xl p-4 shadow-sm">
|
||||
<button
|
||||
@click="generate"
|
||||
:disabled="loading"
|
||||
class="w-full py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-blue-600 hover:to-purple-700 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
>
|
||||
<i v-if="loading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-chuangzuo"></i>
|
||||
<span>{{ loading ? '创作中...' : '立即生成' }}({{ sdPower }}算力)</span>
|
||||
</button>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
</van-form>
|
||||
@@ -563,5 +567,5 @@ const showInfo = (message) => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '../../../assets/css/mobile/image-sd.scss' as *;
|
||||
@use '@/assets/css/mobile/image-sd.scss' as *;
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user