feat: 图片压缩后上传COS + 修复订单LazyInitializationException + 添加调试日志
This commit is contained in:
@@ -6,7 +6,7 @@ export const cleanupApi = {
|
||||
// 获取清理统计信息
|
||||
getCleanupStats() {
|
||||
return request({
|
||||
url: '/api/cleanup/cleanup-stats',
|
||||
url: '/cleanup/cleanup-stats',
|
||||
method: 'GET'
|
||||
})
|
||||
},
|
||||
@@ -14,7 +14,7 @@ export const cleanupApi = {
|
||||
// 执行完整清理
|
||||
performFullCleanup() {
|
||||
return request({
|
||||
url: '/api/cleanup/full-cleanup',
|
||||
url: '/cleanup/full-cleanup',
|
||||
method: 'POST'
|
||||
})
|
||||
},
|
||||
@@ -22,7 +22,7 @@ export const cleanupApi = {
|
||||
// 清理指定用户任务
|
||||
cleanupUserTasks(username) {
|
||||
return request({
|
||||
url: `/api/cleanup/user-tasks/${username}`,
|
||||
url: `/cleanup/user-tasks/${username}`,
|
||||
method: 'POST'
|
||||
})
|
||||
},
|
||||
|
||||
@@ -65,3 +65,13 @@ export const getPaymentStats = () => {
|
||||
export const getUserSubscriptionInfo = () => {
|
||||
return api.get('/payments/subscription/info')
|
||||
}
|
||||
|
||||
// 删除单个支付记录
|
||||
export const deletePayment = (id) => {
|
||||
return api.delete(`/payments/${id}`)
|
||||
}
|
||||
|
||||
// 批量删除支付记录
|
||||
export const deletePayments = (paymentIds) => {
|
||||
return api.delete('/payments/batch', { data: paymentIds })
|
||||
}
|
||||
|
||||
@@ -629,7 +629,11 @@ export default {
|
||||
alipay: 'Alipay',
|
||||
wechat: 'WeChat Pay',
|
||||
paypal: 'PayPal',
|
||||
selected: '{count} selected'
|
||||
selected: '{count} selected',
|
||||
orderDetail: 'Order Detail',
|
||||
basicInfo: 'Basic Info',
|
||||
orderType: 'Order Type',
|
||||
paymentInfo: 'Payment Info'
|
||||
},
|
||||
|
||||
tasks: {
|
||||
@@ -648,7 +652,8 @@ export default {
|
||||
cancelled: 'Cancelled',
|
||||
textToVideo: 'Text to Video',
|
||||
imageToVideo: 'Image to Video',
|
||||
storyboardVideo: 'Storyboard Video'
|
||||
storyboardVideo: 'Storyboard Video',
|
||||
taskDetail: 'Task Detail'
|
||||
},
|
||||
|
||||
members: {
|
||||
@@ -740,6 +745,12 @@ export default {
|
||||
promptOptimizationApiUrl: 'API Endpoint',
|
||||
promptOptimizationApiUrlTip: 'Enter the API endpoint for prompt optimization, e.g., https://api.openai.com',
|
||||
promptOptimizationModel: 'Model Name',
|
||||
promptOptimizationModelTip: 'Enter the model name for prompt optimization, e.g., gpt-4o, gemini-pro'
|
||||
promptOptimizationModelTip: 'Enter the model name for prompt optimization, e.g., gpt-4o, gemini-pro',
|
||||
storyboardSystemPrompt: 'Storyboard System Prompt',
|
||||
storyboardSystemPromptTip: 'This prompt will be prepended to user prompts for consistent storyboard generation style',
|
||||
storyboardSystemPromptPlaceholder: 'E.g., high quality cinematic shot, professional photography, film tone...',
|
||||
promptOptimizationSystemPrompt: 'Prompt Optimization System Instruction',
|
||||
promptOptimizationSystemPromptTip: 'Custom system instruction for AI prompt optimization. Leave empty to use default. This instruction determines how AI understands and optimizes user prompts',
|
||||
promptOptimizationSystemPromptPlaceholder: 'E.g., You are a professional AI prompt optimization expert, transform user descriptions into detailed, professional English prompts...'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,7 +642,11 @@ export default {
|
||||
alipay: '支付宝',
|
||||
wechat: '微信支付',
|
||||
paypal: 'PayPal',
|
||||
selected: '已选择{count}项'
|
||||
selected: '已选择{count}项',
|
||||
orderDetail: '订单详情',
|
||||
basicInfo: '基本信息',
|
||||
orderType: '订单类型',
|
||||
paymentInfo: '支付信息'
|
||||
},
|
||||
|
||||
tasks: {
|
||||
@@ -661,7 +665,8 @@ export default {
|
||||
cancelled: '已取消',
|
||||
textToVideo: '文生视频',
|
||||
imageToVideo: '图生视频',
|
||||
storyboardVideo: '分镜视频'
|
||||
storyboardVideo: '分镜视频',
|
||||
taskDetail: '任务详情'
|
||||
},
|
||||
|
||||
members: {
|
||||
@@ -753,6 +758,12 @@ export default {
|
||||
promptOptimizationApiUrl: 'API端点',
|
||||
promptOptimizationApiUrlTip: '输入用于优化提示词的API端点地址,如 https://api.openai.com',
|
||||
promptOptimizationModel: '模型名称',
|
||||
promptOptimizationModelTip: '输入用于优化提示词的模型名称,如 gpt-4o、gemini-pro 等'
|
||||
promptOptimizationModelTip: '输入用于优化提示词的模型名称,如 gpt-4o、gemini-pro 等',
|
||||
storyboardSystemPrompt: '分镜图系统引导词',
|
||||
storyboardSystemPromptTip: '此引导词会自动添加到用户提示词前面,用于统一分镜图生成风格',
|
||||
storyboardSystemPromptPlaceholder: '例如:高质量电影级画面,专业摄影,电影色调...',
|
||||
promptOptimizationSystemPrompt: '优化提示词系统指令',
|
||||
promptOptimizationSystemPromptTip: '自定义AI优化提示词的系统指令,留空则使用默认指令。该指令决定了AI如何理解和优化用户输入的提示词',
|
||||
promptOptimizationSystemPromptPlaceholder: '例如:你是一个专业的AI提示词优化专家,将用户描述优化为详细、专业的英文提示词...'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,13 +102,10 @@
|
||||
<el-option :label="$t('orders.cancelled')" value="CANCELLED" />
|
||||
<el-option :label="$t('orders.refunded')" value="REFUNDED" />
|
||||
</el-select>
|
||||
<el-select v-model="filters.type" :placeholder="$t('orders.allTypes')" size="small" @change="handleFilterChange">
|
||||
<el-option :label="$t('orders.allTypes')" value="" />
|
||||
<el-option label="商品订单" value="PRODUCT" />
|
||||
<el-option label="服务订单" value="SERVICE" />
|
||||
<el-option label="订阅订单" value="SUBSCRIPTION" />
|
||||
<el-option label="数字商品" value="DIGITAL" />
|
||||
<el-option label="实体商品" value="PHYSICAL" />
|
||||
<el-select v-model="filters.paymentMethod" :placeholder="$t('orders.allPaymentMethods') || '全部支付方式'" size="small" @change="handleFilterChange">
|
||||
<el-option :label="$t('orders.allPaymentMethods') || '全部支付方式'" value="" />
|
||||
<el-option :label="$t('orders.alipay') || '支付宝'" value="ALIPAY" />
|
||||
<el-option :label="$t('orders.paypal') || 'PayPal'" value="PAYPAL" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
@@ -200,6 +197,92 @@
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 订单详情弹窗 -->
|
||||
<el-dialog
|
||||
v-model="orderDetailVisible"
|
||||
:title="$t('orders.orderDetail') || '订单详情'"
|
||||
width="600px"
|
||||
class="order-detail-dialog"
|
||||
>
|
||||
<div v-if="currentOrderDetail" class="order-detail-content">
|
||||
<div class="detail-section">
|
||||
<h4>{{ $t('orders.basicInfo') || '基本信息' }}</h4>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.orderNumber') }}:</span>
|
||||
<span class="value">{{ currentOrderDetail.orderNumber || currentOrderDetail.id }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.username') }}:</span>
|
||||
<span class="value">{{ currentOrderDetail.user?.username || '-' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.orderType') || '订单类型' }}:</span>
|
||||
<span class="value">{{ getOrderTypeText(currentOrderDetail.orderType) }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.status') }}:</span>
|
||||
<span class="status-tag" :class="getStatusClass(currentOrderDetail.status)">
|
||||
{{ getStatusText(currentOrderDetail.status) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h4>{{ $t('orders.paymentInfo') || '支付信息' }}</h4>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.amount') }}:</span>
|
||||
<span class="value amount">{{ currentOrderDetail.currency || '¥' }}{{ currentOrderDetail.totalAmount || 0 }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.paymentMethod') }}:</span>
|
||||
<span class="value">
|
||||
<template v-if="currentOrderDetail.paymentMethod === 'ALIPAY'">{{ $t('orders.alipay') }}</template>
|
||||
<template v-else-if="currentOrderDetail.paymentMethod === 'WECHAT'">{{ $t('orders.wechat') }}</template>
|
||||
<template v-else-if="currentOrderDetail.paymentMethod === 'PAYPAL'">{{ $t('orders.paypal') }}</template>
|
||||
<template v-else>{{ $t('orders.unpaid') }}</template>
|
||||
</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">{{ $t('orders.createTime') }}:</span>
|
||||
<span class="value">{{ formatDate(currentOrderDetail.createdAt) }}</span>
|
||||
</div>
|
||||
<div class="detail-item" v-if="currentOrderDetail.paidAt">
|
||||
<span class="label">{{ $t('orders.paidTime') || '支付时间' }}:</span>
|
||||
<span class="value">{{ formatDate(currentOrderDetail.paidAt) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentOrderDetail.contactEmail || currentOrderDetail.contactPhone">
|
||||
<h4>{{ $t('orders.contactInfo') || '联系信息' }}</h4>
|
||||
<div class="detail-grid">
|
||||
<div class="detail-item" v-if="currentOrderDetail.contactEmail">
|
||||
<span class="label">{{ $t('orders.email') || '邮箱' }}:</span>
|
||||
<span class="value">{{ currentOrderDetail.contactEmail }}</span>
|
||||
</div>
|
||||
<div class="detail-item" v-if="currentOrderDetail.contactPhone">
|
||||
<span class="label">{{ $t('orders.phone') || '电话' }}:</span>
|
||||
<span class="value">{{ currentOrderDetail.contactPhone }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentOrderDetail.description">
|
||||
<h4>{{ $t('orders.description') || '订单描述' }}</h4>
|
||||
<p class="description-text">{{ currentOrderDetail.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading-container">
|
||||
<el-icon class="is-loading"><Loading /></el-icon>
|
||||
<span>{{ $t('common.loading') || '加载中...' }}</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="orderDetailVisible = false">{{ $t('common.close') || '关闭' }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -219,7 +302,8 @@ import {
|
||||
ArrowRight,
|
||||
Delete,
|
||||
CreditCard,
|
||||
Wallet
|
||||
Wallet,
|
||||
Loading
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getOrders, getAdminOrders, getOrderStats, deleteOrder as deleteOrderAPI, deleteOrders } from '@/api/orders'
|
||||
import LanguageSwitcher from '@/components/LanguageSwitcher.vue'
|
||||
@@ -245,7 +329,7 @@ const systemUptime = ref('加载中...')
|
||||
// 筛选条件
|
||||
const filters = reactive({
|
||||
status: '',
|
||||
type: '',
|
||||
paymentMethod: '',
|
||||
search: ''
|
||||
})
|
||||
|
||||
@@ -417,8 +501,13 @@ const goToPage = (page) => {
|
||||
fetchOrders()
|
||||
}
|
||||
|
||||
const viewOrder = (order) => {
|
||||
router.push(`/orders/${order.id}`)
|
||||
// 订单详情弹窗相关
|
||||
const orderDetailVisible = ref(false)
|
||||
const currentOrderDetail = ref(null)
|
||||
|
||||
const viewOrder = async (order) => {
|
||||
currentOrderDetail.value = order
|
||||
orderDetailVisible.value = true
|
||||
}
|
||||
|
||||
const deleteOrder = async (order) => {
|
||||
@@ -433,19 +522,24 @@ const deleteOrder = async (order) => {
|
||||
}
|
||||
)
|
||||
|
||||
await deleteOrderAPI(order.id)
|
||||
const response = await deleteOrderAPI(order.id)
|
||||
console.log('删除订单响应:', response)
|
||||
|
||||
const index = orders.value.findIndex(o => o.id === order.id)
|
||||
if (index > -1) {
|
||||
orders.value.splice(index, 1)
|
||||
totalOrders.value--
|
||||
// 检查响应状态
|
||||
if (response.data?.success) {
|
||||
const index = orders.value.findIndex(o => o.id === order.id)
|
||||
if (index > -1) {
|
||||
orders.value.splice(index, 1)
|
||||
totalOrders.value--
|
||||
}
|
||||
ElMessage.success('删除成功')
|
||||
} else {
|
||||
ElMessage.error(response.data?.message || '删除失败')
|
||||
}
|
||||
|
||||
ElMessage.success('删除成功')
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error)
|
||||
ElMessage.error('删除失败')
|
||||
ElMessage.error('删除失败: ' + (error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -468,17 +562,21 @@ const deleteSelected = async () => {
|
||||
)
|
||||
|
||||
const ids = selectedOrders.value.map(o => o.id)
|
||||
await deleteOrders(ids)
|
||||
const response = await deleteOrders(ids)
|
||||
console.log('批量删除订单响应:', response)
|
||||
|
||||
orders.value = orders.value.filter(o => !ids.includes(o.id))
|
||||
totalOrders.value -= ids.length
|
||||
selectedOrders.value = []
|
||||
|
||||
ElMessage.success('批量删除成功')
|
||||
if (response.data?.success) {
|
||||
orders.value = orders.value.filter(o => !ids.includes(o.id))
|
||||
totalOrders.value -= response.data?.deletedCount || ids.length
|
||||
selectedOrders.value = []
|
||||
ElMessage.success(response.data?.message || '批量删除成功')
|
||||
} else {
|
||||
ElMessage.error(response.data?.message || '批量删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量删除失败:', error)
|
||||
ElMessage.error('批量删除失败')
|
||||
ElMessage.error('批量删除失败: ' + (error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,8 +591,9 @@ const fetchOrders = async () => {
|
||||
const response = await apiFunction({
|
||||
page: currentPage.value - 1,
|
||||
size: pageSize.value,
|
||||
status: filters.status,
|
||||
search: filters.search || searchText.value
|
||||
status: filters.status || undefined,
|
||||
paymentMethod: filters.paymentMethod || undefined,
|
||||
search: filters.search || searchText.value || undefined
|
||||
})
|
||||
|
||||
console.log('获取订单列表响应:', response)
|
||||
@@ -1002,6 +1101,98 @@ const fetchSystemStats = async () => {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 订单详情弹窗样式 */
|
||||
.order-detail-content {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.detail-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-section h4 {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0 0 16px 0;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px 24px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.detail-item .label {
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.detail-item .value {
|
||||
color: #1e293b;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-item .value.amount {
|
||||
color: #f59e0b;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.description-text {
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.loading-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
color: #64748b;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.loading-container .el-icon {
|
||||
font-size: 32px;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
:deep(.order-detail-dialog .el-dialog__header) {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
:deep(.order-detail-dialog .el-dialog__body) {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
:deep(.order-detail-dialog .el-dialog__footer) {
|
||||
padding: 12px 20px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -163,6 +163,96 @@
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 任务详情弹窗 -->
|
||||
<el-dialog
|
||||
v-model="detailDialogVisible"
|
||||
:title="$t('tasks.taskDetail')"
|
||||
width="600px"
|
||||
class="task-detail-dialog"
|
||||
>
|
||||
<div class="task-detail-content" v-if="currentTask">
|
||||
<div class="detail-section">
|
||||
<h4>基本信息</h4>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">任务ID:</span>
|
||||
<span class="detail-value">{{ currentTask.taskId || currentTask.id }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">用户名:</span>
|
||||
<span class="detail-value">{{ currentTask.username || '未知' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">任务类型:</span>
|
||||
<span class="detail-value">{{ currentTask.type || currentTask.taskType || '未知' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">消耗资源:</span>
|
||||
<span class="detail-value">{{ currentTask.resources || '0积分' }}</span>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">状态:</span>
|
||||
<span class="status-tag" :class="getStatusClass(currentTask.status)">
|
||||
{{ getStatusText(currentTask.status) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section">
|
||||
<h4>时间信息</h4>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">创建时间:</span>
|
||||
<span class="detail-value">{{ formatDate(currentTask.createdAt || currentTask.createTime) }}</span>
|
||||
</div>
|
||||
<div class="detail-row" v-if="currentTask.updatedAt">
|
||||
<span class="detail-label">更新时间:</span>
|
||||
<span class="detail-value">{{ formatDate(currentTask.updatedAt) }}</span>
|
||||
</div>
|
||||
<div class="detail-row" v-if="currentTask.completedAt">
|
||||
<span class="detail-label">完成时间:</span>
|
||||
<span class="detail-value">{{ formatDate(currentTask.completedAt) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentTask.progress !== undefined">
|
||||
<h4>进度信息</h4>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">进度:</span>
|
||||
<el-progress :percentage="currentTask.progress || 0" :status="getProgressStatus(currentTask.status)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentTask.resultUrl">
|
||||
<h4>结果</h4>
|
||||
<div class="detail-row">
|
||||
<span class="detail-label">结果链接:</span>
|
||||
<a :href="currentTask.resultUrl" target="_blank" class="result-link">查看结果</a>
|
||||
</div>
|
||||
<div class="result-preview" v-if="isVideoUrl(currentTask.resultUrl)">
|
||||
<video :src="currentTask.resultUrl" controls class="preview-video"></video>
|
||||
</div>
|
||||
<div class="result-preview" v-else-if="isImageUrl(currentTask.resultUrl)">
|
||||
<img :src="currentTask.resultUrl" class="preview-image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-section" v-if="currentTask.errorMessage">
|
||||
<h4>错误信息</h4>
|
||||
<div class="error-message">{{ currentTask.errorMessage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
<el-button
|
||||
v-if="currentTask?.resultUrl"
|
||||
type="primary"
|
||||
@click="openResult(currentTask.resultUrl)"
|
||||
>
|
||||
查看结果
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -196,6 +286,10 @@ const selectedTasks = ref([])
|
||||
const loading = ref(false)
|
||||
const searchText = ref('')
|
||||
|
||||
// 任务详情弹窗相关
|
||||
const detailDialogVisible = ref(false)
|
||||
const currentTask = ref(null)
|
||||
|
||||
// 系统状态数据
|
||||
const onlineUsers = ref('0/500')
|
||||
const systemUptime = ref('加载中...')
|
||||
@@ -376,8 +470,42 @@ const formatDate = (dateString) => {
|
||||
|
||||
// 查看任务详情
|
||||
const handleView = (task) => {
|
||||
ElMessage.info(`查看任务详情: ${task.taskId || task.id}`)
|
||||
// 这里可以跳转到详情页面或打开详情弹窗
|
||||
currentTask.value = task
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 获取进度条状态
|
||||
const getProgressStatus = (status) => {
|
||||
switch (status) {
|
||||
case 'COMPLETED':
|
||||
return 'success'
|
||||
case 'FAILED':
|
||||
case 'TIMEOUT':
|
||||
return 'exception'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否是视频URL
|
||||
const isVideoUrl = (url) => {
|
||||
if (!url) return false
|
||||
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi']
|
||||
return videoExtensions.some(ext => url.toLowerCase().includes(ext))
|
||||
}
|
||||
|
||||
// 判断是否是图片URL
|
||||
const isImageUrl = (url) => {
|
||||
if (!url) return false
|
||||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp']
|
||||
return imageExtensions.some(ext => url.toLowerCase().includes(ext))
|
||||
}
|
||||
|
||||
// 打开结果
|
||||
const openResult = (url) => {
|
||||
if (url) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除单个任务
|
||||
@@ -393,15 +521,37 @@ const handleDelete = async (task) => {
|
||||
}
|
||||
)
|
||||
|
||||
// 从数据中删除
|
||||
const index = taskRecords.value.findIndex(item => item.id === task.id)
|
||||
if (index > -1) {
|
||||
taskRecords.value.splice(index, 1)
|
||||
total.value--
|
||||
// 调用后端API删除
|
||||
const token = sessionStorage.getItem('token')
|
||||
if (!token) {
|
||||
ElMessage.error('请先登录')
|
||||
return
|
||||
}
|
||||
const deleteUrl = `/api/admin/tasks/${task.taskId || task.id}`
|
||||
const response = await fetch(deleteUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (result.success) {
|
||||
// 从前端数据中移除
|
||||
const index = taskRecords.value.findIndex(item => item.id === task.id)
|
||||
if (index > -1) {
|
||||
taskRecords.value.splice(index, 1)
|
||||
total.value--
|
||||
}
|
||||
ElMessage.success('任务删除成功')
|
||||
} else {
|
||||
ElMessage.error(result.message || '删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消删除
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error('删除失败: ' + error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,14 +573,38 @@ const handleBatchDelete = async () => {
|
||||
}
|
||||
)
|
||||
|
||||
// 批量删除
|
||||
const selectedIds = selectedTasks.value.map(item => item.id)
|
||||
taskRecords.value = taskRecords.value.filter(item => !selectedIds.includes(item.id))
|
||||
total.value -= selectedIds.length
|
||||
selectedTasks.value = []
|
||||
ElMessage.success(`成功删除 ${selectedIds.length} 个任务`)
|
||||
// 调用后端API批量删除
|
||||
const token = sessionStorage.getItem('token')
|
||||
if (!token) {
|
||||
ElMessage.error('请先登录')
|
||||
return
|
||||
}
|
||||
const taskIds = selectedTasks.value.map(item => item.taskId || item.id)
|
||||
|
||||
const response = await fetch('/api/admin/tasks/batch', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(taskIds)
|
||||
})
|
||||
|
||||
const result = await response.json()
|
||||
if (result.success) {
|
||||
// 从前端数据中移除
|
||||
const selectedIds = selectedTasks.value.map(item => item.id)
|
||||
taskRecords.value = taskRecords.value.filter(item => !selectedIds.includes(item.id))
|
||||
total.value -= result.deletedCount || selectedIds.length
|
||||
selectedTasks.value = []
|
||||
ElMessage.success(result.message || `成功删除 ${result.deletedCount} 个任务`)
|
||||
} else {
|
||||
ElMessage.error(result.message || '批量删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
// 用户取消删除
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error('批量删除失败: ' + error.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,31 +634,26 @@ const loadTaskRecords = async () => {
|
||||
|
||||
if (response.data && response.data.success) {
|
||||
const realData = response.data.data || []
|
||||
|
||||
// 如果有真实数据,使用真实数据
|
||||
if (realData.length > 0) {
|
||||
taskRecords.value = realData
|
||||
total.value = response.data.totalElements || 0
|
||||
console.log('成功加载任务记录(真实数据):', taskRecords.value.length)
|
||||
} else {
|
||||
// 如果数据库为空,使用模拟数据进行演示
|
||||
console.log('数据库为空,加载模拟数据')
|
||||
loadMockData()
|
||||
}
|
||||
taskRecords.value = realData
|
||||
total.value = response.data.totalElements || realData.length
|
||||
console.log('成功加载任务记录:', taskRecords.value.length)
|
||||
} else {
|
||||
console.warn('API返回失败:', response.data?.message)
|
||||
// 如果API返回失败,使用假数据
|
||||
loadMockData()
|
||||
taskRecords.value = []
|
||||
total.value = 0
|
||||
ElMessage.warning(response.data?.message || '加载数据失败')
|
||||
}
|
||||
} catch (apiError) {
|
||||
console.error('API调用失败,使用假数据:', apiError)
|
||||
// API调用失败(可能是未登录或服务器问题),使用假数据
|
||||
loadMockData()
|
||||
console.error('API调用失败:', apiError)
|
||||
taskRecords.value = []
|
||||
total.value = 0
|
||||
ElMessage.error('API调用失败: ' + (apiError.message || '请检查网络'))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载任务记录失败:', error)
|
||||
ElMessage.error('数据加载失败,请检查网络连接')
|
||||
loadMockData()
|
||||
taskRecords.value = []
|
||||
total.value = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -1019,4 +1188,83 @@ const fetchSystemStats = async () => {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 任务详情弹窗样式 */
|
||||
.task-detail-content {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.detail-section:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-section h4 {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
width: 100px;
|
||||
flex-shrink: 0;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: #1f2937;
|
||||
font-size: 14px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.result-link {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.result-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.result-preview {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.preview-video {
|
||||
max-width: 100%;
|
||||
max-height: 300px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 300px;
|
||||
border-radius: 8px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
color: #dc2626;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1093,7 +1093,7 @@ const restoreProcessingTask = async () => {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态,但不恢复输入参数)
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态和参考图)
|
||||
const checkLastTaskStatus = async () => {
|
||||
if (!userStore.isAuthenticated) return
|
||||
|
||||
@@ -1102,14 +1102,30 @@ const checkLastTaskStatus = async () => {
|
||||
if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
const lastTask = response.data.data[0]
|
||||
|
||||
// 只关注 FAILED 状态,显示失败UI但不恢复输入参数
|
||||
// 只关注 FAILED 状态,显示失败UI和参考图
|
||||
if (lastTask.status === 'FAILED') {
|
||||
console.log('[Last Task Failed]', lastTask)
|
||||
|
||||
currentTask.value = lastTask
|
||||
taskStatus.value = 'FAILED'
|
||||
// 不恢复输入参数,让用户可以自由创建新任务
|
||||
|
||||
// 恢复提示词,让用户看到失败任务的内容
|
||||
if (lastTask.prompt) {
|
||||
inputText.value = lastTask.prompt
|
||||
}
|
||||
// 恢复首帧图片(参考图)
|
||||
if (lastTask.firstFrameUrl) {
|
||||
firstFrameImage.value = processHistoryUrl(lastTask.firstFrameUrl)
|
||||
}
|
||||
// 恢复其他参数
|
||||
if (lastTask.aspectRatio) {
|
||||
aspectRatio.value = lastTask.aspectRatio
|
||||
}
|
||||
if (lastTask.duration) {
|
||||
duration.value = lastTask.duration
|
||||
}
|
||||
}
|
||||
// 如果最近一条任务是成功的,不需要处理
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Check last task status error', error)
|
||||
|
||||
@@ -15,24 +15,30 @@
|
||||
|
||||
<!-- 登录方式切换 -->
|
||||
<div class="login-tabs">
|
||||
<svg width="248" height="59" viewBox="0 0 248 59" fill="none" xmlns="http://www.w3.org/2000/svg" class="tabs-svg">
|
||||
<!-- 邮箱登录 -->
|
||||
<g class="tab-email" :class="{ active: loginType === 'email' }" @click="loginType = 'email'" style="cursor: pointer;">
|
||||
<!-- 透明点击区域 -->
|
||||
<rect x="0" y="10" width="120" height="40" fill="transparent"/>
|
||||
<path d="M13.598 21.112V40.638H11.076V39.13H4.316V40.638H1.768V21.112H6.344V17.55H8.996V21.112H13.598ZM4.316 36.712H6.344V31.122H4.316V36.712ZM8.996 36.712H11.076V31.122H8.996V36.712ZM4.316 28.73H6.344V23.556H4.316V28.73ZM8.996 23.556V28.73H11.076V23.556H8.996ZM15.34 18.772H24.232V20.748C23.452 23.4 22.62 25.818 21.736 28.054C23.556 30.654 24.466 32.76 24.492 34.398C24.492 35.958 24.154 37.024 23.504 37.596C22.802 38.22 21.398 38.532 19.318 38.532L18.512 35.802C19.474 35.906 20.28 35.984 20.956 35.984C21.372 35.958 21.658 35.828 21.814 35.62C21.918 35.464 21.97 35.048 21.996 34.398C21.97 32.786 20.982 30.68 19.032 28.054C19.864 26.156 20.67 23.868 21.45 21.164H17.914V41.73H15.34V18.772ZM27.716 27.326H31.616V24.83H34.346V27.326H37.57V29.926H34.346V30.446C35.542 31.538 36.79 32.734 38.038 34.06L36.53 36.348C35.698 35.048 34.97 33.93 34.346 33.046V41.756H31.616V33.202C30.654 35.23 29.432 37.102 27.924 38.818L26.754 35.776C28.756 34.06 30.238 32.11 31.226 29.926H27.716V27.326ZM49.4 25.48V41.73H46.774V40.82H41.106V41.73H38.48V25.48H49.4ZM41.106 38.428H46.774V36.244H41.106V38.428ZM41.106 33.956H46.774V32.058H41.106V33.956ZM41.106 29.77H46.774V27.872H41.106V29.77ZM31.538 21.762C30.966 22.75 30.316 23.634 29.614 24.466L27.248 22.958C28.782 21.294 29.874 19.5 30.498 17.576L33.124 18.148C32.968 18.564 32.838 18.98 32.682 19.37H38.974V21.762H35.568C36.088 22.49 36.504 23.192 36.842 23.842L34.346 24.778C33.878 23.738 33.306 22.724 32.682 21.762H31.538ZM42.64 21.762C42.12 22.828 41.574 23.816 40.95 24.726L38.636 23.244C39.962 21.424 40.898 19.474 41.444 17.446L44.018 18.018C43.862 18.486 43.732 18.928 43.602 19.37H50.83V21.762H46.67C47.19 22.49 47.632 23.192 47.97 23.842L45.578 24.752C45.11 23.712 44.538 22.724 43.862 21.762H42.64ZM57.538 28.522H72.566V35.282H57.538V28.522ZM69.836 32.89V30.888H60.268V32.89H69.836ZM60.45 35.438C61.282 36.296 62.036 37.31 62.712 38.454H67.626C68.354 37.466 68.978 36.426 69.524 35.36L72.046 36.27C71.578 37.05 71.084 37.778 70.564 38.454H76.232V41.028H53.716V38.454H59.826C59.28 37.726 58.63 37.05 57.902 36.4L60.45 35.438ZM56.368 21.112C57.564 21.996 58.63 22.854 59.514 23.686C60.424 22.802 61.152 21.866 61.724 20.878H55.822V18.382H64.792V20.41C64.194 21.918 63.388 23.27 62.374 24.466H68.822C67.262 22.75 66.014 20.904 65.104 18.876L67.366 17.628C67.782 18.59 68.276 19.5 68.848 20.358C69.758 19.63 70.538 18.85 71.188 18.018L73.086 19.708C72.306 20.644 71.37 21.528 70.304 22.308C70.72 22.828 71.188 23.296 71.708 23.764C72.8 22.932 73.71 21.996 74.49 20.982L76.388 22.646C75.608 23.634 74.672 24.544 73.632 25.35C74.776 26.182 76.024 26.962 77.428 27.664L75.634 29.744C73.606 28.6 71.838 27.352 70.33 25.974V26.936H60.788V26.104C59.124 27.56 57.07 28.782 54.626 29.796L52.962 27.664C54.782 26.962 56.316 26.156 57.616 25.246C56.758 24.466 55.744 23.66 54.548 22.828L56.368 21.112ZM82.368 18.408H97.864V26.806H102.258V29.276H98.228L100.282 30.966C98.93 32.63 97.396 33.956 95.68 34.892C97.604 36.296 99.84 37.518 102.388 38.61L101.01 41.002C97.37 39.286 94.432 37.232 92.196 34.814V38.974C92.196 40.794 91.39 41.73 89.804 41.73H86.762L86.164 39.182C87.1 39.286 88.01 39.364 88.894 39.364C89.284 39.364 89.492 39 89.492 38.324V34.918C86.944 37.154 83.928 39.156 80.47 40.95L79.378 38.428C83.278 36.66 86.632 34.58 89.492 32.136V29.276H79.768V26.806H95.108V25.012H83.252V22.672H95.108V20.852H82.368V18.408ZM83.018 29.666C84.526 30.706 85.8 31.746 86.84 32.786L85.072 34.554C84.162 33.566 82.888 32.526 81.224 31.382L83.018 29.666ZM98.176 29.276H92.196V31.824C92.69 32.37 93.236 32.89 93.834 33.41C95.498 32.422 96.954 31.044 98.176 29.276Z" />
|
||||
</g>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<path d="M124 18.75V40.25" stroke="#9EA9B6"/>
|
||||
|
||||
<!-- 密码登录 -->
|
||||
<g class="tab-password" :class="{ active: loginType === 'password' }" @click="loginType = 'password'" style="cursor: pointer;">
|
||||
<!-- 透明点击区域 -->
|
||||
<rect x="128" y="10" width="120" height="40" fill="transparent"/>
|
||||
<path d="M155.362 18.46V35.126H152.996V20.956H148.212V35.126H145.768V18.46H155.362ZM149.434 22.49H151.722V31.018C151.644 33.93 151.202 36.27 150.37 38.012C149.564 39.65 148.264 40.898 146.47 41.782L145.014 39.494C146.704 38.636 147.848 37.57 148.472 36.322C149.044 34.944 149.356 33.176 149.434 31.018V22.49ZM152.684 35.724C153.984 37.05 155.076 38.35 155.986 39.624L154.036 41.574C153.308 40.274 152.294 38.896 150.942 37.414L152.684 35.724ZM162.044 30.732H160.328V38.298C161.342 37.882 162.33 37.362 163.318 36.738L163.786 39.156C162.122 40.196 160.224 41.028 158.092 41.704L156.974 39.286C157.442 39.052 157.676 38.688 157.676 38.22V30.732H156.064V28.132H157.676V17.68H160.328V28.132H168.622V30.732H164.384C165.528 34.45 167.14 37.336 169.168 39.364L167.322 41.47C165.008 39.026 163.24 35.438 162.044 30.732ZM166.23 18.746L168.31 20.41C166.516 23.322 164.436 25.506 162.044 26.91L160.588 24.83C162.772 23.478 164.67 21.45 166.23 18.746ZM174.966 18.356H191.034V26.026H174.966V18.356ZM188.278 23.608V20.8H177.722V23.608H188.278ZM176.76 30.16H171.118V27.534H194.856V30.16H179.516L178.814 32.422H191.372C191.164 36.894 190.748 39.546 190.072 40.378C189.396 41.184 188.122 41.6 186.198 41.6C184.924 41.6 183.78 41.522 182.766 41.392L181.882 38.922C183.286 39.052 184.508 39.13 185.6 39.13C186.874 39.13 187.654 38.87 187.966 38.402C188.252 37.908 188.46 36.738 188.59 34.866H175.564L176.76 30.16ZM201.538 28.522H216.566V35.282H201.538V28.522ZM213.836 32.89V30.888H204.268V32.89H213.836ZM204.45 35.438C205.282 36.296 206.036 37.31 206.712 38.454H211.626C212.354 37.466 212.978 36.426 213.524 35.36L216.046 36.27C215.578 37.05 215.084 37.778 214.564 38.454H220.232V41.028H197.716V38.454H203.826C203.28 37.726 202.63 37.05 201.902 36.4L204.45 35.438ZM200.368 21.112C201.564 21.996 202.63 22.854 203.514 23.686C204.424 22.802 205.152 21.866 205.724 20.878H199.822V18.382H208.792V20.41C208.194 21.918 207.388 23.27 206.374 24.466H212.822C211.262 22.75 210.014 20.904 209.104 18.876L211.366 17.628C211.782 18.59 212.276 19.5 212.848 20.358C213.758 19.63 214.538 18.85 215.188 18.018L217.086 19.708C216.306 20.644 215.37 21.528 214.304 22.308C214.72 22.828 215.188 23.296 215.708 23.764C216.8 22.932 217.71 21.996 218.49 20.982L220.388 22.646C219.608 23.634 218.672 24.544 217.632 25.35C218.776 26.182 220.024 26.962 221.428 27.664L219.634 29.744C217.606 28.6 215.838 27.352 214.33 25.974V26.936H204.788V26.104C203.124 27.56 201.07 28.782 198.626 29.796L196.962 27.664C198.782 26.962 200.316 26.156 201.616 25.246C200.758 24.466 199.744 23.66 198.548 22.828L200.368 21.112ZM226.368 18.408H241.864V26.806H246.258V29.276H242.228L244.282 30.966C242.93 32.63 241.396 33.956 239.68 34.892C241.604 36.296 243.84 37.518 246.388 38.61L245.01 41.002C241.37 39.286 238.432 37.232 236.196 34.814V38.974C236.196 40.794 235.39 41.73 233.804 41.73H230.762L230.164 39.182C231.1 39.286 232.01 39.364 232.894 39.364C233.284 39.364 233.492 39 233.492 38.324V34.918C230.944 37.154 227.928 39.156 224.47 40.95L223.378 38.428C227.278 36.66 230.632 34.58 233.492 32.136V29.276H223.768V26.806H239.108V25.012H227.252V22.672H239.108V20.852H226.368V18.408ZM227.018 29.666C228.526 30.706 229.8 31.746 230.84 32.786L229.072 34.554C228.162 33.566 226.888 32.526 225.224 31.382L227.018 29.666ZM242.176 29.276H236.196V31.824C236.69 32.37 237.236 32.89 237.834 33.41C239.498 32.422 240.954 31.044 242.176 29.276Z" />
|
||||
</g>
|
||||
</svg>
|
||||
<!-- 邮箱登录盒子 -->
|
||||
<div
|
||||
class="tab-item"
|
||||
:class="{ active: loginType === 'email' }"
|
||||
@click="loginType = 'email'"
|
||||
>
|
||||
<svg width="105" height="30" viewBox="0 0 105 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.598 6.112V25.638H11.076V24.13H4.316V25.638H1.768V6.112H6.344V2.55H8.996V6.112H13.598ZM4.316 21.712H6.344V16.122H4.316V21.712ZM8.996 21.712H11.076V16.122H8.996V21.712ZM4.316 13.73H6.344V8.556H4.316V13.73ZM8.996 8.556V13.73H11.076V8.556H8.996ZM15.34 3.772H24.232V5.748C23.452 8.4 22.62 10.818 21.736 13.054C23.556 15.654 24.466 17.76 24.492 19.398C24.492 20.958 24.154 22.024 23.504 22.596C22.802 23.22 21.398 23.532 19.318 23.532L18.512 20.802C19.474 20.906 20.28 20.984 20.956 20.984C21.372 20.958 21.658 20.828 21.814 20.62C21.918 20.464 21.97 20.048 21.996 19.398C21.97 17.786 20.982 15.68 19.032 13.054C19.864 11.156 20.67 8.868 21.45 6.164H17.914V26.73H15.34V3.772ZM27.716 12.326H31.616V9.83H34.346V12.326H37.57V14.926H34.346V15.446C35.542 16.538 36.79 17.734 38.038 19.06L36.53 21.348C35.698 20.048 34.97 18.93 34.346 18.046V26.756H31.616V18.202C30.654 20.23 29.432 22.102 27.924 23.818L26.754 20.776C28.756 19.06 30.238 17.11 31.226 14.926H27.716V12.326ZM49.4 10.48V26.73H46.774V25.82H41.106V26.73H38.48V10.48H49.4ZM41.106 23.428H46.774V21.244H41.106V23.428ZM41.106 18.956H46.774V17.058H41.106V18.956ZM41.106 14.77H46.774V12.872H41.106V14.77ZM31.538 6.762C30.966 7.75 30.316 8.634 29.614 9.466L27.248 7.958C28.782 6.294 29.874 4.5 30.498 2.576L33.124 3.148C32.968 3.564 32.838 3.98 32.682 4.37H38.974V6.762H35.568C36.088 7.49 36.504 8.192 36.842 8.842L34.346 9.778C33.878 8.738 33.306 7.724 32.682 6.762H31.538ZM42.64 6.762C42.12 7.828 41.574 8.816 40.95 9.726L38.636 8.244C39.962 6.424 40.898 4.474 41.444 2.446L44.018 3.018C43.862 3.486 43.732 3.928 43.602 4.37H50.83V6.762H46.67C47.19 7.49 47.632 8.192 47.97 8.842L45.578 9.752C45.11 8.712 44.538 7.724 43.862 6.762H42.64ZM57.538 13.522H72.566V20.282H57.538V13.522ZM69.836 17.89V15.888H60.268V17.89H69.836ZM60.45 20.438C61.282 21.296 62.036 22.31 62.712 23.454H67.626C68.354 22.466 68.978 21.426 69.524 20.36L72.046 21.27C71.578 22.05 71.084 22.778 70.564 23.454H76.232V26.028H53.716V23.454H59.826C59.28 22.726 58.63 22.05 57.902 21.4L60.45 20.438ZM56.368 6.112C57.564 6.996 58.63 7.854 59.514 8.686C60.424 7.802 61.152 6.866 61.724 5.878H55.822V3.382H64.792V5.41C64.194 6.918 63.388 8.27 62.374 9.466H68.822C67.262 7.75 66.014 5.904 65.104 3.876L67.366 2.628C67.782 3.59 68.276 4.5 68.848 5.358C69.758 4.63 70.538 3.85 71.188 3.018L73.086 4.708C72.306 5.644 71.37 6.528 70.304 7.308C70.72 7.828 71.188 8.296 71.708 8.764C72.8 7.932 73.71 6.996 74.49 5.982L76.388 7.646C75.608 8.634 74.672 9.544 73.632 10.35C74.776 11.182 76.024 11.962 77.428 12.664L75.634 14.744C73.606 13.6 71.838 12.352 70.33 10.974V11.936H60.788V11.104C59.124 12.56 57.07 13.782 54.626 14.796L52.962 12.664C54.782 11.962 56.316 11.156 57.616 10.246C56.758 9.466 55.744 8.66 54.548 7.828L56.368 6.112ZM82.368 3.408H97.864V11.806H102.258V14.276H98.228L100.282 15.966C98.93 17.63 97.396 18.956 95.68 19.892C97.604 21.296 99.84 22.518 102.388 23.61L101.01 26.002C97.37 24.286 94.432 22.232 92.196 19.814V23.974C92.196 25.794 91.39 26.73 89.804 26.73H86.762L86.164 24.182C87.1 24.286 88.01 24.364 88.894 24.364C89.284 24.364 89.492 24 89.492 23.324V19.918C86.944 22.154 83.928 24.156 80.47 25.95L79.378 23.428C83.278 21.66 86.632 19.58 89.492 17.136V14.276H79.768V11.806H95.108V10.012H83.252V7.672H95.108V5.852H82.368V3.408ZM83.018 14.666C84.526 15.706 85.8 16.746 86.84 17.786L85.072 19.554C84.162 18.566 82.888 17.526 81.224 16.382L83.018 14.666ZM98.176 14.276H92.196V16.824C92.69 17.37 93.236 17.89 93.834 18.41C95.498 17.422 96.954 16.044 98.176 14.276Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<div class="tab-divider"></div>
|
||||
|
||||
<!-- 账号登录盒子 -->
|
||||
<div
|
||||
class="tab-item"
|
||||
:class="{ active: loginType === 'password' }"
|
||||
@click="loginType = 'password'"
|
||||
>
|
||||
<svg width="105" height="30" viewBox="0 0 105 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.362 3.46V20.126H7.996V5.956H3.212V20.126H0.768V3.46H10.362ZM4.434 7.49H6.722V16.018C6.644 18.93 6.202 21.27 5.37 23.012C4.564 24.65 3.264 25.898 1.47 26.782L0.014 24.494C1.704 23.636 2.848 22.57 3.472 21.322C4.044 19.944 4.356 18.176 4.434 16.018V7.49ZM7.684 20.724C8.984 22.05 10.076 23.35 10.986 24.624L9.036 26.574C8.308 25.274 7.294 23.896 5.942 22.414L7.684 20.724ZM17.044 15.732H15.328V23.298C16.342 22.882 17.33 22.362 18.318 21.738L18.786 24.156C17.122 25.196 15.224 26.028 13.092 26.704L11.974 24.286C12.442 24.052 12.676 23.688 12.676 23.22V15.732H11.064V13.132H12.676V2.68H15.328V13.132H23.622V15.732H19.384C20.528 19.45 22.14 22.336 24.168 24.364L22.322 26.47C20.008 24.026 18.24 20.438 17.044 15.732ZM21.23 3.746L23.31 5.41C21.516 8.322 19.436 10.506 17.044 11.91L15.588 9.83C17.772 8.478 19.67 6.45 21.23 3.746ZM29.966 3.356H46.034V11.026H29.966V3.356ZM43.278 8.608V5.8H32.722V8.608H43.278ZM31.76 15.16H26.118V12.534H49.856V15.16H34.516L33.814 17.422H46.372C46.164 21.894 45.748 24.546 45.072 25.378C44.396 26.184 43.122 26.6 41.198 26.6C39.924 26.6 38.78 26.522 37.766 26.392L36.882 23.922C38.286 24.052 39.508 24.13 40.6 24.13C41.874 24.13 42.654 23.87 42.966 23.402C43.252 22.908 43.46 21.738 43.59 19.866H30.564L31.76 15.16ZM56.538 13.522H71.566V20.282H56.538V13.522ZM68.836 17.89V15.888H59.268V17.89H68.836ZM59.45 20.438C60.282 21.296 61.036 22.31 61.712 23.454H66.626C67.354 22.466 67.978 21.426 68.524 20.36L71.046 21.27C70.578 22.05 70.084 22.778 69.564 23.454H75.232V26.028H52.716V23.454H58.826C58.28 22.726 57.63 22.05 56.902 21.4L59.45 20.438ZM55.368 6.112C56.564 6.996 57.63 7.854 58.514 8.686C59.424 7.802 60.152 6.866 60.724 5.878H54.822V3.382H63.792V5.41C63.194 6.918 62.388 8.27 61.374 9.466H67.822C66.262 7.75 65.014 5.904 64.104 3.876L66.366 2.628C66.782 3.59 67.276 4.5 67.848 5.358C68.758 4.63 69.538 3.85 70.188 3.018L72.086 4.708C71.306 5.644 70.37 6.528 69.304 7.308C69.72 7.828 70.188 8.296 70.708 8.764C71.8 7.932 72.71 6.996 73.49 5.982L75.388 7.646C74.608 8.634 73.672 9.544 72.632 10.35C73.776 11.182 75.024 11.962 76.428 12.664L74.634 14.744C72.606 13.6 70.838 12.352 69.33 10.974V11.936H59.788V11.104C58.124 12.56 56.07 13.782 53.626 14.796L51.962 12.664C53.782 11.962 55.316 11.156 56.616 10.246C55.758 9.466 54.744 8.66 53.548 7.828L55.368 6.112ZM81.368 3.408H96.864V11.806H101.258V14.276H97.228L99.282 15.966C97.93 17.63 96.396 18.956 94.68 19.892C96.604 21.296 98.84 22.518 101.388 23.61L100.01 26.002C96.37 24.286 93.432 22.232 91.196 19.814V23.974C91.196 25.794 90.39 26.73 88.804 26.73H85.762L85.164 24.182C86.1 24.286 87.01 24.364 87.894 24.364C88.284 24.364 88.492 24 88.492 23.324V19.918C85.944 22.154 82.928 24.156 79.47 25.95L78.378 23.428C82.278 21.66 85.632 19.58 88.492 17.136V14.276H78.768V11.806H94.108V10.012H82.252V7.672H94.108V5.852H81.368V3.408ZM82.018 14.666C83.526 15.706 84.8 16.746 85.84 17.786L84.072 19.554C83.162 18.566 81.888 17.526 80.224 16.382L82.018 14.666ZM97.176 14.276H91.196V16.824C91.69 17.37 92.236 17.89 92.834 18.41C94.498 17.422 95.954 16.044 97.176 14.276Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
@@ -414,27 +420,36 @@ const handleLogin = async () => {
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-bottom: 50px;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.tabs-svg {
|
||||
width: 248px;
|
||||
height: 59px;
|
||||
.tab-item {
|
||||
padding: 10px 16px;
|
||||
cursor: pointer;
|
||||
transition: color 0.3s ease;
|
||||
user-select: none;
|
||||
color: #9EA9B6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.tab-email path,
|
||||
.tab-password path {
|
||||
fill: #9EA9B6;
|
||||
transition: fill 0.3s ease;
|
||||
.tab-item:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.tab-email.active path,
|
||||
.tab-password.active path {
|
||||
fill: white;
|
||||
.tab-item.active {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tab-email:hover path,
|
||||
.tab-password:hover path {
|
||||
fill: rgba(255, 255, 255, 0.8);
|
||||
.tab-divider {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: #9EA9B6;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
/* 登录表单 */
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
size="small"
|
||||
@@ -116,6 +116,13 @@
|
||||
>
|
||||
测试完成
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
@click="handleDeletePayment(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -235,7 +242,7 @@ import {
|
||||
Close,
|
||||
User as Warning
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getPayments, testPaymentComplete as testPaymentCompleteApi, createTestPayment } from '@/api/payments'
|
||||
import { getPayments, testPaymentComplete as testPaymentCompleteApi, createTestPayment, deletePayment } from '@/api/payments'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
@@ -561,6 +568,37 @@ const testPaymentComplete = async (payment) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 删除支付记录
|
||||
const handleDeletePayment = async (payment) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除支付记录 ${payment.orderId} 吗?`,
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
const response = await deletePayment(payment.id)
|
||||
|
||||
if (response.data?.success) {
|
||||
ElMessage.success('删除成功')
|
||||
// 刷新支付记录列表
|
||||
fetchPayments()
|
||||
} else {
|
||||
ElMessage.error(response.data?.message || '删除失败')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('Delete payment error:', error)
|
||||
ElMessage.error('删除失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchPayments()
|
||||
})
|
||||
|
||||
@@ -1808,7 +1808,7 @@ const restoreProcessingTask = async () => {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态,但不恢复输入参数)
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态和参考图)
|
||||
const checkLastTaskStatus = async () => {
|
||||
if (!userStore.isAuthenticated) return
|
||||
|
||||
@@ -1817,14 +1817,30 @@ const checkLastTaskStatus = async () => {
|
||||
if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
const lastTask = response.data.data[0]
|
||||
|
||||
// 只关注 FAILED 状态,显示失败UI但不恢复输入参数
|
||||
// 只关注 FAILED 状态,显示失败UI和参考图
|
||||
if (lastTask.status === 'FAILED') {
|
||||
console.log('[Last Task Failed]', lastTask)
|
||||
|
||||
currentTask.value = lastTask
|
||||
taskStatus.value = 'FAILED'
|
||||
// 不恢复输入参数,让用户可以自由创建新任务
|
||||
|
||||
// 恢复提示词,让用户看到失败任务的内容
|
||||
if (lastTask.prompt) {
|
||||
inputText.value = lastTask.prompt
|
||||
}
|
||||
// 恢复参考图
|
||||
if (lastTask.imageUrl) {
|
||||
generatedImageUrl.value = processHistoryUrl(lastTask.imageUrl)
|
||||
}
|
||||
// 恢复其他参数
|
||||
if (lastTask.aspectRatio) {
|
||||
aspectRatio.value = lastTask.aspectRatio
|
||||
}
|
||||
if (lastTask.hdMode !== undefined) {
|
||||
hdMode.value = lastTask.hdMode
|
||||
}
|
||||
}
|
||||
// 如果最近一条任务是成功的,不需要处理
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Check last task status error', error)
|
||||
|
||||
@@ -78,7 +78,9 @@
|
||||
<el-icon><User /></el-icon>
|
||||
<span>{{ $t('systemSettings.membership') }}</span>
|
||||
</div>
|
||||
<!-- 任务清理管理标签暂时隐藏 -->
|
||||
<div
|
||||
v-if="false"
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'cleanup' }"
|
||||
@click="activeTab = 'cleanup'"
|
||||
@@ -256,8 +258,34 @@
|
||||
<el-input v-model="promptOptimizationModel" style="width: 400px;" placeholder="gpt-5.1-thinking"></el-input>
|
||||
<div class="model-tip">{{ $t('systemSettings.promptOptimizationModelTip') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('systemSettings.storyboardSystemPrompt')">
|
||||
<el-input
|
||||
v-model="storyboardSystemPrompt"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
style="width: 500px;"
|
||||
:placeholder="$t('systemSettings.storyboardSystemPromptPlaceholder')">
|
||||
</el-input>
|
||||
<div class="model-tip">{{ $t('systemSettings.storyboardSystemPromptTip') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('systemSettings.promptOptimizationSystemPrompt')">
|
||||
<el-input
|
||||
v-model="promptOptimizationSystemPrompt"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
style="width: 500px;"
|
||||
:placeholder="$t('systemSettings.promptOptimizationSystemPromptPlaceholder')">
|
||||
</el-input>
|
||||
<div class="model-tip">{{ $t('systemSettings.promptOptimizationSystemPromptTip') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveAiModelSettings" :loading="savingAiModel">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="saveAiModelSettings"
|
||||
:loading="savingAiModel"
|
||||
class="ai-save-btn"
|
||||
>
|
||||
<el-icon v-if="!savingAiModel"><Check /></el-icon>
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
@@ -431,7 +459,8 @@ import {
|
||||
User as Search,
|
||||
User as ArrowDown,
|
||||
Delete,
|
||||
Refresh
|
||||
Refresh,
|
||||
Check
|
||||
} from '@element-plus/icons-vue'
|
||||
import cleanupApi from '@/api/cleanup'
|
||||
import { getMembershipLevels, updateMembershipLevel } from '@/api/members'
|
||||
@@ -498,6 +527,8 @@ const cleanupConfig = reactive({
|
||||
// AI模型设置相关
|
||||
const promptOptimizationModel = ref('gpt-5.1-thinking')
|
||||
const promptOptimizationApiUrl = ref('https://ai.comfly.chat')
|
||||
const storyboardSystemPrompt = ref('')
|
||||
const promptOptimizationSystemPrompt = ref('')
|
||||
const savingAiModel = ref(false)
|
||||
|
||||
const goToDashboard = () => {
|
||||
@@ -773,6 +804,12 @@ const loadAiModelSettings = async () => {
|
||||
if (data.promptOptimizationApiUrl) {
|
||||
promptOptimizationApiUrl.value = data.promptOptimizationApiUrl
|
||||
}
|
||||
if (data.storyboardSystemPrompt !== undefined) {
|
||||
storyboardSystemPrompt.value = data.storyboardSystemPrompt
|
||||
}
|
||||
if (data.promptOptimizationSystemPrompt !== undefined) {
|
||||
promptOptimizationSystemPrompt.value = data.promptOptimizationSystemPrompt
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载AI模型设置失败:', error)
|
||||
@@ -790,7 +827,9 @@ const saveAiModelSettings = async () => {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
promptOptimizationModel: promptOptimizationModel.value,
|
||||
promptOptimizationApiUrl: promptOptimizationApiUrl.value
|
||||
promptOptimizationApiUrl: promptOptimizationApiUrl.value,
|
||||
storyboardSystemPrompt: storyboardSystemPrompt.value,
|
||||
promptOptimizationSystemPrompt: promptOptimizationSystemPrompt.value
|
||||
})
|
||||
})
|
||||
if (response.ok) {
|
||||
@@ -1419,6 +1458,38 @@ const fetchSystemStats = async () => {
|
||||
border-color: #40a9ff;
|
||||
}
|
||||
|
||||
/* AI模型设置保存按钮样式 */
|
||||
.ai-save-btn {
|
||||
width: auto !important;
|
||||
min-width: 140px;
|
||||
padding: 12px 32px !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: 500;
|
||||
border-radius: 8px !important;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
border: none !important;
|
||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
||||
transition: all 0.3s ease !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ai-save-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5);
|
||||
background: linear-gradient(135deg, #5a6fd6 0%, #6a4190 100%) !important;
|
||||
}
|
||||
|
||||
.ai-save-btn:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.ai-save-btn .el-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* 响应式调整 */
|
||||
@media (max-width: 480px) {
|
||||
.membership-modal {
|
||||
|
||||
@@ -930,7 +930,7 @@ const restoreProcessingTask = async () => {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态,但不恢复输入参数)
|
||||
// 检查最近一条任务的状态(如果失败则显示失败状态和提示词)
|
||||
const checkLastTaskStatus = async () => {
|
||||
if (!userStore.isAuthenticated) return
|
||||
|
||||
@@ -939,14 +939,29 @@ const checkLastTaskStatus = async () => {
|
||||
if (response.data && response.data.success && response.data.data && response.data.data.length > 0) {
|
||||
const lastTask = response.data.data[0]
|
||||
|
||||
// 只关注 FAILED 状态,显示失败UI但不恢复输入参数
|
||||
// 只关注 FAILED 状态,显示失败UI和提示词
|
||||
if (lastTask.status === 'FAILED') {
|
||||
console.log('[Last Task Failed]', lastTask)
|
||||
|
||||
currentTask.value = lastTask
|
||||
taskStatus.value = 'FAILED'
|
||||
// 不恢复输入参数,让用户可以自由创建新任务
|
||||
|
||||
// 恢复提示词,让用户看到失败任务的内容
|
||||
if (lastTask.prompt) {
|
||||
inputText.value = lastTask.prompt
|
||||
}
|
||||
// 恢复其他参数
|
||||
if (lastTask.aspectRatio) {
|
||||
aspectRatio.value = lastTask.aspectRatio
|
||||
}
|
||||
if (lastTask.duration) {
|
||||
duration.value = lastTask.duration
|
||||
}
|
||||
if (lastTask.hdMode !== undefined) {
|
||||
hdMode.value = lastTask.hdMode
|
||||
}
|
||||
}
|
||||
// 如果最近一条任务是成功的,不需要处理
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Check last task status error', error)
|
||||
|
||||
Reference in New Issue
Block a user