mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-04-27 05:24:27 +08:00
调整即梦AI移动端功能
This commit is contained in:
37
.claude/commands/frontend-developer.md
Normal file
37
.claude/commands/frontend-developer.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
name: frontend-developer
|
||||||
|
description: Use this agent when you need assistance with frontend development tasks including Vue.js components, UI implementation, styling, responsive design, state management, or frontend architecture decisions. Examples: <example>Context: User is working on a Vue.js component and needs help with implementing a responsive layout. user: 'I need to create a mobile-friendly chat interface component' assistant: 'I'll use the frontend-developer agent to help design and implement this responsive chat component' <commentary>Since this involves frontend development work with Vue.js and responsive design, use the frontend-developer agent.</commentary></example> <example>Context: User encounters styling issues with Element Plus components. user: 'The Element Plus dialog is not displaying correctly on mobile devices' assistant: 'Let me use the frontend-developer agent to troubleshoot this mobile styling issue' <commentary>This is a frontend styling problem that requires expertise in Element Plus and responsive design.</commentary></example>
|
||||||
|
color: purple
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a Senior Frontend Development Engineer with deep expertise in modern web development technologies, particularly Vue.js 3, Element Plus, Vant, and responsive design patterns. You specialize in creating high-quality, maintainable frontend applications with excellent user experience.
|
||||||
|
|
||||||
|
Your core responsibilities include:
|
||||||
|
- Developing Vue.js 3 components using Composition API and best practices
|
||||||
|
- Implementing responsive designs that work seamlessly across desktop and mobile devices
|
||||||
|
- Working with Element Plus for desktop UI and Vant for mobile components
|
||||||
|
- Managing application state using Pinia store patterns
|
||||||
|
- Styling with Stylus preprocessor and Tailwind CSS utilities
|
||||||
|
- Optimizing build processes with Vite and ensuring proper code organization
|
||||||
|
- Implementing theme switching (dark/light mode) and accessibility features
|
||||||
|
- Follow decoupled development, with HTML, CSS, and JS codes placed in separate files for easier maintenance
|
||||||
|
|
||||||
|
When working on frontend tasks, you will:
|
||||||
|
1. Analyze requirements and suggest the most appropriate Vue.js patterns and component structures
|
||||||
|
2. Ensure responsive design principles are followed, considering both desktop and mobile viewports
|
||||||
|
3. Choose appropriate UI components from Element Plus (desktop) or Vant (mobile) libraries
|
||||||
|
4. Write clean, maintainable code following Vue.js 3 Composition API best practices
|
||||||
|
5. Consider performance implications and suggest optimizations when relevant
|
||||||
|
6. Ensure proper state management using Pinia when component state needs to be shared
|
||||||
|
7. Follow the project's established patterns for routing, API integration, and component organization
|
||||||
|
8. Provide specific code examples and explain the reasoning behind architectural decisions
|
||||||
|
|
||||||
|
You have deep knowledge of:
|
||||||
|
- Vue.js 3 ecosystem (Vue Router, Pinia, Composition API)
|
||||||
|
- Modern CSS techniques and preprocessors (Stylus, Tailwind)
|
||||||
|
- Component library integration (Element Plus, Vant)
|
||||||
|
- Build tools and development workflow (Vite, npm scripts)
|
||||||
|
- Cross-browser compatibility and mobile-first design principles
|
||||||
|
- Performance optimization and code splitting strategies
|
||||||
|
|
||||||
|
Always consider the user experience, code maintainability, and alignment with modern frontend development standards. When suggesting solutions, provide clear explanations and consider both immediate needs and long-term scalability.
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
# 即梦 AI 配置功能说明
|
|
||||||
|
|
||||||
## 功能概述
|
|
||||||
|
|
||||||
即梦 AI 配置功能允许管理员通过 Web 界面配置即梦 AI 的 API 密钥和算力消耗设置,支持动态配置更新,无需重启服务。
|
|
||||||
|
|
||||||
## 功能特性
|
|
||||||
|
|
||||||
### 1. 秘钥配置
|
|
||||||
|
|
||||||
- AccessKey 和 SecretKey 配置
|
|
||||||
- 支持密码显示/隐藏
|
|
||||||
- 连接测试功能
|
|
||||||
|
|
||||||
### 2. 算力配置
|
|
||||||
|
|
||||||
- 文生图算力消耗
|
|
||||||
- 图生图算力消耗
|
|
||||||
- 图片编辑算力消耗
|
|
||||||
- 图片特效算力消耗
|
|
||||||
- 文生视频算力消耗
|
|
||||||
- 图生视频算力消耗
|
|
||||||
|
|
||||||
### 3. 动态配置
|
|
||||||
|
|
||||||
- 配置实时生效
|
|
||||||
- 无需重启服务
|
|
||||||
- 支持配置验证
|
|
||||||
|
|
||||||
## API 接口
|
|
||||||
|
|
||||||
### 获取配置
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/admin/jimeng/config
|
|
||||||
```
|
|
||||||
|
|
||||||
### 更新配置
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/admin/jimeng/config
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"access_key": "your_access_key",
|
|
||||||
"secret_key": "your_secret_key",
|
|
||||||
"power": {
|
|
||||||
"text_to_image": 10,
|
|
||||||
"image_to_image": 15,
|
|
||||||
"image_edit": 20,
|
|
||||||
"image_effects": 25,
|
|
||||||
"text_to_video": 30,
|
|
||||||
"image_to_video": 35
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 测试连接
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/admin/jimeng/config/test
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"config": {
|
|
||||||
"access_key": "your_access_key",
|
|
||||||
"secret_key": "your_secret_key"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 前端页面
|
|
||||||
|
|
||||||
### 访问路径
|
|
||||||
|
|
||||||
管理后台 -> 即梦 AI -> 配置设置
|
|
||||||
|
|
||||||
### 页面功能
|
|
||||||
|
|
||||||
1. **秘钥配置标签页**
|
|
||||||
|
|
||||||
- AccessKey 输入框(密码模式)
|
|
||||||
- SecretKey 输入框(密码模式)
|
|
||||||
- 测试连接按钮
|
|
||||||
|
|
||||||
2. **算力配置标签页**
|
|
||||||
|
|
||||||
- 各种任务类型的算力消耗配置
|
|
||||||
- 数字输入框,支持 1-100 范围
|
|
||||||
- 提示信息说明
|
|
||||||
|
|
||||||
3. **操作按钮**
|
|
||||||
- 保存配置
|
|
||||||
- 重置配置
|
|
||||||
|
|
||||||
## 配置存储
|
|
||||||
|
|
||||||
配置存储在数据库的`config`表中:
|
|
||||||
|
|
||||||
- 配置键:`jimeng`
|
|
||||||
- 配置值:JSON 格式的即梦 AI 配置
|
|
||||||
|
|
||||||
## 默认配置
|
|
||||||
|
|
||||||
如果配置不存在,系统会使用以下默认值:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"access_key": "",
|
|
||||||
"secret_key": "",
|
|
||||||
"power": {
|
|
||||||
"text_to_image": 10,
|
|
||||||
"image_to_image": 15,
|
|
||||||
"image_edit": 20,
|
|
||||||
"image_effects": 25,
|
|
||||||
"text_to_video": 30,
|
|
||||||
"image_to_video": 35
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 使用流程
|
|
||||||
|
|
||||||
1. **初始配置**
|
|
||||||
|
|
||||||
- 访问管理后台即梦 AI 配置页面
|
|
||||||
- 填写 AccessKey 和 SecretKey
|
|
||||||
- 点击"测试连接"验证配置
|
|
||||||
- 调整各功能算力消耗
|
|
||||||
- 保存配置
|
|
||||||
|
|
||||||
2. **配置更新**
|
|
||||||
|
|
||||||
- 修改需要更新的配置项
|
|
||||||
- 保存配置
|
|
||||||
- 配置立即生效
|
|
||||||
|
|
||||||
3. **故障排查**
|
|
||||||
- 使用"测试连接"功能验证 API 密钥
|
|
||||||
- 检查配置是否正确保存
|
|
||||||
- 查看服务日志
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **权限要求**
|
|
||||||
|
|
||||||
- 只有管理员可以访问配置页面
|
|
||||||
- 需要有效的管理员登录会话
|
|
||||||
|
|
||||||
2. **配置验证**
|
|
||||||
|
|
||||||
- AccessKey 和 SecretKey 不能为空
|
|
||||||
- 算力消耗必须大于 0
|
|
||||||
- 建议先测试连接再保存配置
|
|
||||||
|
|
||||||
3. **服务影响**
|
|
||||||
- 配置更新不会影响正在进行的任务
|
|
||||||
- 新任务会使用更新后的配置
|
|
||||||
- 客户端配置会在下次请求时更新
|
|
||||||
|
|
||||||
## 错误处理
|
|
||||||
|
|
||||||
1. **配置加载失败**
|
|
||||||
|
|
||||||
- 使用默认配置
|
|
||||||
- 记录错误日志
|
|
||||||
|
|
||||||
2. **连接测试失败**
|
|
||||||
|
|
||||||
- 显示具体错误信息
|
|
||||||
- 建议检查 API 密钥
|
|
||||||
|
|
||||||
3. **配置保存失败**
|
|
||||||
- 显示错误信息
|
|
||||||
- 保留原有配置
|
|
||||||
|
|
||||||
## 开发说明
|
|
||||||
|
|
||||||
### 后端文件
|
|
||||||
|
|
||||||
- `api/handler/admin/jimeng_handler.go` - 配置管理 API
|
|
||||||
- `api/service/jimeng/service.go` - 配置服务逻辑
|
|
||||||
- `api/core/types/jimeng.go` - 配置类型定义
|
|
||||||
|
|
||||||
### 前端文件
|
|
||||||
|
|
||||||
- `web/src/views/admin/jimeng/JimengSetting.vue` - 配置页面
|
|
||||||
|
|
||||||
### 数据库
|
|
||||||
|
|
||||||
- `config`表存储配置信息
|
|
||||||
- 配置键:`jimeng`
|
|
||||||
- 配置值:JSON 格式
|
|
||||||
@@ -181,9 +181,6 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
|
|||||||
taskType = model.JMTaskTypeTextToVideo
|
taskType = model.JMTaskTypeTextToVideo
|
||||||
reqKey = jimeng.ReqKeyTextToVideo
|
reqKey = jimeng.ReqKeyTextToVideo
|
||||||
modelName = "即梦文生视频"
|
modelName = "即梦文生视频"
|
||||||
if req.Seed == 0 {
|
|
||||||
req.Seed = -1
|
|
||||||
}
|
|
||||||
if req.AspectRatio == "" {
|
if req.AspectRatio == "" {
|
||||||
req.AspectRatio = jimeng.AspectRatio16_9
|
req.AspectRatio = jimeng.AspectRatio16_9
|
||||||
}
|
}
|
||||||
@@ -196,9 +193,6 @@ func (h *JimengHandler) CreateTask(c *gin.Context) {
|
|||||||
taskType = model.JMTaskTypeImageToVideo
|
taskType = model.JMTaskTypeImageToVideo
|
||||||
reqKey = jimeng.ReqKeyImageToVideo
|
reqKey = jimeng.ReqKeyImageToVideo
|
||||||
modelName = "即梦图生视频"
|
modelName = "即梦图生视频"
|
||||||
if req.Seed == 0 {
|
|
||||||
req.Seed = -1
|
|
||||||
}
|
|
||||||
params = map[string]any{
|
params = map[string]any{
|
||||||
"seed": req.Seed,
|
"seed": req.Seed,
|
||||||
"aspect_ratio": req.AspectRatio,
|
"aspect_ratio": req.AspectRatio,
|
||||||
|
|||||||
@@ -775,6 +775,107 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 快捷操作按钮样式 */
|
||||||
|
&__works-item-quick-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__works-item-quick-action-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #6b7280;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
color: #ef4444;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #fef2f2;
|
||||||
|
color: #dc2626;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 错误信息样式 */
|
||||||
|
&__works-item-error {
|
||||||
|
margin-top: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #fef2f2;
|
||||||
|
border: 1px solid #fecaca;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__works-item-error-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__works-item-error-text {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #dc2626;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__works-item-error-copy-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fee2e2;
|
||||||
|
color: #dc2626;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #fecaca;
|
||||||
|
color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 旋转动画 */
|
/* 旋转动画 */
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { closeLoading, showLoading, showMessageError, showMessageOK } from '@/utils/dialog'
|
import { showMessageError, showMessageOK } from '@/utils/dialog'
|
||||||
import { httpGet, httpPost } from '@/utils/http'
|
import { httpGet, httpPost } from '@/utils/http'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { showConfirmDialog } from 'vant'
|
import { showConfirmDialog } from 'vant'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, reactive, ref, watch } from 'vue'
|
||||||
|
|
||||||
export const useJimengStore = defineStore('mobile-jimeng', () => {
|
export const useJimengStore = defineStore('mobile-jimeng', () => {
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
@@ -22,6 +22,16 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
const taskPulling = ref(true)
|
const taskPulling = ref(true)
|
||||||
const tastPullHandler = ref(null)
|
const tastPullHandler = ref(null)
|
||||||
|
|
||||||
|
// 新增:算力配置
|
||||||
|
const powerConfig = ref({
|
||||||
|
text_to_image: 20,
|
||||||
|
image_to_image: 30,
|
||||||
|
image_edit: 25,
|
||||||
|
image_effects: 15,
|
||||||
|
text_to_video: 100,
|
||||||
|
image_to_video: 120,
|
||||||
|
})
|
||||||
|
|
||||||
// 功能分类
|
// 功能分类
|
||||||
const categories = ref([
|
const categories = ref([
|
||||||
{ key: 'image_generation', name: '图像生成' },
|
{ key: 'image_generation', name: '图像生成' },
|
||||||
@@ -115,35 +125,45 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// 功能参数
|
// 功能参数
|
||||||
const textToImageParams = ref({
|
// 各功能的参数
|
||||||
size: '1024x1024',
|
const textToImageParams = reactive({
|
||||||
scale: 7.5,
|
size: '1328x1328',
|
||||||
use_pre_llm: false,
|
scale: 2.5,
|
||||||
|
seed: -1,
|
||||||
|
use_pre_llm: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const imageToImageParams = ref({
|
const imageToImageParams = reactive({
|
||||||
image_input: [],
|
image_input: '',
|
||||||
size: '1024x1024',
|
size: '1328x1328',
|
||||||
|
gpen: 0.4,
|
||||||
|
skin: 0.3,
|
||||||
|
skin_unifi: 0,
|
||||||
|
gen_mode: 'creative',
|
||||||
|
seed: -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const imageEditParams = ref({
|
const imageEditParams = reactive({
|
||||||
image_urls: [],
|
image_urls: '',
|
||||||
scale: 0.5,
|
scale: 0.5,
|
||||||
|
seed: -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const imageEffectsParams = ref({
|
const imageEffectsParams = reactive({
|
||||||
image_input1: [],
|
image_input1: '',
|
||||||
template_id: '',
|
template_id: '',
|
||||||
size: '1024x1024',
|
size: '1328x1328',
|
||||||
})
|
})
|
||||||
|
|
||||||
const textToVideoParams = ref({
|
const textToVideoParams = reactive({
|
||||||
aspect_ratio: '16:9',
|
aspect_ratio: '16:9',
|
||||||
|
seed: -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const imageToVideoParams = ref({
|
const imageToVideoParams = reactive({
|
||||||
image_urls: [],
|
image_urls: [],
|
||||||
aspect_ratio: '16:9',
|
aspect_ratio: '16:9',
|
||||||
|
seed: -1,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
@@ -152,12 +172,29 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
return useImageInput.value ? 'image_to_image' : 'text_to_image'
|
return useImageInput.value ? 'image_to_image' : 'text_to_image'
|
||||||
} else if (activeCategory.value === 'image_editing') {
|
} else if (activeCategory.value === 'image_editing') {
|
||||||
return 'image_edit'
|
return 'image_edit'
|
||||||
|
} else if (activeCategory.value === 'image_effects') {
|
||||||
|
return 'image_effects'
|
||||||
} else if (activeCategory.value === 'video_generation') {
|
} else if (activeCategory.value === 'video_generation') {
|
||||||
return useImageInput.value ? 'image_to_video' : 'text_to_video'
|
return useImageInput.value ? 'image_to_video' : 'text_to_video'
|
||||||
}
|
}
|
||||||
return 'text_to_image'
|
return 'text_to_image'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 新增:动态计算当前算力消耗
|
||||||
|
const updateCurrentPowerCost = () => {
|
||||||
|
const functionKey = activeFunction.value
|
||||||
|
currentPowerCost.value = powerConfig.value[functionKey] || 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听任务类型变化,自动更新算力
|
||||||
|
watch(
|
||||||
|
[activeCategory, useImageInput],
|
||||||
|
() => {
|
||||||
|
updateCurrentPowerCost()
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const getCategoryIcon = (category) => {
|
const getCategoryIcon = (category) => {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
@@ -174,52 +211,17 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
useImageInput.value = false
|
useImageInput.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const switchInputMode = () => {
|
// 新增:获取算力配置
|
||||||
currentPrompt.value = ''
|
const fetchPowerConfig = async () => {
|
||||||
}
|
try {
|
||||||
|
const res = await httpGet('/api/jimeng/power-config')
|
||||||
const handleMultipleImageUpload = (event) => {
|
if (res.data) {
|
||||||
const files = Array.from(event.target.files)
|
powerConfig.value = res.data
|
||||||
files.forEach((file) => {
|
updateCurrentPowerCost() // 更新当前算力消耗
|
||||||
if (imageToVideoParams.value.image_urls.length < 2) {
|
|
||||||
onImageUpload({ file, name: file.name })
|
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
}
|
console.error('获取算力配置失败:', error)
|
||||||
|
}
|
||||||
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 = () => {
|
const submitTask = () => {
|
||||||
@@ -229,27 +231,62 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submitting.value = true
|
submitting.value = true
|
||||||
const params = {
|
let requestData = { task_type: activeFunction.value, prompt: currentPrompt.value }
|
||||||
type: activeFunction.value,
|
|
||||||
prompt: currentPrompt.value,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据功能类型添加相应参数
|
// 根据功能类型添加相应参数
|
||||||
if (activeFunction.value === 'text_to_image') {
|
switch (activeFunction.value) {
|
||||||
Object.assign(params, textToImageParams.value)
|
case 'text_to_image':
|
||||||
} else if (activeFunction.value === 'image_to_image') {
|
Object.assign(requestData, {
|
||||||
Object.assign(params, imageToImageParams.value)
|
width: parseInt(textToImageParams.size.split('x')[0]),
|
||||||
} else if (activeFunction.value === 'image_edit') {
|
height: parseInt(textToImageParams.size.split('x')[1]),
|
||||||
Object.assign(params, imageEditParams.value)
|
scale: textToImageParams.scale,
|
||||||
} else if (activeFunction.value === 'image_effects') {
|
seed: textToImageParams.seed,
|
||||||
Object.assign(params, imageEffectsParams.value)
|
use_pre_llm: textToImageParams.use_pre_llm,
|
||||||
} else if (activeFunction.value === 'text_to_video') {
|
})
|
||||||
Object.assign(params, textToVideoParams.value)
|
break
|
||||||
} else if (activeFunction.value === 'image_to_video') {
|
case 'image_to_image':
|
||||||
Object.assign(params, imageToVideoParams.value)
|
Object.assign(requestData, {
|
||||||
|
image_input: imageToImageParams.image_input,
|
||||||
|
width: parseInt(imageToImageParams.size.split('x')[0]),
|
||||||
|
height: parseInt(imageToImageParams.size.split('x')[1]),
|
||||||
|
gpen: imageToImageParams.gpen,
|
||||||
|
skin: imageToImageParams.skin,
|
||||||
|
skin_unifi: imageToImageParams.skin_unifi,
|
||||||
|
gen_mode: imageToImageParams.gen_mode,
|
||||||
|
seed: imageToImageParams.seed,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'image_edit':
|
||||||
|
Object.assign(requestData, {
|
||||||
|
image_urls: [imageEditParams.image_urls],
|
||||||
|
scale: imageEditParams.scale,
|
||||||
|
seed: imageEditParams.seed,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'image_effects':
|
||||||
|
Object.assign(requestData, {
|
||||||
|
image_input: imageEffectsParams.image_input1,
|
||||||
|
template_id: imageEffectsParams.template_id,
|
||||||
|
width: parseInt(imageEffectsParams.size.split('x')[0]),
|
||||||
|
height: parseInt(imageEffectsParams.size.split('x')[1]),
|
||||||
|
prompt: imageEffectsParams.prompt,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'text_to_video':
|
||||||
|
Object.assign(requestData, {
|
||||||
|
aspect_ratio: textToVideoParams.aspect_ratio,
|
||||||
|
seed: textToVideoParams.seed,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 'image_to_video':
|
||||||
|
Object.assign(requestData, {
|
||||||
|
image_urls: imageToVideoParams.image_urls,
|
||||||
|
aspect_ratio: imageToVideoParams.aspect_ratio,
|
||||||
|
seed: imageToVideoParams.seed,
|
||||||
|
})
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpPost('/api/jimeng/create', params)
|
return httpPost('/api/jimeng/task', requestData)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
fetchData(1)
|
fetchData(1)
|
||||||
taskPulling.value = true
|
taskPulling.value = true
|
||||||
@@ -333,7 +370,7 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeJob = (item) => {
|
const removeJob = async (item) => {
|
||||||
return showConfirmDialog({
|
return showConfirmDialog({
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
message: '此操作将会删除任务相关文件,继续操作吗?',
|
message: '此操作将会删除任务相关文件,继续操作吗?',
|
||||||
@@ -383,39 +420,94 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = () => {
|
const closeMediaDialog = () => {
|
||||||
showMediaDialog.value = false
|
showMediaDialog.value = false
|
||||||
currentMediaUrl.value = ''
|
currentMediaUrl.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 新增:画同款功能
|
||||||
|
const drawSame = (item) => {
|
||||||
|
// 设置当前提示词
|
||||||
|
currentPrompt.value = item.prompt
|
||||||
|
|
||||||
|
// 根据任务类型设置相应的参数
|
||||||
|
switch (item.type) {
|
||||||
|
case 'text_to_image':
|
||||||
|
activeCategory.value = 'image_generation'
|
||||||
|
useImageInput.value = false
|
||||||
|
// 设置图片尺寸(如果有的话)
|
||||||
|
if (item.width && item.height) {
|
||||||
|
textToImageParams.size = `${item.width}x${item.height}`
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'image_to_image':
|
||||||
|
activeCategory.value = 'image_generation'
|
||||||
|
useImageInput.value = true
|
||||||
|
// 设置图片尺寸(如果有的话)
|
||||||
|
if (item.width && item.height) {
|
||||||
|
imageToImageParams.size = `${item.width}x${item.height}`
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'image_edit':
|
||||||
|
activeCategory.value = 'image_editing'
|
||||||
|
break
|
||||||
|
case 'image_effects':
|
||||||
|
activeCategory.value = 'image_effects'
|
||||||
|
// 设置特效模板(如果有的话)
|
||||||
|
if (item.template_id) {
|
||||||
|
imageEffectsParams.template_id = item.template_id
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'text_to_video':
|
||||||
|
activeCategory.value = 'video_generation'
|
||||||
|
useImageInput.value = false
|
||||||
|
// 设置视频比例(如果有的话)
|
||||||
|
if (item.aspect_ratio) {
|
||||||
|
textToVideoParams.aspect_ratio = item.aspect_ratio
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'image_to_video':
|
||||||
|
activeCategory.value = 'video_generation'
|
||||||
|
useImageInput.value = true
|
||||||
|
// 设置视频比例(如果有的话)
|
||||||
|
if (item.aspect_ratio) {
|
||||||
|
imageToVideoParams.aspect_ratio = item.aspect_ratio
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
showMessageOK('已设置画同款参数')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:复制提示词功能
|
||||||
|
const copyPrompt = (prompt) => {
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(prompt)
|
||||||
|
.then(() => {
|
||||||
|
showMessageOK('提示词已复制')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showMessageError('复制失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:复制错误信息功能
|
||||||
|
const copyErrorMsg = (msg) => {
|
||||||
|
navigator.clipboard
|
||||||
|
.writeText(msg)
|
||||||
|
.then(() => {
|
||||||
|
showMessageOK('错误信息已复制')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showMessageError('复制失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:初始化方法
|
||||||
|
const init = async () => {
|
||||||
|
await fetchPowerConfig()
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
activeCategory,
|
activeCategory,
|
||||||
@@ -443,6 +535,7 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
imageEffectsParams,
|
imageEffectsParams,
|
||||||
textToVideoParams,
|
textToVideoParams,
|
||||||
imageToVideoParams,
|
imageToVideoParams,
|
||||||
|
powerConfig,
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
activeFunction,
|
activeFunction,
|
||||||
@@ -450,10 +543,6 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
// Actions
|
// Actions
|
||||||
getCategoryIcon,
|
getCategoryIcon,
|
||||||
switchCategory,
|
switchCategory,
|
||||||
switchInputMode,
|
|
||||||
handleMultipleImageUpload,
|
|
||||||
removeImage,
|
|
||||||
onImageUpload,
|
|
||||||
submitTask,
|
submitTask,
|
||||||
fetchData,
|
fetchData,
|
||||||
loadMore,
|
loadMore,
|
||||||
@@ -465,7 +554,11 @@ export const useJimengStore = defineStore('mobile-jimeng', () => {
|
|||||||
getTaskType,
|
getTaskType,
|
||||||
startTaskPolling,
|
startTaskPolling,
|
||||||
stopTaskPolling,
|
stopTaskPolling,
|
||||||
resetParams,
|
|
||||||
closeMediaDialog,
|
closeMediaDialog,
|
||||||
|
fetchPowerConfig,
|
||||||
|
drawSame,
|
||||||
|
copyPrompt,
|
||||||
|
copyErrorMsg,
|
||||||
|
init,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { getSystemInfo } from '@/store/cache'
|
||||||
import { ref, reactive } from 'vue'
|
|
||||||
import { checkSession } from '@/store/cache'
|
|
||||||
import { closeLoading, showLoading, showToastMessage } from '@/utils/dialog'
|
import { closeLoading, showLoading, showToastMessage } from '@/utils/dialog'
|
||||||
import { httpDownload, httpGet, httpPost } from '@/utils/http'
|
import { httpDownload, httpGet, httpPost } from '@/utils/http'
|
||||||
import { replaceImg } from '@/utils/libs'
|
import { replaceImg } from '@/utils/libs'
|
||||||
import { getSystemInfo } from '@/store/cache'
|
import { defineStore } from 'pinia'
|
||||||
|
import { showConfirmDialog } from 'vant'
|
||||||
|
import { reactive, ref } from 'vue'
|
||||||
|
|
||||||
export const useSunoStore = defineStore('suno', () => {
|
export const useSunoStore = defineStore('suno', () => {
|
||||||
// 状态
|
// 状态
|
||||||
@@ -35,7 +35,6 @@ export const useSunoStore = defineStore('suno', () => {
|
|||||||
const uploadRef = ref(null)
|
const uploadRef = ref(null)
|
||||||
const isGenerating = ref(false)
|
const isGenerating = ref(false)
|
||||||
const deleting = ref(false)
|
const deleting = ref(false)
|
||||||
const deleteItem = ref(null)
|
|
||||||
const models = ref([
|
const models = ref([
|
||||||
{ label: 'v3.0', value: 'chirp-v3-0' },
|
{ label: 'v3.0', value: 'chirp-v3-0' },
|
||||||
{ label: 'v3.5', value: 'chirp-v3-5' },
|
{ label: 'v3.5', value: 'chirp-v3-5' },
|
||||||
@@ -287,10 +286,25 @@ export const useSunoStore = defineStore('suno', () => {
|
|||||||
item.downloading = false
|
item.downloading = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const showDeleteDialog = (item) => {
|
|
||||||
deleteItem.value = item
|
const removeJob = (item) => {
|
||||||
// 这里建议在页面层处理弹窗,store 只负责数据和业务
|
showConfirmDialog({
|
||||||
|
title: '确认删除',
|
||||||
|
message: '此操作将会删除任务相关文件,继续操作吗?',
|
||||||
|
confirmButtonText: '确认删除',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
}).then(() => {
|
||||||
|
httpGet('/api/suno/remove', { id: item.id })
|
||||||
|
.then(() => {
|
||||||
|
showToastMessage('任务删除成功', 'success')
|
||||||
|
fetchData(1)
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
showToastMessage('任务删除失败', 'error')
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const extend = (item) => {
|
const extend = (item) => {
|
||||||
refSong.value = item
|
refSong.value = item
|
||||||
refSong.value.extend_secs = item.duration
|
refSong.value.extend_secs = item.duration
|
||||||
@@ -324,7 +338,6 @@ export const useSunoStore = defineStore('suno', () => {
|
|||||||
uploadRef,
|
uploadRef,
|
||||||
isGenerating,
|
isGenerating,
|
||||||
deleting,
|
deleting,
|
||||||
deleteItem,
|
|
||||||
models,
|
models,
|
||||||
tags,
|
tags,
|
||||||
page,
|
page,
|
||||||
@@ -346,7 +359,7 @@ export const useSunoStore = defineStore('suno', () => {
|
|||||||
refreshFirstPage,
|
refreshFirstPage,
|
||||||
play,
|
play,
|
||||||
download,
|
download,
|
||||||
showDeleteDialog,
|
removeJob,
|
||||||
extend,
|
extend,
|
||||||
removeRefSong,
|
removeRefSong,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,11 +49,7 @@
|
|||||||
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
<div class="bg-white rounded-xl p-4 shadow-sm mb-3">
|
||||||
<div class="flex justify-between items-center w-full">
|
<div class="flex justify-between items-center w-full">
|
||||||
<span class="text-gray-700 font-semibold">图生图人像写真</span>
|
<span class="text-gray-700 font-semibold">图生图人像写真</span>
|
||||||
<el-switch
|
<el-switch v-model="jimengStore.useImageInput" size="default" />
|
||||||
v-model="jimengStore.useImageInput"
|
|
||||||
@change="jimengStore.switchInputMode"
|
|
||||||
size="default"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -284,9 +280,9 @@
|
|||||||
<el-image
|
<el-image
|
||||||
v-if="item.img_url"
|
v-if="item.img_url"
|
||||||
:src="item.img_url"
|
:src="item.img_url"
|
||||||
|
:preview-src-list="[item.img_url]"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
class="w-full h-full"
|
class="w-full h-full"
|
||||||
:preview-disabled="true"
|
|
||||||
>
|
>
|
||||||
<template #error>
|
<template #error>
|
||||||
<div class="jimeng-create__works-item-thumb-placeholder">
|
<div class="jimeng-create__works-item-thumb-placeholder">
|
||||||
@@ -294,19 +290,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
<el-image
|
|
||||||
v-else-if="item.video_url"
|
|
||||||
:src="item.video_url"
|
|
||||||
fit="cover"
|
|
||||||
class="w-full h-full"
|
|
||||||
:preview-disabled="true"
|
|
||||||
>
|
|
||||||
<template #error>
|
|
||||||
<div class="jimeng-create__works-item-thumb-placeholder">
|
|
||||||
<i class="iconfont icon-video"></i>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-image>
|
|
||||||
<div v-else class="jimeng-create__works-item-thumb-placeholder">
|
<div v-else class="jimeng-create__works-item-thumb-placeholder">
|
||||||
<i
|
<i
|
||||||
:class="
|
:class="
|
||||||
@@ -326,13 +309,7 @@
|
|||||||
"
|
"
|
||||||
></i>
|
></i>
|
||||||
</button>
|
</button>
|
||||||
<!-- 进度动画 -->
|
|
||||||
<div
|
|
||||||
v-if="item.status === 'in_queue' || item.status === 'generating'"
|
|
||||||
class="jimeng-create__works-item-thumb-status jimeng-create__works-item-thumb-status--loading"
|
|
||||||
>
|
|
||||||
<i class="iconfont icon-loading animate-spin"></i>
|
|
||||||
</div>
|
|
||||||
<!-- 失败状态 -->
|
<!-- 失败状态 -->
|
||||||
<div
|
<div
|
||||||
v-if="item.status === 'failed'"
|
v-if="item.status === 'failed'"
|
||||||
@@ -392,45 +369,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 快捷操作按钮 -->
|
||||||
<div class="jimeng-create__works-item-actions">
|
<div class="jimeng-create__works-item-quick-actions">
|
||||||
<div class="jimeng-create__works-item-actions-left">
|
<!-- 复制提示词 -->
|
||||||
|
<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'">
|
||||||
|
<!-- 画同款 -->
|
||||||
<button
|
<button
|
||||||
v-if="item.status === 'completed'"
|
@click="jimengStore.drawSame(item)"
|
||||||
@click="jimengStore.playMedia(item)"
|
class="jimeng-create__works-item-quick-action-btn"
|
||||||
class="jimeng-create__works-item-actions-btn jimeng-create__works-item-actions-btn--primary"
|
title="画同款"
|
||||||
>
|
>
|
||||||
<i
|
<i class="iconfont icon-image-list"></i>
|
||||||
:class="item.type.includes('video') ? 'iconfont icon-play' : 'iconfont icon-eye'"
|
|
||||||
></i>
|
|
||||||
<span>{{ item.type.includes('video') ? '播放' : '查看' }}</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
<!-- 下载 -->
|
||||||
<button
|
<button
|
||||||
v-if="item.status === 'completed'"
|
v-if="item.status === 'completed' && (item.img_url || item.video_url)"
|
||||||
@click="jimengStore.downloadFile(item)"
|
@click="jimengStore.downloadFile(item)"
|
||||||
:disabled="item.downloading"
|
:disabled="item.downloading"
|
||||||
class="jimeng-create__works-item-actions-btn jimeng-create__works-item-actions-btn--success"
|
class="jimeng-create__works-item-quick-action-btn"
|
||||||
|
title="下载"
|
||||||
>
|
>
|
||||||
<i v-if="item.downloading" class="iconfont icon-loading animate-spin"></i>
|
<i v-if="item.downloading" class="iconfont icon-loading animate-spin"></i>
|
||||||
<i v-else class="iconfont icon-download"></i>
|
<i v-else class="iconfont icon-download"></i></button
|
||||||
<span>{{ item.downloading ? '下载中...' : '下载' }}</span>
|
></span>
|
||||||
</button>
|
|
||||||
|
<!-- 重试 -->
|
||||||
|
<button
|
||||||
|
v-if="item.status === 'failed'"
|
||||||
|
@click="jimengStore.retryTask(item.id)"
|
||||||
|
class="jimeng-create__works-item-quick-action-btn"
|
||||||
|
title="重试"
|
||||||
|
>
|
||||||
|
<i class="iconfont icon-refresh"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<button @click="jimengStore.removeJob(item)" class="p-2">
|
||||||
|
<i class="iconfont icon-remove"></i> 删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 错误信息复制 -->
|
||||||
|
<div
|
||||||
|
v-if="item.status === 'failed' && item.err_msg"
|
||||||
|
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>
|
||||||
<button
|
<button
|
||||||
v-if="item.status === 'failed'"
|
@click="jimengStore.copyErrorMsg(item.err_msg)"
|
||||||
@click="jimengStore.retryTask(item.id)"
|
class="jimeng-create__works-item-error-copy-btn"
|
||||||
class="jimeng-create__works-item-actions-btn jimeng-create__works-item-actions-btn--warning"
|
title="复制错误信息"
|
||||||
>
|
>
|
||||||
<i class="iconfont icon-refresh"></i>
|
<i class="iconfont icon-copy"></i>
|
||||||
<span>重试</span>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
@click="jimengStore.removeJob(item)"
|
|
||||||
class="jimeng-create__works-item-actions-btn jimeng-create__works-item-actions-btn--danger"
|
|
||||||
>
|
|
||||||
<i class="iconfont icon-remove"></i>
|
|
||||||
<span>删除</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -515,6 +517,7 @@ const handleTemplateChange = (value) => {
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkSession()
|
checkSession()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
jimengStore.init() // 初始化算力配置
|
||||||
jimengStore.fetchData(1)
|
jimengStore.fetchData(1)
|
||||||
jimengStore.startTaskPolling()
|
jimengStore.startTaskPolling()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -388,7 +388,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@click="showDeleteDialog(item)"
|
@click="suno.removeJob(item)"
|
||||||
class="px-3 py-1.5 bg-red-100 text-red-600 text-sm rounded-lg hover:bg-red-200 transition-colors flex items-center space-x-1"
|
class="px-3 py-1.5 bg-red-100 text-red-600 text-sm rounded-lg hover:bg-red-200 transition-colors flex items-center space-x-1"
|
||||||
>
|
>
|
||||||
<i class="iconfont icon-remove !text-xs"></i>
|
<i class="iconfont icon-remove !text-xs"></i>
|
||||||
@@ -494,7 +494,6 @@
|
|||||||
import '@/assets/css/mobile/suno.scss'
|
import '@/assets/css/mobile/suno.scss'
|
||||||
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
import CustomSelect from '@/components/mobile/CustomSelect.vue'
|
||||||
import { useSunoStore } from '@/store/mobile/suno'
|
import { useSunoStore } from '@/store/mobile/suno'
|
||||||
import { showConfirmDialog } from 'vant'
|
|
||||||
import { onMounted, onUnmounted } from 'vue'
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
@@ -536,28 +535,6 @@ onUnmounted(() => {
|
|||||||
if (tastPullHandler) clearInterval(tastPullHandler)
|
if (tastPullHandler) clearInterval(tastPullHandler)
|
||||||
window.removeEventListener('scroll', handleScroll)
|
window.removeEventListener('scroll', handleScroll)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 删除弹窗(页面层处理)
|
|
||||||
const showDeleteDialog = (item) => {
|
|
||||||
suno.deleteItem = item
|
|
||||||
showConfirmDialog({
|
|
||||||
title: '确认删除',
|
|
||||||
message: '此操作将会删除任务相关文件,继续操作吗?',
|
|
||||||
confirmButtonText: '确认删除',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
if (!suno.deleteItem) return
|
|
||||||
suno.deleting = true
|
|
||||||
suno.deleteItem && suno.deleteItem.id && suno.$patch({ deleting: true })
|
|
||||||
suno.deleteItem && suno.deleteItem.id && suno.$patch({ deleting: false })
|
|
||||||
suno.deleteItem = null
|
|
||||||
suno.fetchData(1)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
suno.deleteItem = null
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user