mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-05-10 11:44:28 +08:00
366 lines
9.1 KiB
JavaScript
366 lines
9.1 KiB
JavaScript
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
|
// * Use of this source code is governed by a Apache-2.0 license
|
|
// * that can be found in the LICENSE file.
|
|
// * @Author yangjian102621@163.com
|
|
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
import { checkSession } from '@/store/cache'
|
|
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 { reactive, ref } from 'vue'
|
|
|
|
export const useJimengStore = defineStore('jimeng', () => {
|
|
// 共同状态
|
|
const loading = ref(false)
|
|
const submitting = ref(false)
|
|
const page = ref(1)
|
|
const pageSize = ref(10)
|
|
const total = ref(0)
|
|
const taskFilter = ref('all')
|
|
const currentList = ref([])
|
|
const isOver = ref(false)
|
|
|
|
// 用户信息
|
|
const isLogin = ref(false)
|
|
// 视频预览
|
|
const showDialog = ref(false)
|
|
const currentVideoUrl = ref('')
|
|
|
|
// 登录弹窗
|
|
const shareStore = useSharedStore()
|
|
|
|
// 积分消耗配置
|
|
const powerConfig = reactive({})
|
|
const currentPowerCost = ref('0积分')
|
|
|
|
// 功能配置
|
|
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 switchFunction = (f) => {
|
|
activeFunction.value = f.key
|
|
formData.value = {}
|
|
setFunctionPowers()
|
|
}
|
|
|
|
// 获取功能名称
|
|
const getFunctionName = (type) => {
|
|
const func = functions.find((f) => f.key === type)
|
|
return func ? func.name : type
|
|
}
|
|
|
|
// 获取任务状态文本
|
|
const getTaskStatusText = (status) => {
|
|
const statusMap = {
|
|
in_queue: '任务排队中',
|
|
generating: '任务执行中',
|
|
success: '任务成功',
|
|
failed: '任务失败',
|
|
canceled: '任务已取消',
|
|
}
|
|
return statusMap[status] || status
|
|
}
|
|
|
|
// 获取状态类型
|
|
const getTaskType = (type) => {
|
|
const typeMap = {
|
|
text_to_image: 'primary',
|
|
image_to_image: 'primary',
|
|
image_edit: 'primary',
|
|
image_effects: 'primary',
|
|
text_to_video: 'success',
|
|
image_to_video: 'success',
|
|
}
|
|
return typeMap[type] || 'primary'
|
|
}
|
|
|
|
// 切换任务筛选
|
|
const switchTaskFilter = (filter) => {
|
|
taskFilter.value = filter
|
|
isOver.value = false
|
|
fetchData(1)
|
|
}
|
|
|
|
// 轮询定时器
|
|
let pollHandler = null
|
|
// 获取任务列表
|
|
const fetchData = async (pageNum = 1) => {
|
|
try {
|
|
loading.value = true
|
|
page.value = pageNum
|
|
|
|
const response = await httpPost('/api/jimeng/jobs', {
|
|
page: pageNum,
|
|
page_size: pageSize.value,
|
|
filter: taskFilter.value,
|
|
})
|
|
|
|
const data = response.data
|
|
if (!data.items || data.items.length === 0) {
|
|
isOver.value = true
|
|
if (pageNum === 1) {
|
|
currentList.value = []
|
|
}
|
|
return
|
|
}
|
|
|
|
total.value = data.total || 0
|
|
if (data.items.length < pageSize.value) {
|
|
isOver.value = true
|
|
}
|
|
if (pageNum === 1) {
|
|
currentList.value = data.items
|
|
} else {
|
|
currentList.value = currentList.value.concat(data.items)
|
|
}
|
|
} catch (error) {
|
|
showMessageError('获取任务列表失败:' + error.message)
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// 简单轮询逻辑
|
|
const startPolling = () => {
|
|
if (pollHandler) {
|
|
clearInterval(pollHandler)
|
|
}
|
|
pollHandler = setInterval(async () => {
|
|
const response = await httpPost('/api/jimeng/jobs', {
|
|
page: 1,
|
|
page_size: 20,
|
|
})
|
|
const data = response.data
|
|
if (data.items.length === 0) {
|
|
stopPolling()
|
|
return
|
|
}
|
|
|
|
const todoList = data.items.filter(
|
|
(item) => item.status === 'in_queue' || item.status === 'generating'
|
|
)
|
|
// 更新当前列表
|
|
currentList.value.forEach((item) => {
|
|
const index = data.items.findIndex((i) => i.id === item.id)
|
|
if (index !== -1) {
|
|
Object.assign(item, data.items[index])
|
|
}
|
|
})
|
|
if (todoList.length === 0) {
|
|
stopPolling()
|
|
}
|
|
}, 3000)
|
|
}
|
|
|
|
const stopPolling = () => {
|
|
if (pollHandler) {
|
|
clearInterval(pollHandler)
|
|
pollHandler = null
|
|
}
|
|
}
|
|
|
|
// 提交任务
|
|
const submitTask = async () => {
|
|
if (!isLogin.value) {
|
|
shareStore.setShowLoginDialog(true)
|
|
return
|
|
}
|
|
console.log(formData.value)
|
|
for (const key in requiredKeys.value) {
|
|
if (!formData.value[key]) {
|
|
showMessageError('缺少参数:' + requiredKeys.value[key].label)
|
|
return
|
|
}
|
|
}
|
|
|
|
try {
|
|
submitting.value = true
|
|
formData.value.type = activeFunction.value
|
|
// 视频 duration 转成整数
|
|
if (formData.value.duration) {
|
|
formData.value.duration = parseInt(formData.value.duration)
|
|
}
|
|
if (formData.value.image_urls && !Array.isArray(formData.value.image_urls)) {
|
|
formData.value.image_urls = [formData.value.image_urls]
|
|
}
|
|
const response = await httpPost('/api/jimeng/task', formData.value)
|
|
showMessageOK('任务提交成功')
|
|
isOver.value = false
|
|
await fetchData(1)
|
|
startPolling()
|
|
} catch (error) {
|
|
console.error('提交任务失败:', error)
|
|
showMessageError(error.message || '提交任务失败')
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
const downloadFile = async (item) => {
|
|
const url = replaceImg(item.video_url || item.img_url)
|
|
const downloadURL = `/api/download?url=${url}`
|
|
const urlObj = new URL(url)
|
|
const fileName = urlObj.pathname.split('/').pop()
|
|
|
|
item.downloading = true
|
|
|
|
try {
|
|
const response = await httpDownload(downloadURL)
|
|
const blob = new Blob([response.data])
|
|
const link = document.createElement('a')
|
|
link.href = URL.createObjectURL(blob)
|
|
link.download = fileName
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
document.body.removeChild(link)
|
|
URL.revokeObjectURL(link.href)
|
|
item.downloading = false
|
|
} catch (error) {
|
|
showMessageError('下载失败')
|
|
item.downloading = false
|
|
}
|
|
}
|
|
|
|
// 重试任务
|
|
const retryTask = async (taskId) => {
|
|
try {
|
|
const response = await httpGet(`/api/jimeng/retry?id=${taskId}`)
|
|
if (response.data) {
|
|
showMessageOK('重试任务已提交')
|
|
isOver.value = false
|
|
await fetchData(1)
|
|
startPolling()
|
|
}
|
|
} catch (error) {
|
|
console.error('重试任务失败:', error)
|
|
showMessageError(error.message || '重试任务失败')
|
|
}
|
|
}
|
|
|
|
// 删除任务
|
|
const removeJob = async (item) => {
|
|
try {
|
|
await ElMessageBox.confirm('确定要删除这个任务吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
})
|
|
|
|
const response = await httpGet('/api/jimeng/remove', { id: item.id })
|
|
if (response.data) {
|
|
showMessageOK('删除成功')
|
|
await fetchData(1)
|
|
}
|
|
} catch (error) {
|
|
if (error !== 'cancel') {
|
|
console.error('删除任务失败:', error)
|
|
showMessageError(error.message || '删除任务失败')
|
|
}
|
|
}
|
|
}
|
|
|
|
// 播放视频
|
|
const playVideo = (item) => {
|
|
currentVideoUrl.value = item.video_url
|
|
showDialog.value = true
|
|
}
|
|
|
|
const setFunctionPowers = () => {
|
|
if (activeFunction.value === 'image') {
|
|
currentPowerCost.value = `${powerConfig.image}积分/张`
|
|
} else {
|
|
currentPowerCost.value = `${powerConfig.video}积分/秒`
|
|
}
|
|
}
|
|
|
|
// 初始化方法
|
|
const init = async () => {
|
|
try {
|
|
// 获取积分消耗配置
|
|
const powerRes = await httpGet('/api/jimeng/power-config')
|
|
if (powerRes.data) {
|
|
Object.assign(powerConfig, powerRes.data)
|
|
setFunctionPowers()
|
|
}
|
|
const user = await checkSession()
|
|
isLogin.value = true
|
|
// 获取任务列表
|
|
await fetchData(1)
|
|
// 开始轮询
|
|
startPolling()
|
|
} catch (error) {
|
|
console.error('初始化失败:', error)
|
|
}
|
|
}
|
|
|
|
// 页面卸载时清理轮询
|
|
const cleanup = () => {
|
|
stopPolling()
|
|
}
|
|
|
|
// 返回所有状态和方法
|
|
return {
|
|
// 状态
|
|
activeFunction,
|
|
loading,
|
|
submitting,
|
|
page,
|
|
pageSize,
|
|
total,
|
|
taskFilter,
|
|
currentList,
|
|
isOver,
|
|
isLogin,
|
|
showDialog,
|
|
currentVideoUrl,
|
|
|
|
// 配置
|
|
functions,
|
|
activeFunction,
|
|
functionParams,
|
|
formData,
|
|
requiredKeys,
|
|
progress,
|
|
currentPowerCost,
|
|
|
|
// 方法
|
|
init,
|
|
switchFunction,
|
|
getFunctionName,
|
|
getTaskStatusText,
|
|
getTaskType,
|
|
switchTaskFilter,
|
|
fetchData,
|
|
submitTask,
|
|
downloadFile,
|
|
retryTask,
|
|
removeJob,
|
|
playVideo,
|
|
cleanup,
|
|
|
|
// 工具函数
|
|
substr,
|
|
replaceImg,
|
|
}
|
|
})
|