816 lines
20 KiB
Vue
816 lines
20 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"> </view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- Tab切换 -->
|
|||
|
|
<view class="tab-bar">
|
|||
|
|
<view
|
|||
|
|
v-for="(tab, index) in tabs"
|
|||
|
|
:key="index"
|
|||
|
|
:class="['tab-item', selectedTab === tab.value ? 'active' : '']"
|
|||
|
|
@click="switchTab(tab.value)"
|
|||
|
|
>
|
|||
|
|
<text class="tab-text">{{ tab.label }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 内容区 -->
|
|||
|
|
<scroll-view scroll-y class="content-scroll" :style="{ height: scrollViewHeight }" @scrolltolower="loadMore">
|
|||
|
|
<!-- 空状态 -->
|
|||
|
|
<view class="empty-state" v-if="historyList.length === 0 && !isLoading">
|
|||
|
|
<text class="empty-icon">🍽️</text>
|
|||
|
|
<text class="empty-text">暂无饮食记录</text>
|
|||
|
|
<button class="empty-btn" @click="goToCheckin">去打卡</button>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 历史列表 -->
|
|||
|
|
<view class="history-list" v-else>
|
|||
|
|
<view
|
|||
|
|
v-for="(item, index) in historyList"
|
|||
|
|
:key="index"
|
|||
|
|
class="history-item"
|
|||
|
|
@click="viewDetail(item)"
|
|||
|
|
>
|
|||
|
|
<!-- 左侧图片预览 -->
|
|||
|
|
<view class="item-images">
|
|||
|
|
<image
|
|||
|
|
v-if="item.photos && item.photos.length > 0"
|
|||
|
|
:src="item.photos[0]"
|
|||
|
|
mode="aspectFill"
|
|||
|
|
class="preview-image"
|
|||
|
|
></image>
|
|||
|
|
<view class="no-image" v-else>
|
|||
|
|
<text>无图</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view class="image-count" v-if="item.photos && item.photos.length > 1">
|
|||
|
|
<text class="count-text">+{{ item.photos.length - 1 }}</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 餐次标识 -->
|
|||
|
|
<view class="status-badge status-completed">
|
|||
|
|
<text class="status-text">{{ getMealTypeText(item.mealType) }}</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 视频状态标记 -->
|
|||
|
|
<view class="video-status-badge" v-if="item.enableAIVideo">
|
|||
|
|
<text v-if="item.videoUrl || item.videoStatus === 1" class="status-success">📹</text>
|
|||
|
|
<text v-else-if="item.videoStatus === 2" class="status-failed">❌</text>
|
|||
|
|
<text v-else class="status-pending">⏳</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 右侧信息 -->
|
|||
|
|
<view class="item-info">
|
|||
|
|
<view class="info-header">
|
|||
|
|
<text class="item-prompt">{{ item.notes || '健康饮食打卡' }}</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.date }}</text>
|
|||
|
|
<text class="meta-tag" v-if="item.points > 0">+{{ item.points }}积分</text>
|
|||
|
|
<!-- 视频状态文字提示 -->
|
|||
|
|
<text class="meta-tag video-tag" v-if="item.videoUrl || item.videoStatus === 1">有视频</text>
|
|||
|
|
<text class="meta-tag video-tag generating" v-else-if="item.enableAIVideo && item.taskId && item.videoStatus === 0">生成中</text>
|
|||
|
|
<text class="meta-tag video-tag failed" v-else-if="item.videoStatus === 2">视频失败</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- AI分析状态 -->
|
|||
|
|
<view class="item-remark" v-if="item.aiAnalysis">
|
|||
|
|
<text class="remark-text">{{ item.aiAnalysis }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 加载更多 -->
|
|||
|
|
<view class="load-more" v-if="hasMore && historyList.length > 0">
|
|||
|
|
<text class="load-text">{{ isLoading ? '加载中...' : '加载更多' }}</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-text">{{ action.label }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="action-cancel" @click="showActionSheet = false">
|
|||
|
|
<text>取消</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
import { getCheckinList, getVideoTaskStatus } from '@/api/tool.js';
|
|||
|
|
import { mapGetters } from 'vuex';
|
|||
|
|
|
|||
|
|
export default {
|
|||
|
|
computed: {
|
|||
|
|
...mapGetters(['userInfo', 'uid'])
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
statusBarHeight: 0,
|
|||
|
|
scrollViewHeight: '100vh',
|
|||
|
|
|
|||
|
|
// Tab
|
|||
|
|
selectedTab: '',
|
|||
|
|
tabs: [
|
|||
|
|
{ label: '全部', value: '' },
|
|||
|
|
{ label: '早餐', value: 'breakfast' },
|
|||
|
|
{ label: '午餐', value: 'lunch' },
|
|||
|
|
{ label: '晚餐', value: 'dinner' },
|
|||
|
|
{ label: '加餐', value: 'snack' },
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
// 历史记录
|
|||
|
|
historyList: [],
|
|||
|
|
|
|||
|
|
// 分页
|
|||
|
|
currentPage: 1,
|
|||
|
|
pageSize: 10,
|
|||
|
|
hasMore: true,
|
|||
|
|
isLoading: false,
|
|||
|
|
|
|||
|
|
// 视频轮询
|
|||
|
|
pollingTimer: null,
|
|||
|
|
pollingCount: 0,
|
|||
|
|
maxPollingCount: 60, // 最多轮询60次(5分钟)
|
|||
|
|
|
|||
|
|
// 操作菜单
|
|||
|
|
showActionSheet: false,
|
|||
|
|
currentItem: null,
|
|||
|
|
currentActions: []
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onLoad() {
|
|||
|
|
this.initPage();
|
|||
|
|
this.loadHistoryList();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onShow() {
|
|||
|
|
// 从其他页面返回时重新检查
|
|||
|
|
if (this.historyList.length > 0) {
|
|||
|
|
this.loadHistoryList(true);
|
|||
|
|
}
|
|||
|
|
this.startPolling();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
onUnload() {
|
|||
|
|
this.stopPolling();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
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();
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 切换Tab
|
|||
|
|
switchTab(tabValue) {
|
|||
|
|
if (this.selectedTab === tabValue) return;
|
|||
|
|
this.selectedTab = tabValue;
|
|||
|
|
this.loadHistoryList(true);
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载历史记录列表
|
|||
|
|
async loadHistoryList(refresh = false) {
|
|||
|
|
if (this.isLoading) return;
|
|||
|
|
|
|||
|
|
if (refresh) {
|
|||
|
|
this.currentPage = 1;
|
|||
|
|
this.hasMore = true;
|
|||
|
|
this.historyList = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!this.hasMore) return;
|
|||
|
|
|
|||
|
|
this.isLoading = true;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const res = await getCheckinList({
|
|||
|
|
mealType: this.selectedTab,
|
|||
|
|
page: this.currentPage,
|
|||
|
|
limit: this.pageSize
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (res && res.code === 200) {
|
|||
|
|
const list = (res.data.list || res.data || []).map(item => {
|
|||
|
|
// 解析photos
|
|||
|
|
let photos = [];
|
|||
|
|
if (item.photos) {
|
|||
|
|
try {
|
|||
|
|
// 如果是字符串,尝试解析JSON
|
|||
|
|
if (typeof item.photos === 'string') {
|
|||
|
|
photos = JSON.parse(item.photos);
|
|||
|
|
} else if (Array.isArray(item.photos)) {
|
|||
|
|
photos = item.photos;
|
|||
|
|
}
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('解析图片数据失败:', e);
|
|||
|
|
// 如果解析失败,可能是单个URL字符串,或者是无法解析的字符串
|
|||
|
|
if (typeof item.photos === 'string' && item.photos.startsWith('http')) {
|
|||
|
|
photos = [item.photos];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
...item,
|
|||
|
|
photos: photos,
|
|||
|
|
date: item.createTime, // 使用 createTime 作为显示日期
|
|||
|
|
points: item.points || 0, // 接口可能没有返回积分,默认0
|
|||
|
|
aiAnalysis: item.aiAnalysis || item.nutritionScore, // AI分析结果
|
|||
|
|
taskId: item.taskId || null, // 视频任务ID
|
|||
|
|
videoUrl: item.videoUrl || null, // 视频URL
|
|||
|
|
videoStatus: item.videoStatus !== undefined ? item.videoStatus : null, // 视频状态
|
|||
|
|
enableAIVideo: item.enableAIVideo || false // 是否启用AI视频
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (refresh) {
|
|||
|
|
this.historyList = list;
|
|||
|
|
} else {
|
|||
|
|
this.historyList = [...this.historyList, ...list];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 判断是否还有更多
|
|||
|
|
if (list.length < this.pageSize) {
|
|||
|
|
this.hasMore = false;
|
|||
|
|
} else {
|
|||
|
|
this.currentPage++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 启动轮询检查视频任务
|
|||
|
|
this.startPolling();
|
|||
|
|
} else {
|
|||
|
|
throw new Error(res?.message || '获取记录失败');
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载历史记录失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: '加载失败,请重试',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
this.isLoading = false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 加载更多
|
|||
|
|
loadMore() {
|
|||
|
|
if (this.hasMore && !this.isLoading) {
|
|||
|
|
this.loadHistoryList(false);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 去打卡
|
|||
|
|
goToCheckin() {
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: '/pages/tool/checkin-publish'
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 查看详情
|
|||
|
|
viewDetail(item) {
|
|||
|
|
uni.navigateTo({
|
|||
|
|
url: `/pages/tool/checkin-detail?id=${item.id}`
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示管理菜单
|
|||
|
|
showManageMenu() {
|
|||
|
|
this.currentActions = [
|
|||
|
|
{ label: '刷新列表', value: 'refresh' }
|
|||
|
|
];
|
|||
|
|
this.showActionSheet = true;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示单项菜单
|
|||
|
|
showItemMenu(item) {
|
|||
|
|
this.currentItem = item;
|
|||
|
|
this.currentActions = [
|
|||
|
|
{ label: '查看详情', value: 'view' },
|
|||
|
|
// { label: '删除', value: 'delete' } // 暂未实现删除接口
|
|||
|
|
];
|
|||
|
|
this.showActionSheet = true;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理操作
|
|||
|
|
handleAction(action) {
|
|||
|
|
this.showActionSheet = false;
|
|||
|
|
|
|||
|
|
switch (action) {
|
|||
|
|
case 'view':
|
|||
|
|
this.viewDetail(this.currentItem);
|
|||
|
|
break;
|
|||
|
|
case 'refresh':
|
|||
|
|
this.loadHistoryList(true);
|
|||
|
|
break;
|
|||
|
|
case 'delete':
|
|||
|
|
uni.showToast({ title: '删除功能开发中', icon: 'none' });
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
getMealTypeText(type) {
|
|||
|
|
const map = {
|
|||
|
|
breakfast: '早餐',
|
|||
|
|
lunch: '午餐',
|
|||
|
|
dinner: '晚餐',
|
|||
|
|
snack: '加餐'
|
|||
|
|
};
|
|||
|
|
return map[type] || type;
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 启动视频任务轮询
|
|||
|
|
startPolling() {
|
|||
|
|
if (this.pollingTimer) return;
|
|||
|
|
|
|||
|
|
const tasksToCheck = this.historyList.filter(item =>
|
|||
|
|
item.enableAIVideo && item.taskId && !item.videoUrl && item.videoStatus !== 2
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (tasksToCheck.length === 0) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.pollingTimer = setInterval(() => {
|
|||
|
|
this.pollVideoTasks();
|
|||
|
|
}, 5000); // 每5秒查询一次
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 轮询视频任务状态
|
|||
|
|
async pollVideoTasks() {
|
|||
|
|
const tasksToCheck = this.historyList.filter(item =>
|
|||
|
|
item.enableAIVideo && item.taskId && !item.videoUrl && item.videoStatus !== 2
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (tasksToCheck.length === 0 || this.pollingCount >= this.maxPollingCount) {
|
|||
|
|
this.stopPolling();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.pollingCount++;
|
|||
|
|
|
|||
|
|
for (const task of tasksToCheck) {
|
|||
|
|
try {
|
|||
|
|
const res = await getVideoTaskStatus(task.taskId);
|
|||
|
|
if (res.code === 200 && res.data) {
|
|||
|
|
const status = res.data.state;
|
|||
|
|
if (status === 'success') {
|
|||
|
|
// 任务成功,刷新列表获取最新视频URL
|
|||
|
|
this.loadHistoryList(true);
|
|||
|
|
this.pollingCount = 0; // 重置计数
|
|||
|
|
break;
|
|||
|
|
} else if (status === 'fail') {
|
|||
|
|
// 任务失败,刷新列表以更新失败状态,停止对该任务的轮询
|
|||
|
|
this.loadHistoryList(true);
|
|||
|
|
this.pollingCount = 0; // 重置计数
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('查询视频任务失败:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 停止轮询
|
|||
|
|
stopPolling() {
|
|||
|
|
if (this.pollingTimer) {
|
|||
|
|
clearInterval(this.pollingTimer);
|
|||
|
|
this.pollingTimer = null;
|
|||
|
|
}
|
|||
|
|
this.pollingCount = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.history-container {
|
|||
|
|
width: 100%;
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background-color: #f4f5f7;
|
|||
|
|
overflow-x: hidden;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 顶部导航
|
|||
|
|
.nav-bar {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
height: 44px;
|
|||
|
|
padding: 0 16px;
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
position: sticky;
|
|||
|
|
top: 0;
|
|||
|
|
z-index: 100;
|
|||
|
|
|
|||
|
|
.nav-left,
|
|||
|
|
.nav-right {
|
|||
|
|
width: 44px;
|
|||
|
|
height: 44px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
|
|||
|
|
.iconfont {
|
|||
|
|
font-size: 20px;
|
|||
|
|
color: #333333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.nav-title {
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #333333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Tab栏
|
|||
|
|
.tab-bar {
|
|||
|
|
display: flex;
|
|||
|
|
height: 44px;
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-bottom: 1px solid #eeeeee;
|
|||
|
|
z-index: 99;
|
|||
|
|
|
|||
|
|
.tab-item {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
position: relative;
|
|||
|
|
|
|||
|
|
.tab-text {
|
|||
|
|
font-size: 15px;
|
|||
|
|
color: #999999;
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.active {
|
|||
|
|
.tab-text {
|
|||
|
|
color: #ff6b35;
|
|||
|
|
font-weight: 500;
|
|||
|
|
font-size: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&::after {
|
|||
|
|
content: '';
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 0;
|
|||
|
|
left: 50%;
|
|||
|
|
transform: translateX(-50%);
|
|||
|
|
width: 24px;
|
|||
|
|
height: 3px;
|
|||
|
|
background: #ff6b35;
|
|||
|
|
border-radius: 2px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 内容滚动区
|
|||
|
|
.content-scroll {
|
|||
|
|
flex: 1;
|
|||
|
|
padding: 16px;
|
|||
|
|
width: 100%;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 空状态
|
|||
|
|
.empty-state {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding-top: 100px;
|
|||
|
|
|
|||
|
|
.empty-icon {
|
|||
|
|
font-size: 60px;
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-text {
|
|||
|
|
font-size: 15px;
|
|||
|
|
color: #999999;
|
|||
|
|
margin-bottom: 32px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.empty-btn {
|
|||
|
|
width: 160px;
|
|||
|
|
height: 44px;
|
|||
|
|
background: linear-gradient(135deg, #ff6b35 0%, #ff8c5a 100%);
|
|||
|
|
border-radius: 22px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #ffffff;
|
|||
|
|
border: none;
|
|||
|
|
line-height: 44px;
|
|||
|
|
box-shadow: 0 4px 12px rgba(255, 107, 53, 0.3);
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
transform: scale(0.98);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 历史列表
|
|||
|
|
.history-list {
|
|||
|
|
padding-bottom: 20px;
|
|||
|
|
|
|||
|
|
.history-item {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 16px;
|
|||
|
|
padding: 16px;
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 16px;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
min-height: 100px;
|
|||
|
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
|
|||
|
|
transition: all 0.3s;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
transform: scale(0.99);
|
|||
|
|
background: #fafafa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-images {
|
|||
|
|
width: 96px;
|
|||
|
|
height: 96px;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
position: relative;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
overflow: hidden;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
|
|||
|
|
.preview-image {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
object-fit: cover;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.no-image {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
color: #cccccc;
|
|||
|
|
font-size: 12px;
|
|||
|
|
background-color: #f0f0f0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.image-count {
|
|||
|
|
position: absolute;
|
|||
|
|
bottom: 4px;
|
|||
|
|
right: 4px;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
background: rgba(0, 0, 0, 0.6);
|
|||
|
|
border-radius: 4px;
|
|||
|
|
|
|||
|
|
.count-text {
|
|||
|
|
font-size: 10px;
|
|||
|
|
color: #ffffff;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.status-badge {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 4px;
|
|||
|
|
left: 4px;
|
|||
|
|
padding: 2px 6px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
background: rgba(255, 255, 255, 0.9);
|
|||
|
|
backdrop-filter: blur(4px);
|
|||
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|||
|
|
|
|||
|
|
.status-text {
|
|||
|
|
font-size: 10px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
color: #ff6b35;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.video-status-badge {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 4px;
|
|||
|
|
right: 4px;
|
|||
|
|
width: 20px;
|
|||
|
|
height: 20px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background: rgba(0, 0, 0, 0.6);
|
|||
|
|
border-radius: 50%;
|
|||
|
|
|
|||
|
|
text {
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-info {
|
|||
|
|
flex: 1;
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
min-width: 0;
|
|||
|
|
padding: 2px 0;
|
|||
|
|
|
|||
|
|
.info-header {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
gap: 8px;
|
|||
|
|
|
|||
|
|
.item-prompt {
|
|||
|
|
flex: 1;
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #333333;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
overflow: hidden;
|
|||
|
|
text-overflow: ellipsis;
|
|||
|
|
display: -webkit-box;
|
|||
|
|
-webkit-line-clamp: 2;
|
|||
|
|
line-clamp: 2;
|
|||
|
|
-webkit-box-orient: vertical;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-actions {
|
|||
|
|
width: 24px;
|
|||
|
|
height: 24px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
justify-content: center;
|
|||
|
|
margin-top: -2px;
|
|||
|
|
|
|||
|
|
.iconfont {
|
|||
|
|
font-size: 18px;
|
|||
|
|
color: #999999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-meta {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
margin-top: 12px;
|
|||
|
|
|
|||
|
|
.meta-tags {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
align-items: center;
|
|||
|
|
|
|||
|
|
.meta-tag {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #999999;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
padding: 2px 8px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
color: #ff6b35;
|
|||
|
|
background: rgba(255, 107, 53, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.video-tag {
|
|||
|
|
color: #52c41a;
|
|||
|
|
background: rgba(82, 196, 26, 0.1);
|
|||
|
|
|
|||
|
|
&.generating {
|
|||
|
|
color: #1890ff;
|
|||
|
|
background: rgba(24, 144, 255, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&.failed {
|
|||
|
|
color: #ff4d4f;
|
|||
|
|
background: rgba(255, 77, 79, 0.1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.item-remark {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
padding: 8px;
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
|
|||
|
|
.remark-text {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #666666;
|
|||
|
|
line-height: 1.4;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 加载更多
|
|||
|
|
.load-more {
|
|||
|
|
padding: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
|
|||
|
|
.load-text {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #999999;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 操作菜单
|
|||
|
|
.action-sheet {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background: rgba(0, 0, 0, 0.5);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: flex-end;
|
|||
|
|
z-index: 9999;
|
|||
|
|
|
|||
|
|
.action-content {
|
|||
|
|
width: 100%;
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-radius: 16px 16px 0 0;
|
|||
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|||
|
|
overflow: hidden;
|
|||
|
|
|
|||
|
|
.action-title {
|
|||
|
|
padding: 16px;
|
|||
|
|
text-align: center;
|
|||
|
|
font-size: 14px;
|
|||
|
|
color: #999999;
|
|||
|
|
border-bottom: 1px solid #eeeeee;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
padding: 16px;
|
|||
|
|
border-bottom: 1px solid #eeeeee;
|
|||
|
|
background: #ffffff;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-text {
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #333333;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-cancel {
|
|||
|
|
margin-top: 8px;
|
|||
|
|
padding: 16px;
|
|||
|
|
text-align: center;
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #333333;
|
|||
|
|
background: #ffffff;
|
|||
|
|
border-top: 1px solid #eeeeee;
|
|||
|
|
|
|||
|
|
&:active {
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|