This commit is contained in:
2026-07-10 16:14:31 +08:00
commit 637b0bd144
158 changed files with 33269 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+157
View File
@@ -0,0 +1,157 @@
import { historyDownload, historyList } from "../../utils/request"
type FilterKey = 'all' | 'done' | 'processing' | 'failed'
type RecordStatus = 'done' | 'processing' | 'failed'
interface HistoryRecord {
id: number
taskId: string
title: string
scene: string
avatar: string
time: string
duration: string
status: RecordStatus
statusText: string
secondaryText: string
sceneClass: string
isDone: boolean
isProcessing: boolean
isFailed: boolean
coverUrl: string
coverImage: string
videoUrl: string
progress: number
}
Page({
data: {
filters: [
{ key: 'all', label: '全部', active: true },
{ key: 'done', label: '已完成', active: false },
{ key: 'processing', label: '生成中', active: false },
],
activeFilter: 'all' as FilterKey,
records: [] as HistoryRecord[],
page: 1,
limit: 10,
hasMore: true,
loading: false,
},
onLoad() {
this.loadRecords(true)
},
onShow() {
this.loadRecords(true)
},
onPullDownRefresh() {
this.loadRecords(true).finally(() => wx.stopPullDownRefresh())
},
onReachBottom() {
if (this.data.hasMore && !this.data.loading) {
this.loadRecords(false)
}
},
loadRecords(reset = false) {
if (this.data.loading) return Promise.resolve()
const page = reset ? 1 : this.data.page + 1
this.setData({ loading: true })
return historyList(this.data.activeFilter, page, this.data.limit).then((res: any) => {
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message, showCancel: false })
return
}
const list = (res.data.list as HistoryRecord[]).map((item) => ({
...item,
coverImage: item.coverUrl
}))
this.setData({
records: reset ? list : this.data.records.concat(list),
page,
hasMore: res.data.hasMore,
})
}).catch(() => {
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
}).finally(() => {
this.setData({ loading: false })
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/index/index' }),
})
},
handleFilterTap(e: WechatMiniprogram.TouchEvent) {
const activeFilter = e.currentTarget.dataset.key as FilterKey
this.setData({
activeFilter,
filters: this.data.filters.map((item) => ({
...item,
active: item.key === activeFilter,
})),
})
this.loadRecords(true)
},
handleFailedTap() {
this.setData({
activeFilter: 'failed',
filters: this.data.filters.map((item) => ({ ...item, active: false })),
})
this.loadRecords(true)
},
handleDetail(e: WechatMiniprogram.TouchEvent) {
const rawStatus = String(e.currentTarget.dataset.status || '')
const taskId = String(e.currentTarget.dataset.taskId || '')
if (!taskId) return
const status = rawStatus === 'failed' ? 'failed' : rawStatus === 'processing' ? 'processing' : 'done'
wx.navigateTo({
url: '/pages/historyPackage/historyDetail/index?status=' + status + '&taskId=' + encodeURIComponent(taskId),
})
},
handleDownload(e: WechatMiniprogram.TouchEvent) {
const taskId = String(e.currentTarget.dataset.taskId || '')
if (!taskId) return
wx.showLoading({ title: '获取下载地址' })
historyDownload(taskId).then((res: any) => {
wx.hideLoading()
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '下载失败', showCancel: false })
return
}
const url = res.data.url
if (url) {
wx.setClipboardData({ data: url })
} else {
wx.showToast({ title: '暂无下载地址', icon: 'none' })
}
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
handleMore(e: WechatMiniprogram.TouchEvent) {
this.handleDetail(e)
},
handleRetry() {
wx.switchTab({ url: '/pages/index/index' })
},
})
+66
View File
@@ -0,0 +1,66 @@
<view class="history-page">
<custom-nav padding-x="52">
<view class="history-nav">
<text class="nav-title">创作记录</text>
</view>
</custom-nav>
<view class="history-content">
<view class="filter-row">
<view class="filter-tabs">
<view
wx:for="{{filters}}"
wx:key="key"
class="filter-chip {{item.active ? 'active' : ''}}"
data-key="{{item.key}}"
bindtap="handleFilterTap"
>{{item.label}}</view>
</view>
<view class="failed-btn {{activeFilter === 'failed' ? 'active' : ''}}" bindtap="handleFailedTap">
<image class="filter-icon icon-placeholder" src="/static/images/filter.png" mode="aspectFit"></image>
<text>失败记录</text>
</view>
</view>
<view class="record-list" wx:if="{{records.length}}">
<view wx:for="{{records}}" wx:key="taskId" class="record-card">
<view class="thumb {{item.sceneClass}}">
<image class="thumb-placeholder icon-placeholder" src="{{item.coverImage}}" mode="aspectFill"></image>
<view class="duration-badge">{{item.duration}}</view>
</view>
<view class="record-main">
<text class="record-title">{{item.title}}</text>
<view class="tag-row">
<text class="tag">{{item.scene}}</text>
<text class="tag">{{item.avatar}}</text>
</view>
<text class="record-time">{{item.time}}</text>
<view class="status-line {{item.status}}">
<text wx:if="{{item.isProcessing}}" class="progress-dot"></text>
<text>{{item.statusText}}</text>
</view>
<view class="action-row">
<view class="secondary-btn" data-status="{{item.status}}" data-task-id="{{item.taskId}}" bindtap="handleDetail">{{item.secondaryText}}</view>
<view wx:if="{{item.isDone}}" class="download-btn" data-task-id="{{item.taskId}}" bindtap="handleDownload">
<image class="download-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>下载</text>
</view>
<view wx:elif="{{item.isProcessing}}" class="more-btn" data-status="{{item.status}}" data-task-id="{{item.taskId}}" bindtap="handleMore">
<text></text>
<text></text>
<text></text>
</view>
<view wx:else class="retry-btn" data-task-id="{{item.taskId}}" bindtap="handleRetry">重新生成</view>
</view>
</view>
</view>
</view>
<view wx:elif="{{!loading}}" class="empty-state">暂无创作记录</view>
<view wx:if="{{loading}}" class="empty-state">加载中...</view>
</view>
</view>
+470
View File
@@ -0,0 +1,470 @@
page {
min-height: 100vh;
background: #fbfcff;
color: #161a26;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.history-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background: linear-gradient(180deg, #ffffff 0%, #fbfcff 52%, #ffffff 100%);
}
.history-nav {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back,
.nav-placeholder {
width: 58rpx;
height: 58rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.back-line {
width: 24rpx;
height: 24rpx;
display: block;
border-left: 5rpx solid #11151e;
border-bottom: 5rpx solid #11151e;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title {
font-size: 31rpx;
font-weight: 800;
color: #11141f;
letter-spacing: 0;
}
.history-content {
flex: 1;
display: flex;
flex-direction: column;
padding: 26rpx 24rpx 24rpx;
box-sizing: border-box;
}
.filter-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 18rpx;
margin-bottom: 26rpx;
}
.filter-tabs {
display: flex;
align-items: center;
gap: 20rpx;
min-width: 0;
}
.filter-chip,
.failed-btn {
height: 52rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 23rpx;
font-weight: 800;
box-sizing: border-box;
white-space: nowrap;
}
.filter-chip {
min-width: 90rpx;
padding: 0 24rpx;
color: #727887;
background: #f4f6fb;
border: 1rpx solid #e8ebf4;
box-shadow: inset 0 1rpx 5rpx rgba(72, 82, 124, 0.04);
}
.filter-chip.active {
color: #ffffff;
border-color: transparent;
background: linear-gradient(135deg, #5c7cff 0%, #7148f4 100%);
box-shadow: 0 8rpx 16rpx rgba(91, 96, 255, 0.28);
}
.failed-btn {
width: 164rpx;
gap: 12rpx;
color: #3d4352;
background: #ffffff;
border: 2rpx solid #e6e8f7;
box-shadow: 0 6rpx 16rpx rgba(72, 82, 124, 0.05);
}
.filter-icon {
width: 22rpx;
height: 24rpx;
display: block;
border: 4rpx solid #676d85;
border-bottom: 0;
border-radius: 5rpx 5rpx 2rpx 2rpx;
box-sizing: border-box;
transform: skewX(-8deg);
}
.filter-icon::after {
content: "";
display: block;
width: 7rpx;
height: 14rpx;
margin: 16rpx auto 0;
background: #676d85;
border-radius: 999rpx;
}
.record-list {
display: flex;
flex-direction: column;
gap: 18rpx;
}
.record-card {
min-height: 206rpx;
padding: 16rpx 18rpx;
border-radius: 17rpx;
display: flex;
gap: 24rpx;
background: rgba(255, 255, 255, 0.96);
border: 1rpx solid #edf0fb;
box-shadow: 0 8rpx 22rpx rgba(74, 88, 148, 0.06);
box-sizing: border-box;
}
.thumb {
width: 278rpx;
height: 172rpx;
border-radius: 10rpx;
flex-shrink: 0;
overflow: hidden;
display: flex;
align-items: flex-end;
justify-content: flex-start;
padding: 12rpx;
box-sizing: border-box;
background: #ece6dc;
}
.scene-layer {
display: flex;
}
.scene-bg,
.scene-subject {
pointer-events: none;
}
/*
.scene-live {
background:
linear-gradient(90deg, rgba(249, 243, 232, 0.92), rgba(240, 235, 222, 0.74)),
linear-gradient(135deg, #e9dac9 0%, #fbf8f1 45%, #d3dcc1 100%);
}
.scene-live::before,
.scene-classroom::before {
content: "";
width: 76rpx;
height: 118rpx;
margin-left: 82rpx;
margin-bottom: 24rpx;
border-radius: 36rpx 36rpx 10rpx 10rpx;
background: linear-gradient(180deg, #20202a 0 24%, #fff7ef 24% 42%, #ffffff 42% 100%);
box-shadow: 0 0 0 16rpx rgba(255, 255, 255, 0.34);
} */
.scene-product {
/* background:
radial-gradient(circle at 80% 22%, rgba(126, 151, 102, 0.32), transparent 15%),
radial-gradient(circle at 12% 28%, rgba(126, 151, 102, 0.25), transparent 16%),
linear-gradient(135deg, #e9e2d4, #fbf8ef 48%, #d8c8ad);
justify-content: center; */
}
/* .scene-product::before {
content: "";
width: 56rpx;
height: 96rpx;
margin-bottom: 32rpx;
border-radius: 11rpx 11rpx 7rpx 7rpx;
background: linear-gradient(180deg, #1e1b18 0 18%, #5b2e1d 18% 100%);
box-shadow: 0 22rpx 0 58rpx rgba(184, 151, 102, 0.22);
}
.scene-living {
background:
radial-gradient(circle at 14% 16%, rgba(121, 151, 95, 0.26), transparent 17%),
radial-gradient(circle at 88% 20%, rgba(121, 151, 95, 0.22), transparent 14%),
linear-gradient(180deg, #eee1cf 0%, #d8c1a1 100%);
justify-content: center;
}
.scene-living::before {
content: "";
width: 160rpx;
height: 58rpx;
margin-bottom: 44rpx;
border-radius: 18rpx 18rpx 10rpx 10rpx;
background: linear-gradient(180deg, #f4eadf, #bf9d7c);
box-shadow: 0 48rpx 0 -18rpx rgba(106, 76, 52, 0.28);
} */
.duration-badge {
height: 34rpx;
min-width: 68rpx;
padding: 0 10rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
font-weight: 800;
background: rgba(0, 0, 0, 0.56);
box-sizing: border-box;
}
.record-main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
padding-top: 8rpx;
}
.record-title {
font-size: 27rpx;
line-height: 1.25;
font-weight: 900;
color: #111622;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tag-row {
margin-top: 16rpx;
display: flex;
align-items: center;
gap: 12rpx;
}
.tag {
height: 34rpx;
padding: 0 14rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #858b9d;
background: #f3f4f8;
font-size: 19rpx;
font-weight: 800;
white-space: nowrap;
}
.record-time {
margin-top: 18rpx;
font-size: 22rpx;
line-height: 1.2;
color: #777d92;
font-weight: 700;
}
.status-line {
min-height: 34rpx;
margin-top: auto;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8rpx;
font-size: 21rpx;
font-weight: 900;
}
.status-line.done {
color: #32b975;
}
.status-line.done text {
height: 34rpx;
padding: 0 16rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
background: #dcf8e9;
}
.status-line.processing {
color: #686dff;
}
.progress-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
border: 4rpx solid currentColor;
border-top-color: rgba(104, 109, 255, 0.18);
box-sizing: border-box;
}
.status-line.failed {
color: #f06d67;
}
.status-line.failed text {
height: 34rpx;
padding: 0 16rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
background: #fff0ed;
}
.action-row {
margin-top: 14rpx;
display: flex;
align-items: center;
gap: 18rpx;
}
.secondary-btn,
.download-btn,
.more-btn,
.retry-btn {
height: 50rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 21rpx;
font-weight: 900;
box-sizing: border-box;
}
.secondary-btn {
flex: 1;
min-width: 128rpx;
color: #555b6e;
background: #ffffff;
border: 2rpx solid #dadcff;
}
.download-btn,
.retry-btn {
width: 158rpx;
color: #ffffff;
gap: 10rpx;
background: linear-gradient(135deg, #5a84ff, #763fee);
box-shadow: 0 8rpx 14rpx rgba(97, 86, 241, 0.24);
}
.download-icon {
width: 22rpx;
height: 24rpx;
display: block;
border-bottom: 4rpx solid currentColor;
box-sizing: border-box;
}
.download-icon::before {
content: "";
display: block;
width: 4rpx;
height: 22rpx;
margin: 0 auto;
background: currentColor;
border-radius: 999rpx;
}
.download-icon::after {
content: "";
display: block;
width: 14rpx;
height: 14rpx;
margin: -12rpx auto 0;
border-right: 4rpx solid currentColor;
border-bottom: 4rpx solid currentColor;
transform: rotate(45deg);
box-sizing: border-box;
}
.more-btn,
.retry-btn {
background: #ffffff;
color: #50566c;
border: 2rpx solid #e0e2ff;
box-shadow: none;
}
.more-btn {
width: 100rpx;
gap: 7rpx;
}
.more-btn text {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: currentColor;
}
.thumb-placeholder {
width: 100%;
height: 100%;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
.thumb {
position: relative;
padding: 0;
background: #ffffff;
}
.duration-badge {
position: absolute;
left: 12rpx;
bottom: 12rpx;
}
.failed-btn.active {
color: #999999;
/* border-color: transparent; */
/* background: linear-gradient(135deg, #f06d67 0%, #ff8a62 100%); */
}
.empty-state {
min-height: 360rpx;
display: flex;
align-items: center;
justify-content: center;
color: #8a91a3;
font-size: 25rpx;
font-weight: 800;
}