优化移动端即梦页面

This commit is contained in:
RockYang
2025-08-07 22:27:09 +08:00
parent e456210944
commit 4e237c9560
14 changed files with 1906 additions and 1239 deletions

View File

@@ -0,0 +1,410 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { httpGet, httpPost } from '@/utils/http'
import { showMessageError, showMessageOK, showLoading, closeLoading } from '@/utils/dialog'
import { showConfirmDialog } from 'vant'
export const useJimengStore = defineStore('mobile-jimeng', () => {
// 响应式数据
const activeCategory = ref('image_generation')
const useImageInput = ref(false)
const submitting = ref(false)
const listLoading = ref(false)
const listFinished = ref(false)
const currentList = ref([])
const showMediaDialog = ref(false)
const currentMediaUrl = ref('')
const currentPrompt = ref('')
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const currentPowerCost = ref(0)
const taskPulling = ref(true)
const tastPullHandler = ref(null)
// 功能分类
const categories = ref([
{ key: 'image_generation', name: '图像生成' },
{ key: 'image_editing', name: '图像编辑' },
{ key: 'image_effects', name: '图像特效' },
{ key: 'video_generation', name: '视频生成' },
])
// 选项数据
const imageSizeOptions = [
{ label: '512x512', value: '512x512' },
{ label: '768x768', value: '768x768' },
{ label: '1024x1024', value: '1024x1024' },
{ label: '1024x1536', value: '1024x1536' },
{ label: '1536x1024', value: '1536x1024' },
]
const videoAspectRatioOptions = [
{ label: '16:9', value: '16:9' },
{ label: '9:16', value: '9:16' },
{ label: '1:1', value: '1:1' },
{ label: '4:3', value: '4:3' },
]
const imageEffectsTemplateOptions = [
{ label: '亚克力装饰', value: 'acrylic_ornaments' },
{ label: '天使小雕像', value: 'angel_figurine' },
{ label: '毛毫3D拍立得', value: 'felt_3d_polaroid' },
{ label: '水彩插图', value: 'watercolor_illustration' },
]
// 功能参数
const textToImageParams = ref({
size: '1024x1024',
scale: 7.5,
use_pre_llm: false,
})
const imageToImageParams = ref({
image_input: [],
size: '1024x1024',
})
const imageEditParams = ref({
image_urls: [],
scale: 0.5,
})
const imageEffectsParams = ref({
image_input1: [],
template_id: '',
size: '1024x1024',
})
const textToVideoParams = ref({
aspect_ratio: '16:9',
})
const imageToVideoParams = ref({
image_urls: [],
aspect_ratio: '16:9',
})
// 计算属性
const activeFunction = computed(() => {
if (activeCategory.value === 'image_generation') {
return useImageInput.value ? 'image_to_image' : 'text_to_image'
} else if (activeCategory.value === 'image_editing') {
return 'image_edit'
} else if (activeCategory.value === 'video_generation') {
return useImageInput.value ? 'image_to_video' : 'text_to_video'
}
return 'text_to_image'
})
// Actions
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 switchCategory = (key) => {
activeCategory.value = key
useImageInput.value = false
}
const switchInputMode = () => {
currentPrompt.value = ''
}
const handleMultipleImageUpload = (event) => {
const files = Array.from(event.target.files)
files.forEach((file) => {
if (imageToVideoParams.value.image_urls.length < 2) {
onImageUpload({ file, name: file.name })
}
})
}
const removeImage = (index) => {
imageToVideoParams.value.image_urls.splice(index, 1)
}
const onImageUpload = (file) => {
const formData = new FormData()
formData.append('file', file.file, file.name)
showLoading('正在上传图片...')
return httpPost('/api/upload', formData)
.then((res) => {
showMessageOK('图片上传成功')
const imageData = { url: res.data.url, content: res.data.url }
// 根据当前活动功能添加到相应的参数中
if (activeFunction.value === 'image_to_image') {
imageToImageParams.value.image_input = [imageData]
} else if (activeFunction.value === 'image_edit') {
imageEditParams.value.image_urls = [imageData]
} else if (activeFunction.value === 'image_effects') {
imageEffectsParams.value.image_input1 = [imageData]
} else if (activeFunction.value === 'image_to_video') {
imageToVideoParams.value.image_urls.push(imageData)
}
return res.data.url
})
.catch((e) => {
showMessageError('图片上传失败:' + e.message)
})
.finally(() => {
closeLoading()
})
}
const submitTask = () => {
if (!currentPrompt.value.trim()) {
showMessageError('请输入提示词')
return
}
submitting.value = true
const params = {
type: activeFunction.value,
prompt: currentPrompt.value,
}
// 根据功能类型添加相应参数
if (activeFunction.value === 'text_to_image') {
Object.assign(params, textToImageParams.value)
} else if (activeFunction.value === 'image_to_image') {
Object.assign(params, imageToImageParams.value)
} else if (activeFunction.value === 'image_edit') {
Object.assign(params, imageEditParams.value)
} else if (activeFunction.value === 'image_effects') {
Object.assign(params, imageEffectsParams.value)
} else if (activeFunction.value === 'text_to_video') {
Object.assign(params, textToVideoParams.value)
} else if (activeFunction.value === 'image_to_video') {
Object.assign(params, imageToVideoParams.value)
}
return httpPost('/api/jimeng/create', params)
.then(() => {
fetchData(1)
taskPulling.value = true
showMessageOK('创建任务成功')
currentPrompt.value = ''
})
.catch((e) => {
showMessageError('创建任务失败:' + e.message)
})
.finally(() => {
submitting.value = false
})
}
const fetchData = (_page) => {
if (_page) {
page.value = _page
}
listLoading.value = true
return httpGet('/api/jimeng/list', { page: page.value, page_size: pageSize.value })
.then((res) => {
total.value = res.data.total
let needPull = false
const items = []
for (let v of res.data.items) {
if (v.status === 'in_queue' || v.status === 'generating') {
needPull = true
}
items.push(v)
}
listLoading.value = false
taskPulling.value = needPull
if (page.value === 1) {
currentList.value = items
} else {
currentList.value.push(...items)
}
if (items.length < pageSize.value) {
listFinished.value = true
}
})
.catch((e) => {
listLoading.value = false
showMessageError('获取作品列表失败:' + e.message)
})
}
const loadMore = () => {
page.value++
fetchData()
}
const playMedia = (item) => {
currentMediaUrl.value = item.img_url || item.video_url
showMediaDialog.value = true
}
const downloadFile = (item) => {
item.downloading = true
const link = document.createElement('a')
link.href = item.img_url || item.video_url
link.download = item.title || 'file'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
item.downloading = false
showMessageSuccess('开始下载')
}
const retryTask = (id) => {
return httpPost('/api/jimeng/retry', { id })
.then(() => {
showMessageOK('重试任务成功')
fetchData(1)
})
.catch((e) => {
showMessageError('重试任务失败:' + e.message)
})
}
const removeJob = (item) => {
return showConfirmDialog({
title: '确认删除',
message: '此操作将会删除任务相关文件,继续操作吗?',
confirmButtonText: '确认删除',
cancelButtonText: '取消',
})
.then(() => {
return httpGet('/api/jimeng/remove', { id: item.id })
.then(() => {
showMessageOK('任务删除成功')
fetchData(1)
})
.catch((e) => {
showMessageError('任务删除失败:' + e.message)
})
})
.catch(() => {})
}
const getFunctionName = (type) => {
const nameMap = {
text_to_image: '文生图',
image_to_image: '图生图',
image_edit: '图像编辑',
image_effects: '图像特效',
text_to_video: '文生视频',
image_to_video: '图生视频',
}
return nameMap[type] || type
}
const getTaskType = (type) => {
return type.includes('video') ? 'warning' : 'primary'
}
const startTaskPolling = () => {
tastPullHandler.value = setInterval(() => {
if (taskPulling.value) {
fetchData(1)
}
}, 5000)
}
const stopTaskPolling = () => {
if (tastPullHandler.value) {
clearInterval(tastPullHandler.value)
}
}
const resetParams = () => {
textToImageParams.value = {
size: '1024x1024',
scale: 7.5,
use_pre_llm: false,
}
imageToImageParams.value = {
image_input: [],
size: '1024x1024',
}
imageEditParams.value = {
image_urls: [],
scale: 0.5,
}
imageEffectsParams.value = {
image_input1: [],
template_id: '',
size: '1024x1024',
}
textToVideoParams.value = {
aspect_ratio: '16:9',
}
imageToVideoParams.value = {
image_urls: [],
aspect_ratio: '16:9',
}
}
const closeMediaDialog = () => {
showMediaDialog.value = false
currentMediaUrl.value = ''
}
return {
// State
activeCategory,
useImageInput,
submitting,
listLoading,
listFinished,
currentList,
showMediaDialog,
currentMediaUrl,
currentPrompt,
page,
pageSize,
total,
currentPowerCost,
taskPulling,
tastPullHandler,
categories,
imageSizeOptions,
videoAspectRatioOptions,
imageEffectsTemplateOptions,
textToImageParams,
imageToImageParams,
imageEditParams,
imageEffectsParams,
textToVideoParams,
imageToVideoParams,
// Computed
activeFunction,
// Actions
getCategoryIcon,
switchCategory,
switchInputMode,
handleMultipleImageUpload,
removeImage,
onImageUpload,
submitTask,
fetchData,
loadMore,
playMedia,
downloadFile,
retryTask,
removeJob,
getFunctionName,
getTaskType,
startTaskPolling,
stopTaskPolling,
resetParams,
closeMediaDialog,
}
})