1063 lines
29 KiB
Vue
1063 lines
29 KiB
Vue
<template>
|
||
<view class="history-container" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<!-- 顶部导航 -->
|
||
<view class="nav-bar">
|
||
<view class="nav-left" @click="goBack">
|
||
<text class="iconfont icon-fanhui"></text>
|
||
</view>
|
||
<view class="nav-title">我的创作</view>
|
||
<view class="nav-right" @click="showManageMenu">
|
||
<text class="iconfont icon-gengduo"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- AI生成内容标识 -->
|
||
<!-- <view class="ai-notice">
|
||
<text class="ai-notice-icon">🤖</text>
|
||
<text class="ai-notice-text">作品由AI生成,结果仅供参考</text>
|
||
</view> -->
|
||
|
||
<!-- Tab切换 -->
|
||
<view class="tab-bar">
|
||
<view
|
||
v-for="(tab, index) in tabs"
|
||
:key="index"
|
||
:class="['tab-item', selectedTab === tab.value ? 'active' : '']"
|
||
@click="selectedTab = tab.value"
|
||
>
|
||
<text class="tab-text">{{ tab.label }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 内容区 -->
|
||
<scroll-view scroll-y class="content-scroll" :style="{ height: scrollViewHeight }">
|
||
<!-- 空状态 -->
|
||
<view class="empty-state" v-if="historyList.length === 0">
|
||
<image src="/static/images/empty-history.png" class="empty-image" mode="aspectFit"></image>
|
||
<text class="empty-text">暂无历史记录</text>
|
||
<button class="empty-btn" @click="goToGenerate">开始创作</button>
|
||
</view>
|
||
|
||
<!-- 历史列表 -->
|
||
<view class="history-list" v-else>
|
||
<view
|
||
v-for="(item, index) in filteredList"
|
||
:key="item.id"
|
||
class="history-item"
|
||
@click="viewHistory(item)"
|
||
>
|
||
<!-- 左侧图片预览 -->
|
||
<view class="item-images">
|
||
<image
|
||
v-if="item.imageInput"
|
||
:src="item.imageInput"
|
||
mode="aspectFill"
|
||
class="preview-image"
|
||
></image>
|
||
<image
|
||
v-else-if="item.images && item.images.length > 0"
|
||
:src="item.images[0]"
|
||
mode="aspectFill"
|
||
class="preview-image"
|
||
></image>
|
||
<view class="image-count" v-if="item.images && item.images.length > 1">
|
||
<text class="count-text">+{{ item.images.length - 1 }}</text>
|
||
</view>
|
||
|
||
<!-- 状态标识 -->
|
||
<view class="status-badge" :class="{
|
||
'status-processing': item.statusTask === 0,
|
||
'status-completed': item.statusTask === 1,
|
||
'status-failed': item.statusTask === 2
|
||
}">
|
||
<text class="status-text">{{ getStatusTextByTask(item.statusTask) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 右侧信息 -->
|
||
<view class="item-info">
|
||
<view class="info-header">
|
||
<text class="item-prompt">{{ item.prompt }}</text>
|
||
<view class="item-actions" @click.stop="showItemMenu(item)">
|
||
<text class="iconfont icon-gengduo"></text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="info-meta">
|
||
<view class="meta-tags">
|
||
<!-- <text class="meta-tag">{{ item.tags }}</text> -->
|
||
<!-- 类型标识 -->
|
||
|
||
<text class="meta-tag">{{ getTypeText(item.type) }}</text>
|
||
|
||
<text class="meta-tag" v-if="item.quality">{{ item.quality }}</text>
|
||
</view>
|
||
<text class="meta-time">{{ formatTime(item.createdAt) }}</text>
|
||
</view>
|
||
|
||
<!-- 进度条(生成中) -->
|
||
<view class="progress-bar" v-if="item.statusTask === 0">
|
||
<view class="progress-fill" :style="{ width: item.progress + '%' }"></view>
|
||
</view>
|
||
|
||
<!-- 备注(失败状态) -->
|
||
<view class="item-remark" v-if="item.statusTask === 2 && item.remark">
|
||
<text class="remark-text">{{ item.remark }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<view class="load-more" v-if="hasMore && historyList.length > 0">
|
||
<text class="load-text" @click="loadMore">加载更多</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 操作菜单 -->
|
||
<view class="action-sheet" v-if="showActionSheet" @click="showActionSheet = false">
|
||
<view class="action-content" @click.stop>
|
||
<view class="action-title">操作</view>
|
||
<view
|
||
v-for="(action, index) in currentActions"
|
||
:key="index"
|
||
class="action-item"
|
||
@click="handleAction(action.value)"
|
||
>
|
||
<text class="action-icon iconfont" :class="action.icon"></text>
|
||
<text class="action-text">{{ action.label }}</text>
|
||
</view>
|
||
<view class="action-cancel" @click="showActionSheet = false">
|
||
<text>取消</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import api from '@/api/models-api.js'
|
||
import { mapGetters } from 'vuex';
|
||
|
||
export default {
|
||
computed: {
|
||
...mapGetters(['userInfo','uid']),
|
||
filteredList() {
|
||
if (this.selectedTab === 'all') {
|
||
return this.historyList;
|
||
} else if (this.selectedTab === 'completed') {
|
||
// 已完成:statusTask === 1
|
||
return this.historyList.filter(item => item.statusTask === 1);
|
||
} else if (this.selectedTab === 'draft') {
|
||
// 草稿/未完成:statusTask === 0
|
||
return this.historyList.filter(item => item.statusTask === 0);
|
||
}
|
||
return this.historyList;
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
statusBarHeight: 0,
|
||
scrollViewHeight: '100vh',
|
||
|
||
// Tab
|
||
selectedTab: 'all',
|
||
tabs: [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '已完成', value: 'completed' },
|
||
{ label: '草稿', value: 'draft' }
|
||
],
|
||
|
||
// 历史记录
|
||
historyList: [],
|
||
|
||
// 分页
|
||
currentPage: 1,
|
||
pageSize: 10,
|
||
hasMore: true,
|
||
isLoading: false,
|
||
|
||
// 操作菜单
|
||
showActionSheet: false,
|
||
currentItem: null,
|
||
currentActions: []
|
||
};
|
||
},
|
||
|
||
onLoad() {
|
||
this.initPage();
|
||
this.loadHistoryList();
|
||
},
|
||
|
||
methods: {
|
||
initPage() {
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
this.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||
|
||
// 计算滚动区域高度
|
||
const navHeight = 44 + 44; // 导航+Tab
|
||
this.scrollViewHeight = `calc(100vh - ${this.statusBarHeight + navHeight}px)`;
|
||
},
|
||
|
||
// 返回
|
||
goBack() {
|
||
uni.navigateBack();
|
||
},
|
||
|
||
// 查看历史记录
|
||
viewHistory(item) {
|
||
// 根据 statusTask 判断状态
|
||
if (item.statusTask === 0) {
|
||
// 未完成:继续编辑或提示生成中
|
||
uni.showToast({
|
||
title: '生成中,请稍候...',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
} else if (item.statusTask === 2) {
|
||
// 失败
|
||
uni.showToast({
|
||
title: '生成失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
} else if (item.statusTask === 1) {
|
||
// 已完成:根据 type 字段判断跳转页面
|
||
// type == 1 表示图片,跳转到 image.vue
|
||
// 其他情况(包括 undefined、null、0、2等)跳转到 video.vue
|
||
console.log("====viewHistory=====", item);
|
||
if (item.type === 1) {
|
||
uni.navigateTo({
|
||
url: `/pages/ai-generate/image?id=${item.id}`
|
||
});
|
||
} else {
|
||
uni.navigateTo({
|
||
url: `/pages/ai-generate/video?id=${item.id}`
|
||
});
|
||
}
|
||
}
|
||
},
|
||
|
||
// 显示管理菜单
|
||
showManageMenu() {
|
||
this.currentActions = [
|
||
{ label: '清空已完成', value: 'clear_completed', icon: 'icon-shanchu' },
|
||
{ label: '清空全部', value: 'clear_all', icon: 'icon-qingkong' }
|
||
];
|
||
this.showActionSheet = true;
|
||
},
|
||
|
||
// 显示单项菜单
|
||
showItemMenu(item) {
|
||
this.currentItem = item;
|
||
|
||
// 根据 statusTask 判断
|
||
if (item.statusTask === 1) {
|
||
// 已完成
|
||
this.currentActions = [
|
||
{ label: '查看详情', value: 'view', icon: 'icon-chakan' },
|
||
{ label: '再次生成', value: 'regenerate', icon: 'icon-shuaxin' },
|
||
{ label: '下载保存', value: 'download', icon: 'icon-xiazai' },
|
||
{ label: '分享', value: 'share', icon: 'icon-fenxiang' },
|
||
{ label: '删除', value: 'delete', icon: 'icon-shanchu' }
|
||
];
|
||
} else if (item.statusTask === 0) {
|
||
// 未完成
|
||
this.currentActions = [
|
||
{ label: '查看进度', value: 'view', icon: 'icon-chakan' },
|
||
{ label: '取消生成', value: 'cancel', icon: 'icon-guanbi' },
|
||
{ label: '删除', value: 'delete', icon: 'icon-shanchu' }
|
||
];
|
||
} else if (item.statusTask === 2) {
|
||
// 失败
|
||
this.currentActions = [
|
||
{ label: '重新生成', value: 'regenerate', icon: 'icon-shuaxin' },
|
||
{ label: '删除', value: 'delete', icon: 'icon-shanchu' }
|
||
];
|
||
} else {
|
||
// 其他状态
|
||
this.currentActions = [
|
||
{ label: '取消生成', value: 'cancel', icon: 'icon-guanbi' }
|
||
];
|
||
}
|
||
|
||
this.showActionSheet = true;
|
||
},
|
||
|
||
// 处理操作
|
||
handleAction(action) {
|
||
this.showActionSheet = false;
|
||
|
||
switch (action) {
|
||
case 'view':
|
||
this.viewHistory(this.currentItem);
|
||
break;
|
||
case 'regenerate':
|
||
// TODO: 再次生成
|
||
uni.showToast({ title: '开始生成', icon: 'none' });
|
||
break;
|
||
case 'download':
|
||
// TODO: 下载
|
||
uni.showToast({ title: '已保存', icon: 'success' });
|
||
break;
|
||
case 'share':
|
||
// TODO: 分享
|
||
uni.showToast({ title: '分享功能开发中', icon: 'none' });
|
||
break;
|
||
case 'delete':
|
||
this.deleteItem(this.currentItem);
|
||
break;
|
||
case 'edit':
|
||
uni.navigateTo({
|
||
url: `/pages/ai-generate/index?draftId=${this.currentItem.id}`
|
||
});
|
||
break;
|
||
case 'cancel':
|
||
// TODO: 取消生成
|
||
uni.showToast({ title: '已取消', icon: 'none' });
|
||
break;
|
||
case 'clear_completed':
|
||
this.clearHistory('completed');
|
||
break;
|
||
case 'clear_all':
|
||
this.clearHistory('all');
|
||
break;
|
||
}
|
||
},
|
||
|
||
// 删除单项
|
||
deleteItem(item) {
|
||
uni.showModal({
|
||
title: '确认删除',
|
||
content: '删除后无法恢复',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
// TODO: 调用删除API
|
||
// await api.deleteArticle(item.id);
|
||
|
||
const index = this.historyList.findIndex(i => i.id === item.id);
|
||
if (index > -1) {
|
||
this.historyList.splice(index, 1);
|
||
uni.showToast({ title: '已删除', icon: 'success' });
|
||
}
|
||
} catch (error) {
|
||
console.error('删除失败:', error);
|
||
uni.showToast({ title: '删除失败,请重试', icon: 'none' });
|
||
}
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 清空历史
|
||
clearHistory(type) {
|
||
uni.showModal({
|
||
title: '确认清空',
|
||
content: type === 'all' ? '清空所有历史记录?' : '清空已完成的记录?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
// TODO: 调用批量删除API
|
||
// if (type === 'all') {
|
||
// await api.deleteAllArticles();
|
||
// } else {
|
||
// await api.deleteCompletedArticles();
|
||
// }
|
||
|
||
if (type === 'all') {
|
||
this.historyList = [];
|
||
} else {
|
||
// 清空已完成:statusTask === 1
|
||
this.historyList = this.historyList.filter(item =>
|
||
item.statusTask !== 1
|
||
);
|
||
}
|
||
uni.showToast({ title: '已清空', icon: 'success' });
|
||
|
||
// 重新加载数据
|
||
this.loadHistoryList(true);
|
||
} catch (error) {
|
||
console.error('清空失败:', error);
|
||
uni.showToast({ title: '清空失败,请重试', icon: 'none' });
|
||
}
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 加载历史记录列表
|
||
async loadHistoryList(refresh = false) {
|
||
if (this.isLoading) return;
|
||
|
||
if (refresh) {
|
||
this.currentPage = 1;
|
||
this.hasMore = true;
|
||
}
|
||
|
||
if (!this.hasMore) return;
|
||
|
||
// 检查是否有用户ID
|
||
if (!this.uid) {
|
||
console.warn('用户ID不存在,无法加载作品列表');
|
||
uni.showToast({
|
||
title: '请先登录',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
this.isLoading = false;
|
||
return;
|
||
}
|
||
|
||
this.isLoading = true;
|
||
|
||
try {
|
||
// 使用 searchArticles 接口,支持 uid 和 statusTask 参数
|
||
const res = await api.searchArticles({
|
||
uid: this.uid,
|
||
page: this.currentPage,
|
||
size: this.pageSize
|
||
});
|
||
|
||
if (res && res.code === 200 && res.data) {
|
||
// 兼容不同的数据结构
|
||
const list = res.data.list || res.data.records || res.data || [];
|
||
|
||
// 转换数据格式
|
||
const formattedList = list.map(item => {
|
||
// 解析图片列表(可能是字符串或数组)
|
||
let images = [];
|
||
if (item.imageOutput) {
|
||
if (typeof item.imageOutput === 'string') {
|
||
try {
|
||
images = JSON.parse(item.imageOutput);
|
||
} catch (e) {
|
||
images = [item.imageOutput];
|
||
}
|
||
} else if (Array.isArray(item.imageOutput)) {
|
||
images = item.imageOutput;
|
||
}
|
||
}
|
||
|
||
// 获取 statusTask,默认为 0(未完成)
|
||
const statusTask = item.statusTask !== undefined && item.statusTask !== null
|
||
? item.statusTask
|
||
: (images && images.length > 0 ? 1 : 0);
|
||
|
||
// 根据 statusTask 判断状态(用于显示)
|
||
let status = 'draft';
|
||
if (statusTask === 1) {
|
||
status = 'completed';
|
||
} else if (statusTask === 2) {
|
||
status = 'failed';
|
||
} else if (statusTask === 0) {
|
||
status = 'draft';
|
||
}
|
||
|
||
return {
|
||
id: item.id,
|
||
prompt: item.title || item.prompt || '未命名作品',
|
||
images: images,
|
||
imageInput: item.imageInput || '', // 保存 imageInput 用于左侧图片展示
|
||
model: item.model || 'design-v1',
|
||
uid: item.uid,
|
||
taskId: item.taskId,
|
||
tags: item.tags || item.model || '',
|
||
aspectRatio: item.image_size || item.aspect_ratio || '9:16',
|
||
quality: item.quality || '2K',
|
||
status: status,
|
||
statusTask: statusTask, // 保存原始 statusTask 用于过滤
|
||
remark: item.remark || '',
|
||
type: item.type, // 保存文章类型,用于判断跳转页面
|
||
progress: statusTask === 1 ? 100 : 0,
|
||
createdAt: item.created_at || item.createTime ? new Date(item.created_at || item.createTime).getTime() : Date.now(),
|
||
articleData: item // 保存原始数据
|
||
};
|
||
});
|
||
|
||
if (refresh) {
|
||
this.historyList = formattedList;
|
||
} else {
|
||
this.historyList = [...this.historyList, ...formattedList];
|
||
}
|
||
|
||
// 判断是否还有更多
|
||
if (list.length < this.pageSize) {
|
||
this.hasMore = false;
|
||
} else {
|
||
this.currentPage++;
|
||
}
|
||
} else {
|
||
throw new Error(res?.message || '获取作品列表失败');
|
||
}
|
||
} catch (error) {
|
||
console.error('加载历史记录失败:', error);
|
||
uni.showToast({
|
||
title: error.message || '加载失败,请重试',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
} finally {
|
||
this.isLoading = false;
|
||
}
|
||
},
|
||
|
||
// 加载更多
|
||
loadMore() {
|
||
if (this.hasMore && !this.isLoading) {
|
||
this.loadHistoryList(false);
|
||
}
|
||
},
|
||
|
||
// 去生成页面
|
||
goToGenerate() {
|
||
uni.navigateTo({
|
||
url: '/pages/ai-generate/index'
|
||
});
|
||
},
|
||
|
||
// 获取状态文本(兼容旧方法)
|
||
getStatusText(status) {
|
||
const map = {
|
||
processing: '生成中',
|
||
failed: '失败',
|
||
draft: '草稿'
|
||
};
|
||
return map[status] || '';
|
||
},
|
||
|
||
// 根据 statusTask 获取状态文本
|
||
getStatusTextByTask(statusTask) {
|
||
switch (statusTask) {
|
||
case 0:
|
||
return '进行中';
|
||
case 1:
|
||
return '已完成';
|
||
case 2:
|
||
return '失败';
|
||
default:
|
||
return '未知';
|
||
}
|
||
},
|
||
|
||
// 根据 type 获取类型文本
|
||
getTypeText(type) {
|
||
if (type === 1) {
|
||
return '图片';
|
||
} else if (type === 2) {
|
||
return '视频';
|
||
}
|
||
return '';
|
||
},
|
||
|
||
// 格式化时间
|
||
formatTime(timestamp) {
|
||
const now = Date.now();
|
||
const diff = now - timestamp;
|
||
|
||
if (diff < 60000) {
|
||
return '刚刚';
|
||
} else if (diff < 3600000) {
|
||
return Math.floor(diff / 60000) + '分钟前';
|
||
} else if (diff < 86400000) {
|
||
return Math.floor(diff / 3600000) + '小时前';
|
||
} else if (diff < 2592000000) {
|
||
return Math.floor(diff / 86400000) + '天前';
|
||
} else {
|
||
const date = new Date(timestamp);
|
||
return `${date.getMonth() + 1}-${date.getDate()}`;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.history-container {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%);
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
// 顶部导航
|
||
.nav-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 44px;
|
||
padding: 0 16px;
|
||
|
||
.nav-left,
|
||
.nav-right {
|
||
width: 44px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.iconfont {
|
||
font-size: 20px;
|
||
color: #ffffff;
|
||
}
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 17px;
|
||
font-weight: 500;
|
||
color: #ffffff;
|
||
}
|
||
}
|
||
|
||
// AI生成内容标识
|
||
.ai-notice {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
padding: 8px 16px;
|
||
margin: 8px 16px;
|
||
background: rgba(66, 202, 77, 0.1);
|
||
border: 1px solid rgba(66, 202, 77, 0.3);
|
||
border-radius: 8px;
|
||
backdrop-filter: blur(10px);
|
||
|
||
.ai-notice-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.ai-notice-text {
|
||
font-size: 12px;
|
||
color: #42ca4d;
|
||
line-height: 1.4;
|
||
}
|
||
}
|
||
|
||
// Tab栏
|
||
.tab-bar {
|
||
display: flex;
|
||
height: 44px;
|
||
padding: 0 16px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.tab-item {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
|
||
.tab-text {
|
||
font-size: 15px;
|
||
color: #8f9bb3;
|
||
}
|
||
|
||
&.active {
|
||
.tab-text {
|
||
color: #42ca4d;
|
||
font-weight: 500;
|
||
}
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 24px;
|
||
height: 3px;
|
||
background: #42ca4d;
|
||
border-radius: 2px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 内容滚动区
|
||
.content-scroll {
|
||
padding: 16px; width: 92%;
|
||
}
|
||
|
||
// 空状态
|
||
.empty-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 80px 0;
|
||
|
||
.empty-image {
|
||
width: 200px;
|
||
height: 200px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.empty-text {
|
||
font-size: 15px;
|
||
color: #8f9bb3;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.empty-btn {
|
||
width: 140px;
|
||
height: 44px;
|
||
background: linear-gradient(135deg, #42ca4d 0%, #38b045 100%);
|
||
border-radius: 22px;
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
border: none;
|
||
line-height: 44px;
|
||
}
|
||
}
|
||
|
||
// 历史列表
|
||
.history-list {
|
||
.history-item {
|
||
display: flex;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
background: rgba(26, 31, 46, 0.6);
|
||
border-radius: 16px;
|
||
margin-bottom: 16px;
|
||
min-height: 140px;
|
||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||
backdrop-filter: blur(20px);
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2), 0 0 1px rgba(255, 255, 255, 0.05);
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
background: rgba(26, 31, 46, 0.75);
|
||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 1px rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
&:hover {
|
||
border-color: rgba(66, 202, 77, 0.2);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3), 0 0 2px rgba(66, 202, 77, 0.1);
|
||
}
|
||
|
||
.item-images {
|
||
width: 120px;
|
||
height: 120px;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
|
||
.preview-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
&:hover .preview-image {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.image-count {
|
||
position: absolute;
|
||
bottom: 6px;
|
||
right: 6px;
|
||
padding: 3px 8px;
|
||
background: rgba(0, 0, 0, 0.75);
|
||
border-radius: 8px;
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||
|
||
.count-text {
|
||
font-size: 11px;
|
||
color: #ffffff;
|
||
font-weight: 500;
|
||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||
}
|
||
}
|
||
|
||
.type-badge {
|
||
position: absolute;
|
||
top: 6px;
|
||
right: 6px;
|
||
padding: 3px 8px;
|
||
border-radius: 10px;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
backdrop-filter: blur(8px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
z-index: 9;
|
||
|
||
.type-text {
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
color: #ffffff;
|
||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||
letter-spacing: 0.5px;
|
||
}
|
||
}
|
||
|
||
.status-badge {
|
||
position: absolute;
|
||
bottom: 8px;
|
||
left: 8px;
|
||
padding: 5px 12px;
|
||
border-radius: 14px;
|
||
backdrop-filter: blur(12px);
|
||
z-index: 10;
|
||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4), 0 0 12px rgba(66, 202, 77, 0.3);
|
||
|
||
.status-text {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
// 进行中状态 - 使用主色调高亮
|
||
&.status-processing {
|
||
background: linear-gradient(135deg, rgba(0, 167, 14, 0.95) 0%, rgba(0, 2, 0, 0.95) 100%);
|
||
box-shadow:
|
||
0 2px 12px rgba(66, 202, 77, 0.5),
|
||
0 0 20px rgba(66, 202, 77, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||
animation: pulse-glow 2s ease-in-out infinite;
|
||
border-color: rgba(66, 202, 77, 0.4);
|
||
}
|
||
|
||
// 已完成状态
|
||
&.status-completed {
|
||
background: linear-gradient(135deg, rgba(66, 202, 77, 0.9) 0%, rgba(56, 176, 69, 0.9) 100%);
|
||
box-shadow:
|
||
0 2px 8px rgba(66, 202, 77, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||
border-color: rgba(66, 202, 77, 0.3);
|
||
}
|
||
|
||
// 失败状态 - 使用红色
|
||
&.status-failed {
|
||
background: linear-gradient(135deg, rgba(255, 107, 107, 0.95) 0%, rgba(238, 90, 111, 0.95) 100%);
|
||
box-shadow:
|
||
0 2px 12px rgba(255, 107, 107, 0.5),
|
||
0 0 20px rgba(255, 107, 107, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||
border-color: rgba(255, 107, 107, 0.4);
|
||
}
|
||
}
|
||
|
||
// 脉冲光晕动画
|
||
@keyframes pulse-glow {
|
||
0%, 100% {
|
||
box-shadow:
|
||
0 2px 10px rgba(66, 202, 77, 0.5),
|
||
0 0 16px rgba(66, 202, 77, 0.4),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||
}
|
||
50% {
|
||
box-shadow:
|
||
0 2px 12px rgba(66, 202, 77, 0.7),
|
||
0 0 20px rgba(66, 202, 77, 0.6),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||
}
|
||
}
|
||
}
|
||
|
||
.item-info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
min-width: 0;
|
||
|
||
.info-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
|
||
.item-prompt {
|
||
flex: 1;
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
line-height: 1.5;
|
||
font-weight: 400;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.item-actions {
|
||
width: 28px;
|
||
height: 28px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
border-radius: 6px;
|
||
transition: all 0.2s ease;
|
||
|
||
.iconfont {
|
||
font-size: 18px;
|
||
color: #8f9bb3;
|
||
transition: color 0.2s ease;
|
||
}
|
||
|
||
&:active {
|
||
background: rgba(66, 202, 77, 0.1);
|
||
|
||
.iconfont {
|
||
color: #42ca4d;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.info-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: auto;
|
||
|
||
.meta-tags {
|
||
display: flex;
|
||
gap: 8px;
|
||
flex-wrap: wrap;
|
||
|
||
.meta-tag {
|
||
padding: 4px 10px;
|
||
background: rgba(61, 68, 88, 0.4);
|
||
border-radius: 8px;
|
||
font-size: 11px;
|
||
color: #8f9bb3;
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
backdrop-filter: blur(8px);
|
||
transition: all 0.2s ease;
|
||
|
||
&:first-child {
|
||
background: rgba(66, 202, 77, 0.15);
|
||
color: #42ca4d;
|
||
border-color: rgba(66, 202, 77, 0.3);
|
||
}
|
||
}
|
||
}
|
||
|
||
.meta-time {
|
||
font-size: 12px;
|
||
color: #6b7589;
|
||
flex-shrink: 0;
|
||
margin-left: 8px;
|
||
}
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 4px;
|
||
background: rgba(61, 68, 88, 0.4);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
margin-top: 12px;
|
||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #42ca4d 0%, #38b045 100%);
|
||
transition: width 0.3s ease;
|
||
box-shadow: 0 0 8px rgba(66, 202, 77, 0.5);
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
.item-remark {
|
||
margin-top: 12px;
|
||
padding: 10px 12px;
|
||
background: rgba(255, 107, 107, 0.1);
|
||
border: 1px solid rgba(255, 107, 107, 0.2);
|
||
border-radius: 8px;
|
||
backdrop-filter: blur(8px);
|
||
|
||
.remark-text {
|
||
font-size: 13px;
|
||
color: #ff6b6b;
|
||
line-height: 1.5;
|
||
word-break: break-word;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加载更多
|
||
.load-more {
|
||
padding: 20px;
|
||
text-align: center;
|
||
|
||
.load-text {
|
||
font-size: 13px;
|
||
color: #42ca4d;
|
||
}
|
||
}
|
||
|
||
// 操作菜单
|
||
.action-sheet {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: flex-end;
|
||
z-index: 9999;
|
||
|
||
.action-content {
|
||
width: 100%;
|
||
background: #1a1f2e;
|
||
border-radius: 16px 16px 0 0;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
|
||
.action-title {
|
||
padding: 16px;
|
||
text-align: center;
|
||
font-size: 13px;
|
||
color: #8f9bb3;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.action-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
|
||
.action-icon {
|
||
font-size: 18px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.action-text {
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
}
|
||
}
|
||
|
||
.action-cancel {
|
||
margin-top: 8px;
|
||
padding: 16px;
|
||
text-align: center;
|
||
font-size: 15px;
|
||
color: #ffffff;
|
||
background: rgba(61, 68, 88, 0.3);
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
|