mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-05-01 15:34:31 +08:00
手机端即梦页面功能完成
This commit is contained in:
@@ -56,7 +56,7 @@
|
||||
<!-- 图生图参数 -->
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3" v-if="jimengStore.useImageInput">
|
||||
<ImageUpload
|
||||
v-model="jimengStore.imageToImageParams.image_input[0]"
|
||||
v-model="jimengStore.imageToImageParams.image_input"
|
||||
:max-count="1"
|
||||
:multiple="false"
|
||||
/>
|
||||
@@ -220,16 +220,13 @@
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<label class="text-gray-700 font-semibold">使用图片辅助生成:</label>
|
||||
<el-switch v-model="jimengStore.textToVideoParams.use_image_input" size="default" />
|
||||
<el-switch v-model="jimengStore.useImageInput" size="default" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="bg-white rounded-xl p-4 shadow-sm mb-3"
|
||||
v-if="jimengStore.textToVideoParams.use_image_input"
|
||||
>
|
||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3" v-if="jimengStore.useImageInput">
|
||||
<ImageUpload
|
||||
v-model="jimengStore.textToVideoParams.image_input"
|
||||
v-model="jimengStore.imageToVideoParams.image_input"
|
||||
:max-count="2"
|
||||
:multiple="true"
|
||||
/>
|
||||
@@ -268,7 +265,7 @@
|
||||
<!-- 作品列表 -->
|
||||
<div class="jimeng-create__works">
|
||||
<h2 class="jimeng-create__works-title">我的作品</h2>
|
||||
<div class="jimeng-create__works-list space-y-4">
|
||||
<div class="jimeng-create__works-list space-y-4" v-if="jimengStore.currentList.length > 0">
|
||||
<div
|
||||
v-for="item in jimengStore.currentList"
|
||||
:key="item.id"
|
||||
@@ -290,6 +287,28 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div
|
||||
v-else-if="item.video_url"
|
||||
class="jimeng-create__works-item-thumb-placeholder relative"
|
||||
>
|
||||
<video
|
||||
:src="item.video_url"
|
||||
preload="auto"
|
||||
loop="loop"
|
||||
muted="muted"
|
||||
class="w-full h-full object-cover"
|
||||
>
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
<div
|
||||
class="video-mask absolute top-0 left-0 w-full h-full flex justify-center items-center"
|
||||
@click="jimengStore.playMedia(item)"
|
||||
>
|
||||
<div class="play-btn">
|
||||
<img src="/images/play.svg" alt="播放" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="jimeng-create__works-item-thumb-placeholder">
|
||||
<i
|
||||
:class="
|
||||
@@ -297,18 +316,6 @@
|
||||
"
|
||||
></i>
|
||||
</div>
|
||||
<!-- 播放/查看按钮 -->
|
||||
<button
|
||||
v-if="item.status === 'completed'"
|
||||
@click="jimengStore.playMedia(item)"
|
||||
class="jimeng-create__works-item-thumb-overlay"
|
||||
>
|
||||
<i
|
||||
:class="
|
||||
item.type.includes('video') ? 'iconfont icon-play' : 'iconfont icon-eye'
|
||||
"
|
||||
></i>
|
||||
</button>
|
||||
|
||||
<!-- 失败状态 -->
|
||||
<div
|
||||
@@ -371,50 +378,44 @@
|
||||
|
||||
<!-- 快捷操作按钮 -->
|
||||
<div class="jimeng-create__works-item-quick-actions">
|
||||
<!-- 复制提示词 -->
|
||||
<button
|
||||
v-if="item.prompt"
|
||||
@click="jimengStore.copyPrompt(item.prompt)"
|
||||
class="jimeng-create__works-item-quick-action-btn"
|
||||
title="复制提示词"
|
||||
>
|
||||
<i class="iconfont icon-copy"></i>
|
||||
</button>
|
||||
|
||||
<span v-if="item.status === 'success'">
|
||||
<!-- 画同款 -->
|
||||
<span v-if="item.status === 'success'" class="flex">
|
||||
<!-- 复制提示词 -->
|
||||
<button
|
||||
@click="jimengStore.drawSame(item)"
|
||||
v-if="item.prompt"
|
||||
@click="jimengStore.copyPrompt(item.prompt)"
|
||||
class="jimeng-create__works-item-quick-action-btn"
|
||||
title="画同款"
|
||||
title="复制提示词"
|
||||
>
|
||||
<i class="iconfont icon-image-list"></i>
|
||||
<i class="iconfont icon-copy"></i>
|
||||
</button>
|
||||
|
||||
<!-- 下载 -->
|
||||
<button
|
||||
v-if="item.status === 'completed' && (item.img_url || item.video_url)"
|
||||
v-if="item.status === 'success' && (item.img_url || item.video_url)"
|
||||
@click="jimengStore.downloadFile(item)"
|
||||
:disabled="item.downloading"
|
||||
class="jimeng-create__works-item-quick-action-btn"
|
||||
title="下载"
|
||||
class="p-2 text-blue-500"
|
||||
>
|
||||
<i v-if="item.downloading" class="iconfont icon-loading animate-spin"></i>
|
||||
<i v-else class="iconfont icon-download"></i></button
|
||||
></span>
|
||||
<i v-else class="iconfont icon-download"></i>
|
||||
<span class="ml-1">下载</span>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<!-- 重试 -->
|
||||
<button
|
||||
v-if="item.status === 'failed'"
|
||||
@click="jimengStore.retryTask(item.id)"
|
||||
class="jimeng-create__works-item-quick-action-btn"
|
||||
title="重试"
|
||||
class="p-2 text-green-500"
|
||||
>
|
||||
<i class="iconfont icon-refresh"></i>
|
||||
<span class="ml-1">重试</span>
|
||||
</button>
|
||||
|
||||
<!-- 删除 -->
|
||||
<button @click="jimengStore.removeJob(item)" class="p-2">
|
||||
<i class="iconfont icon-remove"></i> 删除
|
||||
<button @click="jimengStore.removeJob(item)" class="p-2 text-red-500">
|
||||
<i class="iconfont icon-remove"></i>
|
||||
<span class="ml-1">删除</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -424,7 +425,9 @@
|
||||
class="jimeng-create__works-item-error"
|
||||
>
|
||||
<div class="jimeng-create__works-item-error-content">
|
||||
<span class="jimeng-create__works-item-error-text">{{ item.err_msg }}</span>
|
||||
<span class="jimeng-create__works-item-error-text line-clamp-3">{{
|
||||
item.err_msg
|
||||
}}</span>
|
||||
<button
|
||||
@click="jimengStore.copyErrorMsg(item.err_msg)"
|
||||
class="jimeng-create__works-item-error-copy-btn"
|
||||
@@ -449,6 +452,10 @@
|
||||
没有更多了
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4" v-else>
|
||||
<van-empty description="暂无数据" image-size="120" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 媒体预览弹窗 -->
|
||||
@@ -461,21 +468,15 @@
|
||||
<div class="jimeng-create__media-dialog-header">
|
||||
<h3>媒体预览</h3>
|
||||
<button @click="jimengStore.closeMediaDialog">
|
||||
<i class="iconfont icon-close"></i>
|
||||
<i class="iconfont icon-error"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="jimeng-create__media-dialog-body">
|
||||
<img
|
||||
v-if="jimengStore.currentMediaUrl && !jimengStore.currentMediaUrl.includes('video')"
|
||||
:src="jimengStore.currentMediaUrl"
|
||||
class="w-full max-h-[60vh] object-contain rounded-lg"
|
||||
/>
|
||||
<video
|
||||
v-else-if="jimengStore.currentMediaUrl"
|
||||
:src="jimengStore.currentMediaUrl"
|
||||
controls
|
||||
autoplay
|
||||
class="w-full max-h-[60vh] rounded-lg"
|
||||
class="w-full max-h-[60vh] rounded-lg object-cover"
|
||||
>
|
||||
您的浏览器不支持视频播放
|
||||
</video>
|
||||
@@ -535,5 +536,5 @@ const goBack = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/css/mobile/jimeng.scss';
|
||||
@use '@/assets/css/mobile/jimeng.scss';
|
||||
</style>
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-prompt" v-else>
|
||||
<el-button type="primary" size="large" @click="router.push('/mobile/login')"
|
||||
>立即登录</el-button
|
||||
<button
|
||||
class="py-3 px-5 bg-gradient-to-r from-green-400 to-blue-400 text-white rounded-xl disabled:from-gray-400 disabled:to-gray-400 disabled:cursor-not-allowed hover:from-green-500 hover:to-blue-500 transition-all duration-200 flex items-center justify-center space-x-2"
|
||||
@click="router.push('/login')"
|
||||
>
|
||||
立即登录
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,7 +281,6 @@ onMounted(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
isLogin.value = false
|
||||
showLoginDialog(router)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@
|
||||
<!-- 作品列表 -->
|
||||
<div class="p-4">
|
||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">我的作品</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-4" v-if="suno.list.length > 0">
|
||||
<div v-for="item in suno.list" :key="item.id" class="bg-white rounded-xl p-4 shadow-sm">
|
||||
<div class="flex space-x-4">
|
||||
<div class="flex-shrink-0">
|
||||
@@ -447,6 +447,10 @@
|
||||
没有更多了
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4" v-else>
|
||||
<van-empty description="暂无数据" image-size="120" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 音乐播放器 -->
|
||||
@@ -496,6 +500,7 @@ import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
||||
import { useSunoStore } from '@/store/mobile/suno'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { checkSession } from '@/store/cache'
|
||||
|
||||
const router = useRouter()
|
||||
const suno = useSunoStore()
|
||||
@@ -523,13 +528,19 @@ const handleScroll = () => {
|
||||
|
||||
let tastPullHandler = null
|
||||
onMounted(() => {
|
||||
suno.fetchData(1)
|
||||
tastPullHandler = setInterval(() => {
|
||||
if (suno.taskPulling) {
|
||||
suno.refreshFirstPage()
|
||||
}
|
||||
}, 5000)
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
checkSession()
|
||||
.then(() => {
|
||||
suno.fetchData(1)
|
||||
tastPullHandler = setInterval(() => {
|
||||
if (suno.taskPulling) {
|
||||
suno.refreshFirstPage()
|
||||
}
|
||||
}, 5000)
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn('用户未登录')
|
||||
})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (tastPullHandler) clearInterval(tastPullHandler)
|
||||
@@ -538,128 +549,5 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 自定义动画 */
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-fade-out {
|
||||
animation: fade-out 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-slide-up {
|
||||
animation: slide-up 0.3s ease-out;
|
||||
}
|
||||
|
||||
.animate-scale-up {
|
||||
animation: scale-up 0.3s ease-out;
|
||||
}
|
||||
|
||||
/* 文本截断 */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 滚动监听自动加载更多 */
|
||||
.scroll-container {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 深色模式适配 */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg-gray-50 {
|
||||
background-color: #1f2937;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background-color: #374151;
|
||||
}
|
||||
|
||||
.text-gray-900 {
|
||||
color: #f9fafb;
|
||||
}
|
||||
|
||||
.text-gray-700 {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
.text-gray-600 {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.border-gray-200 {
|
||||
border-color: #4b5563;
|
||||
}
|
||||
|
||||
.bg-gray-100:hover {
|
||||
background-color: #4b5563;
|
||||
}
|
||||
}
|
||||
|
||||
/* el-upload 组件样式定制 */
|
||||
.upload-area {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-upload) {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:deep(.el-button) {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@use '@/assets/css/mobile/suno.scss';
|
||||
</style>
|
||||
|
||||
@@ -438,7 +438,7 @@
|
||||
<!-- 作品列表 -->
|
||||
<div class="p-4">
|
||||
<h2 class="text-lg font-semibold text-gray-900 mb-4">我的作品</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-4" v-if="video.currentList.length > 0">
|
||||
<div
|
||||
v-for="item in video.currentList"
|
||||
:key="item.id"
|
||||
@@ -573,6 +573,10 @@
|
||||
没有更多了
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="px-4" v-else>
|
||||
<van-empty description="暂无数据" image-size="120" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 视频预览弹窗 -->
|
||||
@@ -611,6 +615,7 @@ import { useVideoStore } from '@/store/mobile/video'
|
||||
import { showConfirmDialog } from 'vant'
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { checkSession } from '@/store/cache'
|
||||
|
||||
const router = useRouter()
|
||||
const video = useVideoStore()
|
||||
@@ -623,13 +628,17 @@ const goBack = () => {
|
||||
// 定时轮询等副作用
|
||||
let tastPullHandler = null
|
||||
onMounted(() => {
|
||||
video.fetchData(1)
|
||||
video.fetchUserPower()
|
||||
tastPullHandler = setInterval(() => {
|
||||
if (video.taskPulling) {
|
||||
checkSession()
|
||||
.then(() => {
|
||||
video.fetchData(1)
|
||||
}
|
||||
}, 5000)
|
||||
video.fetchUserPower()
|
||||
tastPullHandler = setInterval(() => {
|
||||
if (video.taskPulling) {
|
||||
video.fetchData(1)
|
||||
}
|
||||
}, 5000)
|
||||
})
|
||||
.catch(() => {})
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (tastPullHandler) clearInterval(tastPullHandler)
|
||||
|
||||
Reference in New Issue
Block a user