This commit is contained in:
2026-07-10 16:14:31 +08:00
commit 637b0bd144
158 changed files with 33269 additions and 0 deletions
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
@@ -0,0 +1,217 @@
import { historyCancel, historyDelete, historyDetail, historyDownload } from "../../../utils/request"
type DetailStatus = 'done' | 'processing' | 'failed'
const emptyDetail = {
taskId: '',
title: '',
status: 'done',
statusText: '',
progress: 0,
pageTitle: '作品详情',
pageClass: 'success-page',
videoUrl: '',
coverUrl: '',
duration: '',
createTime: '',
finishTime: '',
failureTime: '',
failureStage: '',
failureReason: '',
scriptText: '',
avatar: { name: '默认数字人', imageUrl: '' },
voice: { name: '默认音色', audioUrl: '' },
background: { name: '默认背景', coverUrl: '', imageUrl: '' },
steps: [] as any[],
avatarImage: '/static/images/my/image.png',
backgroundImage: '/static/images/my/image.png',
videoCover: '/static/images/my/image.png',
failureMessage: '生成失败,请稍后重试',
failureDisplayTime: '',
successTime: '',
taskDisplayId: '',
scriptDisplayText: '暂无文案',
}
Page({
data: {
status: 'done' as DetailStatus,
isFailed: false,
isProcessing: false,
pageTitle: '作品详情',
pageClass: 'success-page',
taskId: '',
detail: emptyDetail as any,
progressStyle: 'width: 0%;',
},
onLoad(query: Record<string, string | undefined>) {
const status: DetailStatus = query.status === 'failed'
? 'failed'
: query.status === 'processing'
? 'processing'
: 'done'
this.setStatus(status, query.taskId || '')
if (query.taskId) {
this.loadDetail(query.taskId)
}
},
loadDetail(taskId: string) {
wx.showLoading({ title: '加载中' })
historyDetail(taskId).then((res: any) => {
wx.hideLoading()
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '获取详情失败', showCancel: false })
return
}
const data = res.data || {}
const detail = { ...emptyDetail, ...data }
detail.avatar = { ...emptyDetail.avatar, ...(data.avatar || {}) }
detail.voice = { ...emptyDetail.voice, ...(data.voice || {}) }
detail.background = { ...emptyDetail.background, ...(data.background || {}) }
detail.avatarImage = detail.avatar.imageUrl || '/static/images/my/image.png'
detail.backgroundImage = detail.background.coverUrl || detail.background.imageUrl || '/static/images/my/image.png'
detail.videoCover = detail.coverUrl || detail.avatar.imageUrl || '/static/images/my/image.png'
detail.failureMessage = detail.failureReason || '生成失败,请稍后重试'
detail.failureDisplayTime = detail.failureTime || detail.finishTime || detail.createTime
detail.successTime = detail.finishTime || detail.createTime
detail.taskDisplayId = detail.taskId || taskId
detail.scriptDisplayText = detail.scriptText || '暂无文案';
let status = '';
if (detail.status === 'failed') {
status = 'failed';
} else {
status = detail.status === 'processing' ? 'processing' : 'done';
}
this.setData({
detail,
progressStyle: 'width: ' + Math.max(0, Math.min(100, Number(+detail.progress))) + '%;',
})
this.setStatus(status as DetailStatus, detail.taskId || taskId)
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
setStatus(status: DetailStatus, taskId: string) {
this.setData({
status,
isFailed: status === 'failed',
isProcessing: status === 'processing',
pageTitle: status === 'failed' ? '失败详情' : status === 'processing' ? '生成详情' : '作品详情',
pageClass: status === 'failed' ? 'failed-page' : status === 'processing' ? 'processing-page' : 'success-page',
taskId,
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/history/history' }),
})
},
handleDownload() {
if (!this.data.taskId) return
wx.showLoading({ title: '获取下载地址' })
historyDownload(this.data.taskId).then((res: any) => {
wx.hideLoading()
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '下载失败', showCancel: false })
return
}
const url = (res.data && res.data.url) || ''
if (url) {
wx.setClipboardData({ data: url })
} else {
wx.showToast({ title: '暂无下载地址', icon: 'none' })
}
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
handleRetry() {
wx.switchTab({ url: '/pages/index/index' })
},
handleEdit() {
wx.switchTab({ url: '/pages/index/index' })
},
handleViewCopy() {
this.handleCopyText()
},
handleCopyText() {
const detail = this.data.detail || {}
const text = detail.scriptText || ''
if (!text) {
wx.showToast({ title: '暂无文案', icon: 'none' })
return
}
wx.setClipboardData({ data: text })
},
handleSame() {
wx.switchTab({ url: '/pages/index/index' })
},
handleRecordVoice() {
wx.switchTab({ url: '/pages/index/index' })
},
handleBackToHistory() {
wx.switchTab({ url: '/pages/history/history' })
},
handleCancel() {
if (!this.data.taskId) return
wx.showModal({
title: '确认取消',
content: '确定取消当前生成任务吗?',
success: (modal) => {
if (!modal.confirm) return
historyCancel(this.data.taskId).then((res: any) => {
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '取消失败', showCancel: false })
return
}
wx.showToast({ title: '已取消', icon: 'success' })
this.loadDetail(this.data.taskId)
})
},
})
},
handleDelete() {
if (!this.data.taskId) return
wx.showModal({
title: '确认删除',
content: '删除后将不再展示该创作记录',
success: (modal) => {
if (!modal.confirm) return
historyDelete(this.data.taskId).then((res: any) => {
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '删除失败', showCancel: false })
return
}
wx.showToast({ title: '已删除', icon: 'success' })
setTimeout(() => this.handleBackToHistory(), 400)
})
},
})
},
})
@@ -0,0 +1,79 @@
<view class="detail-page {{pageClass}}">
<custom-nav padding-x="42">
<view class="detail-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<text class="nav-title">{{pageTitle}}</text>
<view class="nav-placeholder"></view>
</view>
</custom-nav>
<view class="detail-content">
<block wx:if="{{isFailed}}">
<view class="failure-alert">
<view class="alert-icon">!</view>
<view class="alert-copy">
<text class="alert-title">本次生成失败</text>
<text class="alert-desc">{{detail.failureMessage}}</text>
</view>
</view>
<view class="info-card fail-info-card">
<view class="info-row"><text class="info-label">失败时间</text><text class="info-value">{{detail.failureDisplayTime}}</text></view>
<view class="info-row"><text class="info-label">数字人形象</text><view class="value-with-thumb"><image class="mini-avatar icon-placeholder" src="{{detail.avatarImage}}" mode="aspectFit"></image><text>{{detail.avatar.name}}</text></view></view>
<view class="info-row"><text class="info-label">背景模板</text><view class="value-with-thumb"><image class="mini-scene living-scene icon-placeholder" src="{{detail.backgroundImage}}" mode="aspectFit"></image><text>{{detail.background.name}}</text></view></view>
<view class="info-row"><text class="info-label">音色状态</text><view class="voice-error"><text class="error-dot">未</text><text>{{detail.voice.name}}</text></view></view>
<view class="info-row reason-row"><text class="info-label">失败原因</text><text class="reason-text">{{detail.failureMessage}}</text></view>
</view>
<view class="advice-card"><view class="advice-head"><image class="bulb-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>建议</text></view><text class="advice-text">建议检查数字人形象、音色和口播文案后重新生成。</text></view>
<view class="primary-action" bindtap="handleRetry">重新生成</view>
<view class="outline-action" bindtap="handleRecordVoice"><image class="voice-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>重新录取音色</text></view>
<view class="delete-link" bindtap="handleDelete">删除记录</view>
</block>
<block wx:elif="{{isProcessing}}">
<view class="processing-card">
<view class="processing-head">
<view class="ai-visual"><view class="ai-box">AI</view><view class="ai-base"></view></view>
<view class="processing-copy">
<text class="processing-title">AI 正在生成中</text>
<text class="processing-desc">请耐心等待,精彩内容即将呈现</text>
<view class="progress-row"><view class="progress-track"><view class="progress-fill" style="{{progressStyle}}"></view></view><text>{{detail.progress}}%</text></view>
</view>
</view>
<view class="stage-row">
<view wx:for="{{detail.steps}}" wx:key="code" class="stage-item {{item.status === 2 ? 'done' : item.status === 1 ? 'active' : ''}}"><view class="stage-dot {{item.status === 1 ? 'loading' : item.status === 0 ? 'empty' : ''}}"><text wx:if="{{item.status === 2}}"></text></view><text>{{item.name}}</text></view>
</view>
</view>
<view class="info-card processing-info-card">
<view class="info-row"><text class="info-label">任务ID</text><text class="info-value">{{detail.taskDisplayId}}</text></view>
<view class="info-row"><text class="info-label">数字人形象</text><view class="value-with-thumb"><image class="mini-avatar icon-placeholder" src="{{detail.avatarImage}}" mode="aspectFit"></image><text>{{detail.avatar.name}}</text></view></view>
<view class="info-row"><text class="info-label">背景模板</text><view class="value-with-thumb"><image class="mini-scene product-scene icon-placeholder" src="{{detail.backgroundImage}}" mode="aspectFit"></image><text>{{detail.background.name}}</text></view></view>
<view class="info-row"><text class="info-label">音色</text><view class="value-with-thumb"><image class="audio-thumb icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>{{detail.voice.name}}</text></view></view>
<view class="info-row"><text class="info-label">创建时间</text><view class="value-with-thumb"><image class="clock-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>{{detail.createTime}}</text></view></view>
</view>
<view class="primary-action" bindtap="handleBackToHistory">返回创作记录</view>
<view class="outline-action" bindtap="handleCancel">取消任务</view>
<view class="bottom-tip"><image class="bulb-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>生成完成后会自动保存到创作记录</text></view>
</block>
<block wx:else>
<view class="video-card scene-live"><image class="speaker-avatar icon-placeholder" src="{{detail.videoCover}}" mode="aspectFit"></image><image class="play-circle icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><view class="duration-badge">{{detail.duration}}</view></view>
<view class="info-card work-info-card">
<view class="info-row"><text class="info-label">创作状态</text><text class="status-done">{{detail.statusText}}</text></view>
<view class="info-row"><text class="info-label">生成时间</text><text class="info-value">{{detail.successTime}}</text></view>
<view class="info-row"><text class="info-label">数字人形象</text><view class="value-with-thumb"><image class="mini-avatar icon-placeholder" src="{{detail.avatarImage}}" mode="aspectFit"></image><text>{{detail.avatar.name}}</text></view></view>
<view class="info-row"><text class="info-label">背景模板</text><view class="value-with-thumb"><image class="mini-scene live-scene icon-placeholder" src="{{detail.backgroundImage}}" mode="aspectFit"></image><text>{{detail.background.name}}</text></view></view>
<view class="info-row"><text class="info-label">音色</text><view class="value-with-thumb"><image class="audio-thumb icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>{{detail.voice.name}}</text></view></view>
<view class="info-row copy-row"><text class="info-label">口播文案</text><view class="copy-content"><text>{{detail.scriptDisplayText}}</text><view class="view-copy" bindtap="handleViewCopy">复制文案 <text></text></view></view></view>
</view>
<view class="action-buttons"><view class="download-action" bindtap="handleDownload"><image class="download-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>下载视频</text></view><view class="outline-action small" bindtap="handleRetry"><image class="refresh-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>重新生成</text></view><view class="outline-action small" bindtap="handleEdit"><image class="edit-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>继续编辑</text></view></view>
<view class="related-card"><text class="related-title">相关操作</text><view class="related-item" bindtap="handleCopyText"><image class="copy-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>复制文案</text><text class="item-arrow"></text></view><view class="related-item" bindtap="handleSame"><image class="refresh-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>再次生成同款</text><text class="item-arrow"></text></view><view class="related-item danger" bindtap="handleDelete"><image class="trash-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image><text>删除记录</text><text class="item-arrow"></text></view></view>
</block>
</view>
</view>
@@ -0,0 +1,732 @@
page {
min-height: 100vh;
background: #fbfcff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.detail-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 58%, #ffffff 100%);
}
.detail-nav {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back,
.nav-placeholder {
width: 58rpx;
height: 58rpx;
display: flex;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
}
.nav-back text {
width: 24rpx;
height: 24rpx;
display: block;
border-left: 5rpx solid #111827;
border-bottom: 5rpx solid #111827;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title {
font-size: 31rpx;
font-weight: 900;
color: #111827;
}
.detail-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 18rpx;
padding: 24rpx 20rpx 34rpx;
box-sizing: border-box;
}
.video-card {
height: 332rpx;
border-radius: 14rpx;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background:
radial-gradient(circle at 12% 30%, rgba(111, 139, 78, 0.34), transparent 15%),
radial-gradient(circle at 88% 30%, rgba(111, 139, 78, 0.26), transparent 13%),
linear-gradient(90deg, #e7d9c7 0%, #faf2e8 52%, #d8c4a9 100%);
}
.speaker-avatar {
width: 150rpx;
height: 230rpx;
border-radius: 75rpx 75rpx 24rpx 24rpx;
background:
radial-gradient(circle at 38% 36%, #17131a 0 6rpx, transparent 7rpx),
radial-gradient(circle at 62% 36%, #17131a 0 6rpx, transparent 7rpx),
radial-gradient(circle at 50% 50%, #df7f87 0 8rpx, transparent 9rpx),
linear-gradient(180deg, #17131a 0 28%, #ffe0cd 28% 50%, #ffffff 50% 100%);
}
.play-circle {
width: 96rpx;
height: 96rpx;
margin-left: -126rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.38);
border: 4rpx solid rgba(255, 255, 255, 0.76);
}
.play-circle text {
width: 0;
height: 0;
border-top: 24rpx solid transparent;
border-bottom: 24rpx solid transparent;
border-left: 34rpx solid #ffffff;
margin-left: 8rpx;
}
.duration-badge {
align-self: flex-end;
margin-left: -310rpx;
margin-bottom: 18rpx;
height: 36rpx;
padding: 0 12rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
color: #ffffff;
font-size: 20rpx;
font-weight: 900;
background: rgba(0, 0, 0, 0.56);
}
.info-card,
.related-card,
.failure-alert,
.advice-card {
border-radius: 15rpx;
background: rgba(255, 255, 255, 0.96);
border: 1rpx solid #eef1fb;
box-shadow: 0 6rpx 18rpx rgba(82, 90, 146, 0.05);
box-sizing: border-box;
}
.info-card {
padding: 18rpx 24rpx;
display: flex;
flex-direction: column;
}
.info-row {
min-height: 54rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
border-bottom: 1rpx solid #eef0f6;
}
.info-row:last-child {
border-bottom: 0;
}
.info-label {
width: 150rpx;
flex-shrink: 0;
color: #596072;
font-size: 22rpx;
font-weight: 800;
}
.info-value,
.value-with-thumb {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 16rpx;
color: #33394b;
font-size: 22rpx;
font-weight: 700;
text-align: right;
}
.status-done {
color: #32c07a;
font-size: 22rpx;
font-weight: 900;
}
.mini-avatar {
width: 40rpx;
height: 40rpx;
border-radius: 10rpx;
background: linear-gradient(180deg, #1e1b20 0 35%, #ffd8c8 35% 62%, #ffffff 62% 100%);
}
.mini-scene {
width: 50rpx;
height: 34rpx;
border-radius: 5rpx;
}
.live-scene {
background: linear-gradient(135deg, #eadfcc, #ffffff 52%, #bbc99d);
}
.living-scene {
background: linear-gradient(135deg, #e0c6a6, #fff4e9 52%, #b28b68);
}
.audio-thumb {
width: 50rpx;
height: 34rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
background: #f1efff;
}
.audio-thumb text,
.voice-icon,
.refresh-icon,
.copy-icon,
.edit-icon,
.download-icon,
.trash-icon {
display: block;
color: currentColor;
}
.audio-thumb text {
width: 22rpx;
height: 24rpx;
border-left: 5rpx solid #665cff;
border-right: 5rpx solid #665cff;
box-sizing: border-box;
}
.copy-row {
align-items: flex-start;
padding: 14rpx 0;
}
.copy-content {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12rpx;
color: #33394b;
font-size: 21rpx;
line-height: 1.45;
text-align: right;
}
.view-copy {
display: flex;
align-items: center;
gap: 8rpx;
color: #665cff;
font-size: 21rpx;
font-weight: 900;
}
.view-copy text,
.item-arrow {
width: 14rpx;
height: 14rpx;
border-top: 4rpx solid currentColor;
border-right: 4rpx solid currentColor;
transform: rotate(45deg);
}
.action-buttons {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 22rpx;
}
.download-action,
.outline-action,
.primary-action {
height: 66rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
font-size: 23rpx;
font-weight: 900;
box-sizing: border-box;
}
.download-action,
.primary-action {
color: #ffffff;
background: linear-gradient(135deg, #4f85ff 0%, #7d42f0 100%);
box-shadow: 0 8rpx 18rpx rgba(91, 83, 244, 0.18);
}
.outline-action {
color: #665cff;
background: #ffffff;
border: 2rpx solid #dcdcff;
}
.download-icon {
width: 22rpx;
height: 24rpx;
border-bottom: 4rpx solid currentColor;
}
.refresh-icon {
width: 22rpx;
height: 22rpx;
border: 4rpx solid currentColor;
border-left-color: transparent;
border-radius: 50%;
box-sizing: border-box;
}
.edit-icon {
width: 22rpx;
height: 22rpx;
border-left: 5rpx solid currentColor;
border-bottom: 5rpx solid currentColor;
transform: rotate(-45deg);
box-sizing: border-box;
}
.related-card {
padding: 22rpx 24rpx;
}
.related-title {
display: block;
padding-bottom: 18rpx;
color: #121722;
font-size: 25rpx;
font-weight: 900;
border-bottom: 1rpx solid #edf0f6;
}
.related-item {
min-height: 58rpx;
display: flex;
align-items: center;
gap: 18rpx;
color: #4b5262;
font-size: 23rpx;
font-weight: 800;
border-bottom: 1rpx solid #edf0f6;
}
.related-item:last-child {
border-bottom: 0;
}
.related-item text:nth-child(2) {
flex: 1;
}
.related-item.danger {
color: #ff5d59;
}
.copy-icon {
width: 22rpx;
height: 22rpx;
border: 4rpx solid currentColor;
border-radius: 4rpx;
box-sizing: border-box;
}
.trash-icon {
width: 22rpx;
height: 24rpx;
border: 4rpx solid currentColor;
border-top: 0;
border-radius: 0 0 5rpx 5rpx;
box-sizing: border-box;
}
.failure-alert {
min-height: 146rpx;
padding: 28rpx 36rpx;
display: flex;
align-items: center;
gap: 30rpx;
background: #fff4f2;
border-color: #ffd2ce;
}
.alert-icon {
width: 80rpx;
height: 70rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 42rpx;
font-weight: 900;
background: #f05a52;
transform: perspective(120rpx) rotateX(8deg);
}
.alert-copy {
display: flex;
flex-direction: column;
gap: 14rpx;
}
.alert-title {
color: #e64038;
font-size: 29rpx;
font-weight: 900;
}
.alert-desc {
color: #7d5d5d;
font-size: 23rpx;
font-weight: 700;
}
.fail-info-card .info-row {
min-height: 66rpx;
}
.voice-error {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rpx;
color: #ef514c;
font-size: 24rpx;
font-weight: 900;
}
.error-dot {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 17rpx;
background: #ef514c;
}
.reason-row {
align-items: flex-start;
padding: 18rpx 0;
}
.reason-text {
flex: 1;
color: #505668;
font-size: 22rpx;
line-height: 1.45;
font-weight: 700;
}
.advice-card {
padding: 24rpx 28rpx;
display: flex;
flex-direction: column;
gap: 18rpx;
background: #fff9ea;
border-color: #f6d99e;
}
.advice-head {
display: flex;
align-items: center;
gap: 14rpx;
color: #4b2a0d;
font-size: 25rpx;
font-weight: 900;
}
.bulb-icon {
width: 34rpx;
height: 34rpx;
border: 4rpx solid #ad7b2b;
border-radius: 50%;
box-sizing: border-box;
}
.advice-text {
color: #5a6274;
font-size: 23rpx;
line-height: 1.65;
font-weight: 700;
}
.failed-page .primary-action,
.failed-page .outline-action {
height: 84rpx;
border-radius: 13rpx;
font-size: 28rpx;
}
.failed-page .outline-action {
border-color: #dcdcff;
}
.voice-icon {
width: 24rpx;
height: 28rpx;
border-left: 5rpx solid currentColor;
border-right: 5rpx solid currentColor;
box-sizing: border-box;
}
.delete-link {
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
color: #f05a52;
font-size: 28rpx;
font-weight: 800;
}
.processing-page .detail-content {
gap: 26rpx;
padding-left: 32rpx;
padding-right: 32rpx;
}
.processing-card {
border-radius: 17rpx;
overflow: hidden;
display: flex;
flex-direction: column;
background: #ffffff;
border: 1rpx solid #e6e9fb;
box-shadow: 0 8rpx 22rpx rgba(82, 90, 146, 0.06);
}
.processing-head {
min-height: 226rpx;
padding: 28rpx 28rpx 26rpx;
display: flex;
align-items: center;
gap: 30rpx;
background: linear-gradient(100deg, #f4f6ff, #ffffff);
box-sizing: border-box;
}
.ai-visual {
width: 178rpx;
height: 158rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.ai-box {
width: 88rpx;
height: 88rpx;
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 28rpx;
font-weight: 900;
background: linear-gradient(135deg, #7d74ff, #3c93f4);
box-shadow: 22rpx 10rpx 0 rgba(86, 204, 255, 0.26), 0 14rpx 26rpx rgba(77, 83, 234, 0.24);
}
.ai-base {
width: 150rpx;
height: 42rpx;
margin-top: -2rpx;
border-radius: 50%;
background: linear-gradient(90deg, #6d55f3, #ffffff 50%, #8a78ff);
box-shadow: 0 10rpx 18rpx rgba(96, 116, 255, 0.18);
}
.processing-copy {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.processing-title {
color: #101522;
font-size: 34rpx;
line-height: 1.2;
font-weight: 900;
}
.processing-desc {
margin-top: 16rpx;
color: #6d7385;
font-size: 24rpx;
font-weight: 700;
}
.progress-row {
margin-top: 34rpx;
display: flex;
align-items: center;
gap: 16rpx;
color: #4a4e63;
font-size: 24rpx;
font-weight: 800;
}
.progress-track {
flex: 1;
height: 16rpx;
border-radius: 999rpx;
background: #e5e6fb;
overflow: hidden;
}
.progress-fill {
width: 78%;
height: 100%;
border-radius: 999rpx;
background: linear-gradient(90deg, #4d82ff, #7d42f0);
}
.stage-row {
min-height: 150rpx;
padding: 28rpx 28rpx 22rpx;
display: flex;
align-items: flex-start;
justify-content: space-between;
border-top: 1rpx solid #edf0f7;
box-sizing: border-box;
}
.stage-item {
width: 104rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 16rpx;
color: #575e70;
font-size: 21rpx;
font-weight: 800;
text-align: center;
}
.stage-dot {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 4rpx solid #d5d4ff;
box-sizing: border-box;
}
.stage-item.done .stage-dot {
background: #5d6dff;
border-color: #5d6dff;
}
.stage-dot text {
width: 17rpx;
height: 9rpx;
border-left: 4rpx solid #ffffff;
border-bottom: 4rpx solid #ffffff;
transform: rotate(-45deg);
}
.stage-dot.loading {
border-color: #6d5fff;
border-top-color: rgba(109, 95, 255, 0.16);
}
.stage-line {
width: 78rpx;
height: 4rpx;
margin-top: 20rpx;
border-radius: 999rpx;
background: #d8d8ff;
}
.stage-line.dashed {
background: repeating-linear-gradient(90deg, #d8d8ff 0 10rpx, transparent 10rpx 18rpx);
}
.processing-info-card .info-row {
min-height: 74rpx;
}
.product-scene {
background: linear-gradient(135deg, #eadfcc, #fff8ef 52%, #c8b089);
}
.clock-icon {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 4rpx solid #665cff;
box-sizing: border-box;
}
.processing-page .primary-action,
.processing-page .outline-action {
height: 84rpx;
border-radius: 13rpx;
font-size: 29rpx;
}
.bottom-tip {
height: 72rpx;
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 14rpx;
color: #6d69c9;
font-size: 24rpx;
font-weight: 800;
background: #f4f5ff;
border: 1rpx solid #e2e5ff;
}
.bottom-tip .bulb-icon {
border-color: #75a4ff;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}