This commit is contained in:
2026-07-10 16:14:31 +08:00
commit 637b0bd144
158 changed files with 33269 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
{
"pages": [
"pages/index/index",
"pages/myPackage/yjfk/index",
"pages/my/my",
"pages/history/history",
"pages/myPackage/edit/index",
"pages/myPackage/vip/index",
"pages/myPackage/bzzx/index",
"pages/myPackage/ysxy/index",
"pages/myPackage/golb/index",
"pages/historyPackage/historyDetail/index",
"pages/aiPackage/preview/index",
"pages/aiPackage/record/index",
"pages/aiPackage/bg/index",
"pages/aiPackage/gen/index"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff"
},
"style": "v2",
"componentFramework": "glass-easel",
"lazyCodeLoading": "requiredComponents",
"tabBar": {
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/history/history",
"text": "创作记录"
},
{
"pagePath": "pages/my/my",
"text": "我的"
}
]
}
}
+14
View File
@@ -0,0 +1,14 @@
import { customEvent } from "./utils/event";
import { event } from "./utils/util";
// app.ts
App<IAppOption>({
globalData: {},
onLaunch(op) {
// 展示本地存储能力
wx.setStorageSync('scene', +op.scene);
customEvent.on('update:user:info', event.user.update);
customEvent.emmit('update:user:info', null);
}
})
+19
View File
@@ -0,0 +1,19 @@
/**app.wxss**/
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
image.icon-placeholder {
display: block !important;
flex-shrink: 0 !important;
background: none !important;
border: 0 !important;
box-shadow: none !important;
transform: none !important;
}
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,65 @@
import { resetuserinfocache, sign, userInfo } from "../../utils/request"
Component({
properties: {
visible: {
type: Boolean,
value: false,
},
reward: {
type: Number,
value: 30,
},
streakDays: {
type: Number,
value: 7,
},
},
data: {
days: [
{ day: 1, label: '1天', today: false },
{ day: 2, label: '2天', today: false },
{ day: 3, label: '3天', today: false },
{ day: 4, label: '4天', today: false },
{ day: 5, label: '5天', today: false },
{ day: 6, label: '6天', today: false },
{ day: 7, label: '今天', today: true },
],
userInfo: null as any,
},
lifetimes: {
attached() {
this.setData({ userInfo: wx.getStorageSync('userInfo') })
},
detached() {
}
},
methods: {
noop() { },
handleOverlayTap() {
this.handleClose()
},
handleClose() {
this.triggerEvent('close')
},
sign() {
if (this.data.userInfo.is_sign) {
return;
}
sign().then((res: any) => {
if (!res.code) {
this.triggerEvent('close')
resetuserinfocache(this);
} else {
wx.showModal({ title: '温馨提示', content: res.message });
}
})
},
handleRule() {
this.triggerEvent('rule')
},
},
})
@@ -0,0 +1,19 @@
<view wx:if="{{visible}}" class="checkin-overlay" catchtap="handleOverlayTap">
<view class="checkin-popup" catchtap="noop">
<image class="popup-bg" src="/static/images/sign_bg.png" mode="widthFix"></image>
<view class="days-row">
<view wx:for="{{days}}" wx:key="day" class="day-item {{item.today ? 'today' : ''}}">
<text class="day-label">{{item.label}}</text>
<image class="day-coin" src="/static/images/sign_glob_icon.png" mode="aspectFit"></image>
<view class="day-check">
<text></text>
</view>
</view>
</view>
<view class="close-hotspot" bindtap="handleClose"></view>
<view class="receive-hotspot {{userInfo.is_sign ? 'disabled': ''}}" bindtap="sign"></view>
<view class="rule-hotspot" bindtap="handleRule"></view>
</view>
</view>
@@ -0,0 +1,129 @@
.checkin-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx 0;
background: rgba(0, 0, 0, 0.5);
box-sizing: border-box;
}
.disabled {
background-color: grey;
}
.checkin-popup {
position: relative;
width: 670rpx;
overflow: hidden;
border-radius: 32rpx;
}
.popup-bg {
width: 100%;
display: block;
}
.days-row {
position: absolute;
top: 700rpx;
left: 70rpx;
right: 56rpx;
height: 140rpx;
display: flex;
align-items: flex-start;
justify-content: space-between;
box-sizing: border-box;
}
.day-item {
position: relative;
width: 58rpx;
height: 132rpx;
display: flex;
flex-direction: column;
align-items: center;
color: #4b4f5d;
box-sizing: border-box;
}
.day-item.today {
width: 72rpx;
height: 136rpx;
margin-top: -16rpx;
padding-top: 16rpx;
border-radius: 30rpx;
color: #7a4cff;
background: #e4cdfd;
}
.day-label {
height: 24rpx;
line-height: 24rpx;
font-size: 19rpx;
font-weight: 600;
white-space: nowrap;
}
.day-coin {
width: 50rpx;
height: 50rpx;
margin-top: 17rpx;
display: block;
}
.day-check {
width: 24rpx;
height: 24rpx;
margin-top: -8rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: #754cff;
box-shadow: 0 2rpx 6rpx rgba(117, 76, 255, 0.24);
}
.day-check text {
width: 10rpx;
height: 6rpx;
border-left: 3rpx solid #ffffff;
border-bottom: 3rpx solid #ffffff;
transform: rotate(-45deg) translate(1rpx, -1rpx);
box-sizing: border-box;
}
.close-hotspot,
.receive-hotspot,
.rule-hotspot {
position: absolute;
z-index: 2;
}
.close-hotspot {
top: 32rpx;
right: 28rpx;
width: 84rpx;
height: 84rpx;
border-radius: 50%;
}
.receive-hotspot {
left: 44rpx;
right: 44rpx;
bottom: 100rpx;
height: 104rpx;
border-radius: 52rpx;
}
.rule-hotspot {
left: 190rpx;
right: 190rpx;
bottom: 22rpx;
height: 62rpx;
}
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,48 @@
Component({
options: {
multipleSlots: true,
},
properties: {
contentHeight: {
type: Number,
value: 44,
},
paddingX: {
type: Number,
value: 0,
},
background: {
type: String,
value: 'transparent',
},
},
data: {
statusBarHeight: 0,
navHeight: 44,
},
lifetimes: {
attached() {
this.updateNavMetrics()
},
},
methods: {
updateNavMetrics() {
const systemInfo = wx.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 0
const contentHeight = Number(this.data.contentHeight) || 44
const navHeight = statusBarHeight + contentHeight
this.setData({
statusBarHeight,
navHeight,
})
this.triggerEvent('ready', {
statusBarHeight,
contentHeight,
navHeight,
})
},
},
})
@@ -0,0 +1,8 @@
<view
class="custom-nav"
style="height: {{navHeight}}px; padding-top: {{statusBarHeight}}px; padding-left: {{paddingX}}rpx; padding-right: {{paddingX}}rpx; background: {{background}};"
>
<view class="custom-nav__content" style="height: {{contentHeight}}px;">
<slot></slot>
</view>
</view>
@@ -0,0 +1,15 @@
.custom-nav {
width: 100%;
flex: 0 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.custom-nav__content {
width: 100%;
flex: 1;
display: flex;
align-items: center;
box-sizing: border-box;
}
@@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}
@@ -0,0 +1,169 @@
import { updatesessionkey, userInfo, vips, vipVirtualPayment } from "../../utils/request"
interface VipPlan {
id: number
name: string
price: number
desc: string
recommend?: boolean
selected: boolean
}
Component({
properties: {
visible: {
type: Boolean,
value: false,
},
},
data: {
benefits: [
{ title: '更低生成成本', desc: '享受更多优惠', iconClass: 'cost-icon' },
{ title: '专属会员标识', desc: '尊享身份标识', iconClass: 'badge-icon' },
{ title: '优先体验新功能', desc: '抢先体验特权', iconClass: 'flash-icon' },
],
plans: [
{ id: 1, name: '月卡会员', price: 25, desc: '¥25/月', selected: false },
{ id: 2, name: '半年卡会员', price: 98, desc: '¥16.3/月', selected: false },
{ id: 3, name: '年卡会员', price: 168, desc: '¥14/月', recommend: true, selected: true },
{ id: 4, name: '终身会员', price: 298, desc: '一次付费\n永久使用', selected: false },
] as VipPlan[],
selectedPackage: null as VipPlan | null
},
lifetimes: {
attached() {
vips().then((res: any) => {
if (!res.code) {
let array = [];
for (let q = 0; q < res.data.length; q++) {
let item = res.data[q];
item.price = (+item.price).toFixed(0);
array.push(item);
}
this.setData({ plans: array });
}
})
},
detached() {
}
},
attached() {
vips().then((res: any) => {
if (!res.code) {
let array = [];
for (let q = 0; q < res.data.length; q++) {
let item = res.data[q];
item.price = (+item.price).toFixed(0);
array.push(item);
}
this.setData({ plans: array });
}
})
},
detached() {
},
pageLifetimes: {
},
methods: {
noop() { },
handleOverlayTap() {
this.handleClose()
},
handleClose() {
this.triggerEvent('close')
},
handlePlanTap(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id
this.data.plans.forEach((value, i) => {
if (id == value.id) {
console.log(value);
this.setData({
selectedPackage: value,
})
}
})
},
handleConfirm() {
this.handleBuy()
},
handleBuy() {
const that = this;
if (that.data.selectedPackage == null) {
return;
}
wx.showLoading({ title: '处理中...', mask: true });
wx.checkSession({
success() {
that.payment();
},
fail() {
wx.login({
success(login) {
updatesessionkey(login.code).then((res: any) => {
if (!res.code) {
that.payment();
} else {
wx.showModal({
title: '温馨提示',
content: res.message,
showCancel: false
})
}
})
}
})
wx.hideLoading();
}
})
},
payment() {
let that = this;
vipVirtualPayment((that.data.selectedPackage || { id: 0 }).id).then((res: any) => {
wx.requestVirtualPayment({
signData: JSON.stringify(res.param.signData),
paySig: res.param.paySig,
signature: res.param.signature,
mode: res.param.mode,
success(result: any) {
wx.hideLoading();
console.log(result)
userInfo().then((res: any) => {
if (!res.code) {
wx.setStorageSync('userInfo', res.data);
that.setData({ userInfo: res.data });
}
})
},
fail(error: any) {
wx.hideLoading();
console.log(error);
wx.showModal({
title: '温馨提示',
content: '支付失败',
showCancel: false
})
}
});
}).catch(() => {
wx.hideLoading();
wx.showModal({
title: '温馨提示',
content: '系统繁忙, 请稍后再试',
showCancel: false
})
})
}
},
})
@@ -0,0 +1,64 @@
<view wx:if="{{visible}}" class="vip-overlay" catchtap="handleOverlayTap">
<view class="vip-popup" catchtap="noop">
<image class="vip-bg" src="/static/images/my/vip_bg.png" mode="widthFix"></image>
<view class="close-hotspot" bindtap="handleClose"></view>
<view class="vip-content">
<view class="gift-list">
<view class="gift-copy">
<text>立即赠送</text>
<text class="highlight">300</text>
<text>金币</text>
</view>
<view class="gift-copy daily-copy">
<text>平台每天赠送</text>
<text class="highlight">30</text>
<text>金币</text>
<text class="orange-note">(仅当天有效)</text>
</view>
</view>
<view class="plan-section">
<scroll-view class="plan-scroll" scroll-x enhanced show-scrollbar="false">
<view class="plan-track">
<view
wx:for="{{plans}}"
wx:key="id"
class="plan-card {{(selectedPackage && item.id == selectedPackage.id) || (!selectedPackage && item.selected) ? 'selected' : ''}}"
data-id="{{item.id}}"
bindtap="handlePlanTap"
>
<image wx:if="{{(selectedPackage && item.id == selectedPackage.id) || (!selectedPackage && item.selected)}}" class="plan-selected-bg" src="/static/images/my/vip_selected.png" mode="scaleToFill"></image>
<view wx:if="{{item.recommend}}" class="recommend-ribbon">推荐</view>
<text class="plan-name">{{item.title}}</text>
<view class="price-row">
<text class="currency">¥</text>
<text class="price">{{item.price}}</text>
</view>
<text class="plan-desc">{{item.price}}/月</text>
</view>
</view>
</scroll-view>
<view class="dots">
<text
wx:for="{{plans}}"
wx:key="id"
class="dot {{(selectedPackage && item.id == selectedPackage.id) || (!selectedPackage && item.selected) ? 'active' : ''}}"
></text>
</view>
</view>
<view class="action-section">
<image class="vip-btn" src="/static/images/my/vip_btn.png" mode="widthFix" bindtap="handleConfirm"></image>
<view class="skip-btn" bindtap="handleClose">暂不需要</view>
</view>
</view>
</view>
</view>
@@ -0,0 +1,228 @@
.vip-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.58);
box-sizing: border-box;
}
.vip-popup {
position: relative;
width: 706rpx;
height: 1130rpx;
max-height: 92vh;
overflow: visible;
background: transparent;
box-sizing: border-box;
}
.vip-bg {
position: absolute;
top: 0;
left: 0;
z-index: 0;
width: 706rpx;
height: auto;
display: block;
}
.close-hotspot {
position: absolute;
top: 112rpx;
right: 24rpx;
z-index: 5;
width: 72rpx;
height: 72rpx;
}
.vip-content {
position: relative;
z-index: 2;
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-top: 284rpx;
}
.gift-list {
display: flex;
flex-direction: column;
gap: 68rpx;
padding-left: 208rpx;
box-sizing: border-box;
}
.gift-copy {
display: flex;
align-items: baseline;
gap: 9rpx;
height: 58rpx;
color: #321708;
font-size: 27rpx;
font-weight: 900;
white-space: nowrap;
}
.highlight {
color: #f18a0a;
font-size: 42rpx;
font-style: italic;
}
.orange-note {
color: #f18400;
font-size: 24rpx;
}
.plan-section {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 220rpx;
}
.plan-scroll {
width: 706rpx;
height: 218rpx;
white-space: nowrap;
}
.plan-track {
height: 218rpx;
padding: 0 52rpx;
display: flex;
gap: 14rpx;
box-sizing: border-box;
}
.plan-card {
position: relative;
width: 142rpx;
height: 214rpx;
border-radius: 14rpx;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-shrink: 0;
overflow: hidden;
border: 1rpx solid #e7dcf2;
background: linear-gradient(180deg, #fffefe, #fbf9ff);
box-sizing: border-box;
}
.plan-card.selected {
border: 0;
background: transparent;
}
.plan-selected-bg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 0;
width: 100%;
height: 100%;
display: block;
}
.recommend-ribbon {
display: none;
}
.plan-name,
.price-row,
.plan-desc {
position: relative;
z-index: 1;
}
.plan-name {
color: #25170d;
font-size: 23rpx;
font-weight: 900;
}
.price-row {
margin-top: 20rpx;
display: flex;
align-items: baseline;
color: #7047ee;
}
.selected .price-row {
color: #f08d09;
}
.currency {
font-size: 24rpx;
font-weight: 900;
}
.price {
font-size: 48rpx;
line-height: 1;
font-weight: 900;
}
.plan-desc {
margin-top: 18rpx;
color: #7f7f86;
font-size: 20rpx;
line-height: 1.25;
text-align: center;
white-space: pre-line;
}
.dots {
display: flex;
align-items: center;
justify-content: center;
gap: 14rpx;
margin-top: 22rpx;
}
.dot {
width: 12rpx;
height: 12rpx;
border-radius: 999rpx;
background: #dedee2;
}
.dot.active {
width: 36rpx;
background: #764ef7;
}
.action-section {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 24rpx;
}
.vip-btn {
width: 590rpx;
height: auto;
display: block;
}
.skip-btn {
margin-top: 20rpx;
color: #8e8e94;
font-size: 24rpx;
font-weight: 800;
text-align: center;
}
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+103
View File
@@ -0,0 +1,103 @@
import { backgroundTemplates, updateTaskBackground } from "../../../utils/request"
type Category = {
id: string
name: string
}
type BgTemplate = {
id: number
name: string
scene: string
category: string
coverUrl?: string
imageUrl?: string
videoUrl?: string
isRecommend?: number
}
function filterTemplates(category: string, templates: BgTemplate[]) {
if (category === 'all') return templates
return templates.filter((item) => item.category === category)
}
Page({
data: {
taskId: '',
categories: [] as Category[],
activeCategory: 'all',
selectedTemplate: 0,
allTemplates: [] as BgTemplate[],
templates: [] as BgTemplate[],
loading: false,
},
onLoad(options: any) {
this.setData({ taskId: options.taskId || options.taskid || '' })
this.loadBackgroundTemplates()
},
loadBackgroundTemplates() {
this.setData({ loading: true })
backgroundTemplates().then((res: any) => {
const categories = res && !res.code && res.data ? (res.data.categories || []) : []
const templates = res && !res.code && res.data ? (res.data.templates || []) : []
const selectedTemplate = templates.length ? Number(templates[0].id) : 0
this.setData({
categories,
allTemplates: templates,
templates: filterTemplates(this.data.activeCategory, templates),
selectedTemplate,
loading: false,
})
}).catch(() => {
this.setData({ loading: false })
wx.showModal({ title: '温馨提示', content: '背景模板加载失败,请稍后再试', showCancel: false })
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.navigateTo({ url: '/pages/aiPackage/record/index' }),
})
},
handleCategorySelect(e: WechatMiniprogram.TouchEvent) {
const activeCategory = e.currentTarget.dataset.id
if (!activeCategory) return
const templates = filterTemplates(activeCategory, this.data.allTemplates)
this.setData({ activeCategory, templates })
},
handleTemplateSelect(e: WechatMiniprogram.TouchEvent) {
const selectedTemplate = Number(e.currentTarget.dataset.id || 0)
if (selectedTemplate <= 0) return
this.setData({ selectedTemplate })
},
handleConfirm() {
if (!this.data.taskId) {
wx.showModal({ title: '温馨提示', content: '创作记录不存在', showCancel: false })
return
}
if (this.data.selectedTemplate <= 0) {
wx.showModal({ title: '温馨提示', content: '请选择背景模板', showCancel: false })
return
}
wx.showLoading({ mask: true, title: '保存背景中' })
updateTaskBackground(this.data.taskId, this.data.selectedTemplate).then((res: any) => {
wx.hideLoading()
if (!res.code) {
wx.redirectTo({ url: '/pages/aiPackage/gen/index?taskId=' + encodeURIComponent(this.data.taskId) })
} else {
wx.showModal({ title: '温馨提示', content: res.message || '背景保存失败', showCancel: false })
}
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
})
+44
View File
@@ -0,0 +1,44 @@
<view class="bg-page">
<custom-nav padding-x="34">
<view class="bg-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<view class="nav-title-wrap">
<text class="nav-title">选择背景模板</text>
<text class="nav-subtitle">选择适合你视频内容的场景背景</text>
</view>
<view class="nav-space"></view>
</view>
</custom-nav>
<view class="bg-content">
<scroll-view class="category-scroll" scroll-x="true" enhanced="true" show-scrollbar="false">
<view class="category-list">
<view wx:for="{{categories}}" wx:key="id"
class="category-item {{activeCategory === item.id ? 'category-active' : ''}}" data-id="{{item.id}}"
bindtap="handleCategorySelect">{{item.name}}</view>
</view>
</scroll-view>
<view class="template-grid">
<scroll-view class="auto-flex" enable-flex="true" scroll-y="true">
<view wx:for="{{templates}}" wx:key="id"
class="template-card {{selectedTemplate === item.id ? 'template-active' : ''}}" data-id="{{item.id}}"
bindtap="handleTemplateSelect">
<view class="scene-preview scene-{{item.scene}}">
<image class="scene-placeholder icon-placeholder" src="/static/images/my/image.png" mode="aspectFit">
</image>
<view class="select-circle top-circle {{selectedTemplate === item.id ? 'circle-active' : ''}}"><text></text>
</view>
</view>
<view class="template-foot">
<text>{{item.name}}</text>
<!-- <view class="select-circle foot-circle {{selectedTemplate === item.id ? 'circle-active' : ''}}">
<text></text></view> -->
</view>
</view>
</scroll-view>
</view>
<view class="confirm-action" bindtap="handleConfirm">确认背景,去生成</view>
</view>
</view>
+367
View File
@@ -0,0 +1,367 @@
page {
min-height: 100vh;
background: #f8faff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.bg-page {
width: 750rpx;
height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 12% 0%, rgba(104, 126, 255, 0.08), transparent 34%),
radial-gradient(circle at 88% 10%, rgba(130, 86, 255, 0.08), transparent 30%),
linear-gradient(180deg, #fbfcff 0%, #f6f8ff 44%, #ffffff 100%);
}
.bg-nav {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back,
.nav-space {
width: 60rpx;
height: 60rpx;
flex: 0 0 60rpx;
display: flex;
align-items: center;
}
.nav-back text {
width: 26rpx;
height: 26rpx;
display: block;
border-left: 5rpx solid #111827;
border-bottom: 5rpx solid #111827;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title-wrap {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10rpx;
}
.nav-title {
color: #111827;
font-size: 34rpx;
line-height: 40rpx;
font-weight: 900;
}
.nav-subtitle {
max-width: 560rpx;
color: #6c7284;
font-size: 22rpx;
line-height: 28rpx;
font-weight: 600;
text-align: center;
white-space: nowrap;
}
.bg-content {
flex: 1;
display: flex;
overflow-y: hidden;
flex-direction: column;
padding: 20rpx 18rpx 34rpx;
box-sizing: border-box;
}
.category-scroll {
width: 100%;
flex: 0 0 auto;
white-space: nowrap;
}
.auto-flex {
height: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.category-list {
display: flex;
align-items: center;
gap: 10rpx;
padding: 2rpx 0 26rpx;
}
.category-item {
height: 46rpx;
padding: 0 18rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid #e0e4f2;
border-radius: 24rpx;
background: #ffffff;
color: #656d80;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 800;
box-sizing: border-box;
white-space: nowrap;
flex: 0 0 auto;
}
.category-active {
border-color: #7462ff;
color: #ffffff;
background: linear-gradient(100deg, #6b83ff 0%, #754df3 100%);
box-shadow: 0 8rpx 18rpx rgba(101, 85, 246, 0.2);
}
.template-grid {
display: flex;
flex-wrap: wrap;
flex: 1;
overflow-y: auto;
justify-content: space-between;
row-gap: 30rpx;
padding: 0 0 34rpx;
box-sizing: border-box;
}
.template-card {
width: 326rpx;
display: flex;
flex-direction: column;
border: 2rpx solid transparent;
border-radius: 14rpx;
margin-bottom: 20rpx;
background: #ffffff;
box-shadow: 0 12rpx 30rpx rgba(83, 92, 151, 0.12);
box-sizing: border-box;
overflow: hidden;
}
.template-active {
border-color: #765eff;
box-shadow: 0 12rpx 30rpx rgba(101, 85, 246, 0.22);
}
.scene-preview {
height: 166rpx;
padding: 12rpx 14rpx;
display: flex;
flex-wrap: wrap;
align-content: space-between;
justify-content: space-between;
box-sizing: border-box;
overflow: hidden;
}
.scene-window,
.scene-light,
.scene-plant,
.scene-main {
flex: 0 0 auto;
}
.scene-window {
width: 96rpx;
height: 56rpx;
border-radius: 4rpx;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.88) 0%, rgba(225, 238, 250, 0.72) 100%);
box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.5);
}
.scene-light {
width: 54rpx;
height: 66rpx;
border-radius: 28rpx 28rpx 10rpx 10rpx;
background: linear-gradient(180deg, rgba(255, 246, 210, 0.9), rgba(226, 170, 92, 0.35));
}
.scene-plant {
width: 44rpx;
height: 62rpx;
border-radius: 28rpx 28rpx 8rpx 8rpx;
background:
radial-gradient(circle at 30% 28%, #7db66d 0 10rpx, transparent 11rpx),
radial-gradient(circle at 68% 36%, #679f5d 0 12rpx, transparent 13rpx),
linear-gradient(180deg, transparent 0 42rpx, #b48354 43rpx 100%);
}
.plant-right {
transform: scale(0.82);
}
.scene-main {
width: 160rpx;
height: 54rpx;
border-radius: 10rpx 10rpx 4rpx 4rpx;
background: rgba(255, 255, 255, 0.72);
box-shadow: inset 0 -8rpx 0 rgba(188, 158, 120, 0.18);
}
.scene-live {
background:
linear-gradient(90deg, rgba(255, 255, 255, 0.28) 0 2rpx, transparent 2rpx 100%),
linear-gradient(180deg, #fff4df 0%, #f9ead4 48%, #dfc09a 100%);
}
.scene-office {
background:
linear-gradient(90deg, rgba(150, 168, 184, 0.16) 0 2rpx, transparent 2rpx 64rpx),
linear-gradient(180deg, #eff6fb 0%, #dfe7ec 52%, #c8d2d9 100%);
}
.scene-office .scene-main {
width: 178rpx;
background: linear-gradient(180deg, #f8fbfd 0%, #cbd7df 100%);
}
.scene-classroom {
background: linear-gradient(180deg, #fff4df 0%, #f4dfbf 52%, #d6b98c 100%);
}
.scene-classroom .scene-main {
width: 184rpx;
height: 58rpx;
background:
linear-gradient(180deg, #4d7555 0%, #315b3b 100%);
box-shadow: 0 18rpx 0 rgba(140, 98, 56, 0.32);
}
.scene-emotion {
background: linear-gradient(180deg, #ffe2be 0%, #e5b379 52%, #b57945 100%);
}
.scene-emotion .scene-main,
.scene-life .scene-main {
width: 178rpx;
height: 52rpx;
border-radius: 30rpx 30rpx 12rpx 12rpx;
background: linear-gradient(180deg, #f5dfc9 0%, #d1a78a 100%);
}
.scene-product {
background: linear-gradient(180deg, #f8dbb5 0%, #dfb076 55%, #b9844f 100%);
}
.scene-product .scene-main {
width: 174rpx;
height: 58rpx;
border-radius: 50%;
background: radial-gradient(circle at 50% 34%, #fff1d7 0%, #c8965e 66%, #9e6e3c 100%);
}
.scene-life {
background: linear-gradient(180deg, #fff6e9 0%, #ead8bd 54%, #c9ab81 100%);
}
.template-foot {
height: 62rpx;
padding: 0 18rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12rpx;
box-sizing: border-box;
}
.template-foot text {
flex: 1;
min-width: 0;
color: #222633;
font-size: 25rpx;
line-height: 32rpx;
font-weight: 900;
white-space: nowrap;
}
.template-active .template-foot text {
color: #6559ff;
}
.select-circle {
width: 34rpx;
height: 34rpx;
display: flex;
align-items: center;
justify-content: center;
border: 3rpx solid #d4d8e8;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
box-sizing: border-box;
flex: 0 0 34rpx;
}
.top-circle {
margin-left: auto;
}
.foot-circle {
width: 32rpx;
height: 32rpx;
}
.circle-active {
border-color: #7363ff;
background: linear-gradient(135deg, #7089ff 0%, #7a4df4 100%);
}
.circle-active text {
width: 14rpx;
height: 8rpx;
display: block;
border-left: 4rpx solid #ffffff;
border-bottom: 4rpx solid #ffffff;
transform: rotate(-45deg);
box-sizing: border-box;
}
.confirm-action {
height: 86rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
color: #ffffff;
background: linear-gradient(100deg, #4e84ff 0%, #7d3ff3 100%);
box-shadow: 0 14rpx 28rpx rgba(91, 86, 245, 0.24);
font-size: 29rpx;
line-height: 36rpx;
font-weight: 900;
}
.scene-placeholder {
width: 100%;
height: 100%;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
.scene-preview {
position: relative;
padding: 0;
background: #ffffff;
}
.top-circle {
position: absolute;
top: 12rpx;
right: 14rpx;
}
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+74
View File
@@ -0,0 +1,74 @@
import { taskinfo } from "../../../utils/request"
function createTaskId() {
return `task_${Date.now()}`
}
Page({
data: {
taskinfo: null as any,
userinfo: null,
price: 40,
start: false
},
onLoad(ops: { taskId: string }) {
this.setData({ userinfo: wx.getStorageSync('userInfo') })
taskinfo(ops.taskId).then((res: any) => {
console.log(res);
if (!res.code) {
this.setData({ taskinfo: res.data });
}
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.navigateTo({ url: '/pages/aiPackage/bg/index' }),
})
},
handleViewScript() {
wx.showToast({ title: '展示完整文案', icon: 'none' })
},
handleGenerate() {
if (!this.data.taskinfo) {
return;
}
let task_no = this.data.taskinfo.task_no || '';
const detailUrl = `/pages/historyPackage/historyDetail/index?status=processing&taskId=${encodeURIComponent(task_no)}`
let userinfo = wx.getStorageSync('userinfo');
if (userinfo.diamond < this.data.price) {
wx.showModal({ title: '温馨提示', content: '余额不足' })
return;
}
this.setData({ start: true })
// wx.redirectTo({ url: detailUrl })
},
handleDownload() {
// wx.showToast({ title: '开始下载', icon: 'none' })
wx.downloadFile({
url: '',
success(res) {
wx.showModal({ title: '温馨提示', content: '下载成功' })
}
})
},
handleRegenerate() {
// wx.showToast({ title: '重新生成', icon: 'none' })
},
handleHome() {
wx.switchTab({
url: '/pages/index/index',
})
},
})
+105
View File
@@ -0,0 +1,105 @@
<view class="gen-page">
<custom-nav content-height="62" padding-x="34">
<view class="gen-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<text class="nav-title">生成视频</text>
<view class="coin-pill">
<!-- <image class="coin-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>金币</text>
<text class="coin-count">128</text> -->
</view>
</view>
</custom-nav>
<view class="gen-content">
<view class="confirm-card">
<text class="card-title">生成确认</text>
<view class="info-list">
<view class="info-row avatar-row">
<text class="info-label">数字人形象</text>
<image class="small-avatar icon-placeholder" src="{{ taskinfo.avatar.image_url }}" mode="aspectFit"></image>
</view>
<view class="info-row">
<text class="info-label">背景模板</text>
<text class="info-value">清新直播间</text>
</view>
<view class="info-row">
<text class="info-label">音色</text>
<view class="voice-value">
<image class="voice-icon icon-placeholder" src="/static/images/yinse.png" mode="aspectFit"></image>
<text>已上传专属音色</text>
</view>
</view>
<view class="info-row script-row">
<text class="info-label">文案摘要</text>
<view class="script-summary">
<text>{{ taskinfo.script_text }}</text>
<view class="view-script" bindtap="handleViewScript">
<text>查看完整文案</text>
<text class="right-arrow"></text>
</view>
</view>
</view>
</view>
</view>
<view class="cost-card" wx:if="{{!start}}">
<view class="cost-row">
<text>本次生成消耗</text>
<view class="amount"><text>{{price}}</text><text>金币</text></view>
</view>
<view class="cost-row">
<text>当前余额</text>
<view class="balance-line">
<view class="amount"><text>{{userinfo.diamond}}</text><text>金币</text></view>
<view class="{{ userinfo.diamond < price ?'enough-waring': 'enough'}}">
<text></text><text>{{userinfo.diamond < price? '余额不足' : '余额充足'}}</text>
</view>
</view>
</view>
<view class="generate-action" bindtap="handleGenerate">
<image class="sparkle icon-placeholder" src="/static/images/filter-3-filled.png" mode="aspectFit"></image>
<text>一键生成数字人视频</text>
</view>
</view>
<view wx:if="{{start}}" style="display:flex;flex-direction: column;gap: 30rpx;">
<view class="result-title">
<image class="result-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>生成结果</text>
</view>
<view class="video-preview">
<view class="preview-bg">
<image class="preview-plant plant-left icon-placeholder" src="/static/images/my/image.png" mode="aspectFit">
</image>
<image class="preview-plant plant-right icon-placeholder" src="/static/images/my/image.png" mode="aspectFit">
</image>
<image class="preview-avatar icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<image class="play-btn icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
</view>
</view>
<view class="bottom-actions">
<view class="action-primary" bindtap="handleDownload">
<image class="download-icon icon-placeholder" src="/static/images/download.png" mode="aspectFit"></image>
<text>下载视频</text>
</view>
<view class="action-outline" bindtap="handleRegenerate">
<image class="refresh-icon icon-placeholder" src="/static/images/currency-exchange.png" mode="aspectFit">
</image>
<text>重新生成</text>
</view>
<view class="action-outline" bindtap="handleHome">
<image class="home-icon icon-placeholder" src="/static/images/to_index.png" mode="aspectFit"></image>
<text>返回首页</text>
</view>
</view>
<view class="save-tip">
<image class="tip-icon icon-placeholder" src="/static/images/info-circle.png" mode="aspectFit"></image>
<text>视频已为你自动保存到【创作记录】</text>
</view>
</view>
</view>
</view>
+590
View File
@@ -0,0 +1,590 @@
page {
min-height: 100vh;
background: #f8faff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.gen-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 12% 0%, rgba(104, 126, 255, 0.08), transparent 34%),
radial-gradient(circle at 88% 8%, rgba(130, 86, 255, 0.08), transparent 30%),
linear-gradient(180deg, #fbfcff 0%, #f6f8ff 46%, #ffffff 100%);
}
.gen-nav {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
}
.nav-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
flex: 0 0 60rpx;
}
.nav-back text {
width: 26rpx;
height: 26rpx;
display: block;
border-left: 5rpx solid #111827;
border-bottom: 5rpx solid #111827;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title {
flex: 1;
min-width: 0;
color: #111827;
font-size: 34rpx;
line-height: 42rpx;
font-weight: 900;
text-align: center;
}
.coin-pill {
height: 50rpx;
padding: 0 16rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
/* border: 2rpx solid #eef0fb; */
border-radius: 28rpx;
/* background: rgba(255, 255, 255, 0.88); */
/* box-shadow: 0 8rpx 18rpx rgba(93, 102, 158, 0.12); */
color: #111827;
font-size: 22rpx;
line-height: 28rpx;
font-weight: 800;
flex: 0 0 auto;
}
.coin-icon {
width: 30rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
color: #ffffff;
background: linear-gradient(135deg, #ffd45d 0%, #ffa51f 100%);
font-size: 17rpx;
font-weight: 900;
}
.coin-count {
color: #6657ff;
}
.gen-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 18rpx;
padding: 30rpx;
box-sizing: border-box;
justify-content: space-between;
}
.confirm-card,
.cost-card {
width: 100%;
display: flex;
flex-direction: column;
border: 2rpx solid #e6e9ff;
border-radius: 16rpx;
background: rgba(255, 255, 255, 0.96);
box-shadow: 0 14rpx 34rpx rgba(80, 92, 170, 0.08);
box-sizing: border-box;
}
.confirm-card {
padding: 22rpx 28rpx 10rpx;
}
.card-title {
color: #111827;
font-size: 27rpx;
line-height: 34rpx;
font-weight: 900;
padding-bottom: 18rpx;
border-bottom: 2rpx solid #edf0f8;
}
.info-list {
display: flex;
flex-direction: column;
}
.info-row {
min-height: 62rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
border-bottom: 2rpx solid #edf0f8;
}
.info-row:last-child {
border-bottom: 0;
}
.avatar-row {
min-height: 72rpx;
}
.info-label {
flex: 0 0 160rpx;
color: #303748;
font-size: 24rpx;
line-height: 32rpx;
font-weight: 900;
}
.info-value,
.voice-value,
.script-summary {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: flex-end;
color: #41495b;
font-size: 24rpx;
line-height: 32rpx;
font-weight: 700;
text-align: right;
}
.small-avatar {
width: 58rpx;
height: 58rpx;
border-radius: 10rpx;
background:
radial-gradient(circle at 50% 33%, #ffe1d2 0 17rpx, transparent 18rpx),
radial-gradient(circle at 50% 34%, #33201b 0 24rpx, transparent 25rpx),
linear-gradient(180deg, #eef4ff 0%, #ffffff 100%);
flex: 0 0 58rpx;
}
.voice-value {
gap: 10rpx;
}
.voice-icon {
width: 28rpx;
height: 28rpx;
display: flex;
align-items: center;
justify-content: center;
color: #6d58ff;
flex: 0 0 28rpx;
}
.voice-icon::before,
.voice-icon::after,
.voice-icon text {
content: '';
width: 5rpx;
border-radius: 5rpx;
background: #6d58ff;
}
.voice-icon::before { height: 18rpx; }
.voice-icon text { height: 28rpx; margin: 0 4rpx; display: block; }
.voice-icon::after { height: 20rpx; }
.script-row {
align-items: flex-start;
padding: 16rpx 0;
}
.script-summary {
flex-direction: column;
align-items: flex-end;
gap: 10rpx;
line-height: 34rpx;
}
.view-script {
display: flex;
align-items: center;
gap: 10rpx;
color: #7062ff;
font-size: 23rpx;
font-weight: 800;
}
.right-arrow {
width: 14rpx;
height: 14rpx;
border-right: 4rpx solid #7062ff;
border-top: 4rpx solid #7062ff;
transform: rotate(45deg);
box-sizing: border-box;
}
.cost-card {
padding: 12rpx 24rpx 22rpx;
background: linear-gradient(100deg, rgba(255, 255, 255, 0.98) 0%, rgba(239, 255, 248, 0.92) 100%);
margin-bottom: calc(10rpx + env(safe-area-inset-bottom));
}
.cost-row {
min-height: 52rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 2rpx solid rgba(224, 229, 242, 0.75);
color: #303748;
font-size: 24rpx;
line-height: 32rpx;
font-weight: 900;
}
.amount,
.balance-line,
.enough {
display: flex;
align-items: center;
}
.amount {
gap: 10rpx;
color: #111827;
}
.amount text:first-child {
font-size: 32rpx;
line-height: 38rpx;
font-weight: 900;
}
.amount text:last-child {
font-size: 23rpx;
font-weight: 800;
}
.balance-line {
gap: 22rpx;
}
.enough {
gap: 8rpx;
color: #40bf83;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 900;
}
.enough-waring {
gap: 8rpx;
color: red;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 900;
}
.enough text:first-child {
width: 26rpx;
height: 26rpx;
border-radius: 50%;
background: #42c982;
}
.enough text:first-child::after {
content: '';
width: 11rpx;
height: 7rpx;
margin: 7rpx auto 0;
display: block;
border-left: 3rpx solid #ffffff;
border-bottom: 3rpx solid #ffffff;
transform: rotate(-45deg);
box-sizing: border-box;
}
.generate-action {
height: 66rpx;
margin-top: 14rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
border-radius: 12rpx;
color: #ffffff;
background: linear-gradient(100deg, #4e84ff 0%, #7d3ff3 100%);
box-shadow: 0 12rpx 24rpx rgba(91, 86, 245, 0.22);
font-size: 27rpx;
line-height: 34rpx;
font-weight: 900;
}
.sparkle {
width: 28rpx;
height: 28rpx;
background:
linear-gradient(90deg, transparent 42%, #ffffff 43% 57%, transparent 58%),
linear-gradient(0deg, transparent 42%, #ffffff 43% 57%, transparent 58%);
transform: rotate(45deg);
}
.result-title {
margin-top: 14rpx;
display: flex;
align-items: center;
gap: 10rpx;
color: #3f4dff;
font-size: 25rpx;
line-height: 32rpx;
font-weight: 900;
}
.result-icon {
width: 25rpx;
height: 25rpx;
border: 4rpx solid #625aff;
border-radius: 8rpx;
box-sizing: border-box;
}
.video-preview {
width: 100%;
height: 274rpx;
border-radius: 14rpx;
overflow: hidden;
box-shadow: 0 12rpx 30rpx rgba(83, 92, 151, 0.13);
}
.preview-bg {
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: center;
background:
linear-gradient(90deg, rgba(255,255,255,0.36) 0 3rpx, transparent 3rpx 64rpx),
linear-gradient(180deg, #fff4e3 0%, #f1dcc3 58%, #d9b98f 100%);
}
.preview-plant {
width: 86rpx;
height: 126rpx;
margin-bottom: 18rpx;
border-radius: 50rpx 50rpx 12rpx 12rpx;
background:
radial-gradient(circle at 30% 24%, #79ad65 0 22rpx, transparent 23rpx),
radial-gradient(circle at 66% 32%, #6aa05f 0 24rpx, transparent 25rpx),
radial-gradient(circle at 50% 50%, #7eb86a 0 25rpx, transparent 26rpx),
linear-gradient(180deg, transparent 0 82rpx, #a9794d 83rpx 100%);
}
.plant-left {
margin-right: 76rpx;
}
.plant-right {
margin-left: 76rpx;
}
.preview-avatar {
width: 176rpx;
height: 236rpx;
margin-bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.avatar-head {
width: 116rpx;
height: 130rpx;
display: flex;
align-items: flex-end;
justify-content: center;
border-radius: 60rpx 60rpx 42rpx 42rpx;
background: linear-gradient(160deg, #241a18 0%, #604038 60%, #8a6258 100%);
}
.avatar-face {
width: 78rpx;
height: 92rpx;
margin-bottom: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12rpx;
border-radius: 42rpx 42rpx 34rpx 34rpx;
background: linear-gradient(180deg, #ffe6d9 0%, #ffd0c2 100%);
}
.avatar-eyes {
width: 48rpx;
display: flex;
justify-content: space-between;
}
.avatar-eyes text {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #241a18;
}
.avatar-mouth {
width: 26rpx;
height: 8rpx;
border-radius: 0 0 16rpx 16rpx;
background: #ca6975;
}
.avatar-body {
width: 160rpx;
height: 106rpx;
border-radius: 46rpx 46rpx 0 0;
background: linear-gradient(120deg, #ffffff 0%, #f5f4f1 48%, #ffffff 100%);
}
.play-btn {
width: 74rpx;
height: 74rpx;
margin-left: -126rpx;
margin-bottom: 54rpx;
display: flex;
align-items: center;
justify-content: center;
border: 4rpx solid rgba(255, 255, 255, 0.82);
border-radius: 50%;
background: rgba(49, 43, 38, 0.48);
box-sizing: border-box;
}
.play-btn text {
width: 0;
height: 0;
margin-left: 6rpx;
border-top: 16rpx solid transparent;
border-bottom: 16rpx solid transparent;
border-left: 24rpx solid #ffffff;
}
.bottom-actions {
display: flex;
gap: 18rpx;
}
.action-primary,
.action-outline {
height: 82rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
border-radius: 14rpx;
font-size: 24rpx;
line-height: 31rpx;
font-weight: 900;
box-sizing: border-box;
flex: 1;
}
.action-primary {
color: #ffffff;
background: linear-gradient(100deg, #5a80ff 0%, #764df3 100%);
box-shadow: 0 12rpx 24rpx rgba(91, 86, 245, 0.18);
}
.action-outline {
color: #625aff;
border: 2rpx solid #dce0f5;
background: #ffffff;
}
.download-icon {
width: 24rpx;
height: 24rpx;
border-left: 4rpx solid #ffffff;
border-bottom: 4rpx solid #ffffff;
transform: rotate(-45deg);
box-sizing: border-box;
}
.refresh-icon {
width: 24rpx;
height: 24rpx;
border: 4rpx solid #625aff;
border-right-color: transparent;
border-radius: 50%;
box-sizing: border-box;
}
.home-icon {
width: 25rpx;
height: 22rpx;
border: 4rpx solid #625aff;
border-top: 0;
border-radius: 4rpx;
box-sizing: border-box;
transform: translateY(4rpx);
}
.home-icon::before {
content: '';
width: 18rpx;
height: 18rpx;
display: block;
border-left: 4rpx solid #625aff;
border-top: 4rpx solid #625aff;
transform: rotate(45deg) translate(-4rpx, -13rpx);
box-sizing: border-box;
}
.save-tip {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
color: #8d94a5;
font-size: 23rpx;
line-height: 30rpx;
font-weight: 600;
margin-bottom: calc(10rpx + env(safe-area-inset-bottom));
}
.tip-icon {
width: 24rpx;
height: 24rpx;
display: flex;
align-items: center;
justify-content: center;
border: 3rpx solid #9aa1b2;
border-radius: 50%;
font-size: 16rpx;
line-height: 18rpx;
font-weight: 900;
box-sizing: border-box;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
@@ -0,0 +1,137 @@
import { polishScript, saveScript, taskinfo } from "../../../utils/request"
type PolishStyle = {
id: string
name: string
icon: string
activeIcon: string
}
const styles: PolishStyle[] = [
{ id: 'natural', name: '自然口播', icon: '/static/images/Frame-15355646363.png', activeIcon: '/static/images/Frame-1561268387.png' },
{ id: 'goods', name: '带货风格', icon: '/static/images/Frame-1561268385.png', activeIcon: '/static/images/Frame-1561268389.png' },
{ id: 'emotion', name: '情感风格', icon: '/static/images/Frame-1561268384.png', activeIcon: '/static/images/Frame-1561268388.png' },
{ id: 'teach', name: '专业讲解', icon: '/static/images/Frame-1561268383.png', activeIcon: '/static/images/Frame-1561268390.png' },
{ id: 'viral', name: '爆款短视频', icon: '/static/images/Frame-1561268382.png', activeIcon: '/static/images/Frame-1561268386.png' },
]
const styleMap: Record<string, string> = {
natural: 'friendly',
goods: 'product',
emotion: 'rewrite',
teach: 'professional',
viral: 'rewrite',
}
Page({
data: {
styles,
selectedStyle: 'natural',
taskinfo: null as any,
inEdit: false,
},
onLoad(ops: any) {
taskinfo(ops.taskId).then((res: any) => {
if (!res.code) {
this.setData({ taskinfo: res.data })
} else {
wx.showModal({ title: '温馨提示', content: res.message || '获取文案失败', showCancel: false })
}
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/index/index' }),
})
},
handleStyleSelect(e: WechatMiniprogram.TouchEvent) {
const selectedStyle = e.currentTarget.dataset.id
if (!selectedStyle) return
this.setData({ selectedStyle })
},
handleEdit() {
if (!this.data.taskinfo) return
this.setData({ inEdit: !this.data.inEdit })
},
changeContent(e: any) {
const taskinfo = this.data.taskinfo
if (!taskinfo) return
taskinfo.script_text = e.detail.value
this.setData({ taskinfo })
},
handleRegenerate() {
const taskinfo = this.data.taskinfo
if (!taskinfo) return
const content = taskinfo.script_text || taskinfo.original_script || ''
if (!content) {
wx.showToast({ title: '暂无可润色文案', icon: 'none' })
return
}
wx.showLoading({ title: '润色中' })
polishScript(taskinfo.task_no, content, styleMap[this.data.selectedStyle] || 'optimize').then((res: any) => {
wx.hideLoading()
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '润色失败', showCancel: false })
return
}
taskinfo.script_text = res.data.scriptText
this.setData({ taskinfo, inEdit: false })
wx.showToast({ title: '已重新润色', icon: 'success' })
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
handleConfirm() {
this.saveCurrentScript(true)
},
saveCurrentScript(next: boolean) {
const taskinfo = this.data.taskinfo
if (!taskinfo) return
const content = (taskinfo.script_text || '').trim()
if (!content) {
wx.showToast({ title: '文案不能为空', icon: 'none' })
return
}
wx.showLoading({ title: '保存中' })
saveScript(taskinfo.task_no, content).then((res: any) => {
wx.hideLoading()
if (res.code) {
wx.showModal({ title: '温馨提示', content: res.message || '保存失败', showCancel: false })
return
}
taskinfo.script_text = res.data.scriptText
this.setData({ taskinfo, inEdit: false })
if (next) {
wx.redirectTo({
url: '/pages/aiPackage/record/index?taskid=' + taskinfo.task_no,
})
} else {
wx.showToast({ title: '已保存', icon: 'success' })
}
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
})
@@ -0,0 +1,68 @@
<view class="preview-page">
<custom-nav content-height="86" padding-x="34">
<view class="preview-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<view class="nav-title-wrap">
<text class="nav-title">文案优化</text>
<text class="nav-subtitle">让口播更自然,更适合短视频传播</text>
</view>
<view class="nav-space"></view>
</view>
</custom-nav>
<view class="preview-content">
<view class="panel original-panel">
<text class="panel-title">原文案</text>
<view class="script-text original-text">
<text>{{taskinfo.original_script}}</text>
</view>
<text class="count-text">共 {{taskinfo.original_script.length}} 字</text>
</view>
<view class="panel ai-panel">
<view class="panel-head">
<view class="title-line">
<text class="panel-title">AI 优化后文案</text>
<text class="status-pill">已优化</text>
</view>
<view class="edit-btn" bindtap="handleEdit">
<image class="edit-icon icon-placeholder" src="/static/images/edit.png" mode="aspectFit"></image>
<text>{{inEdit?'保存':'编辑'}}</text>
</view>
</view>
<view class="optimized-box">
<text wx:if="{{inEdit == false}}">{{taskinfo.script_text}}</text>
<textarea bindinput="changeContent" wx:else value="{{taskinfo.script_text}}"></textarea>
<text class="optimized-count">共 {{taskinfo.script_text.length}} 字</text>
</view>
</view>
<view class="panel style-panel">
<text class="panel-title">选择润色风格</text>
<view class="style-list">
<view
wx:for="{{styles}}"
wx:key="id"
class="style-item {{selectedStyle === item.id ? 'style-item-active' : ''}}"
data-id="{{item.id}}"
bindtap="handleStyleSelect"
>
<image class="style-icon style-icon-{{item.id}} icon-placeholder" src="{{selectedStyle == item.id ? item.activeIcon :item.icon}}" mode="aspectFit"></image>
<text>{{item.name}}</text>
</view>
</view>
</view>
<view class="action-row">
<view class="outline-action" bindtap="handleRegenerate">重新润色</view>
<view class="confirm-action" bindtap="handleConfirm">确认使用此文案</view>
</view>
<view class="bottom-tip">
<image class="tip-dot icon-placeholder" src="/static/images/info-circle.png" mode="aspectFit"></image>
<text>新生成的视频将使用优化后的文案进行口播</text>
</view>
</view>
</view>
@@ -0,0 +1,335 @@
page {
min-height: 100vh;
background: #f8faff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.preview-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 10% 0%, rgba(117, 93, 255, 0.08), transparent 32%),
radial-gradient(circle at 92% 8%, rgba(74, 132, 255, 0.08), transparent 30%),
linear-gradient(180deg, #fbfcff 0%, #f5f7ff 48%, #ffffff 100%);
}
.preview-nav {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back,
.nav-space {
width: 60rpx;
height: 60rpx;
flex: 0 0 60rpx;
display: flex;
align-items: center;
}
.nav-back text {
width: 26rpx;
height: 26rpx;
display: block;
border-left: 5rpx solid #111827;
border-bottom: 5rpx solid #111827;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title-wrap {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10rpx;
}
.nav-title {
font-size: 34rpx;
line-height: 40rpx;
font-weight: 900;
color: #111827;
}
.nav-subtitle {
max-width: 520rpx;
font-size: 22rpx;
line-height: 28rpx;
font-weight: 600;
color: #6d7484;
text-align: center;
white-space: nowrap;
}
.preview-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 26rpx;
padding: 22rpx 34rpx 30rpx;
box-sizing: border-box;
}
.panel {
width: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
border: 2rpx solid #e8eaff;
border-radius: 16rpx;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 12rpx 34rpx rgba(80, 92, 170, 0.08);
}
.original-panel {
min-height: 326rpx;
padding: 25rpx 32rpx 22rpx;
}
.panel-title {
font-size: 28rpx;
line-height: 36rpx;
font-weight: 900;
color: #111827;
}
.script-text {
margin-top: 22rpx;
display: flex;
flex-direction: column;
color: #252a35;
font-size: 24rpx;
line-height: 42rpx;
font-weight: 700;
}
.original-text {
flex: 1;
}
.count-text,
.optimized-count {
align-self: flex-end;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 600;
color: #9ba1af;
}
.ai-panel {
padding: 20rpx 18rpx 18rpx;
}
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
padding: 0 16rpx 16rpx;
}
.title-line {
min-width: 0;
display: flex;
align-items: center;
gap: 18rpx;
}
.status-pill {
padding: 4rpx 18rpx;
border-radius: 20rpx;
background: #ece9ff;
color: #7860ff;
font-size: 20rpx;
line-height: 28rpx;
font-weight: 800;
white-space: nowrap;
}
.edit-btn {
height: 48rpx;
padding: 0 20rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
border: 2rpx solid #d8d1ff;
border-radius: 14rpx;
color: #7254f4;
font-size: 23rpx;
line-height: 28rpx;
font-weight: 800;
box-sizing: border-box;
flex: 0 0 auto;
}
.edit-icon {
width: 21rpx;
height: 21rpx;
border: 4rpx solid #7254f4;
border-left: 0;
border-top: 0;
transform: skew(-12deg) rotate(-45deg);
box-sizing: border-box;
}
.optimized-box {
min-height: 286rpx;
padding: 20rpx 20rpx 18rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
border: 2rpx solid #dcd9ff;
border-radius: 14rpx;
box-sizing: border-box;
color: #252a35;
font-size: 24rpx;
line-height: 40rpx;
font-weight: 700;
background: #ffffff;
}
.style-panel {
padding: 25rpx 18rpx 22rpx;
gap: 22rpx;
}
.style-list {
display: flex;
align-items: stretch;
justify-content: space-between;
gap: 16rpx;
}
.style-item {
width: 112rpx;
height: 112rpx;
padding: 12rpx 8rpx 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
border: 2rpx solid #e1e4f4;
border-radius: 12rpx;
background: #ffffff;
box-sizing: border-box;
color: #555d6f;
font-size: 19rpx;
line-height: 24rpx;
font-weight: 800;
text-align: center;
flex: 0 0 112rpx;
}
.style-item-active {
border-color: #7461ff;
background: linear-gradient(180deg, #806bff 0%, #5f55f5 100%);
color: #ffffff;
box-shadow: 0 10rpx 22rpx rgba(98, 85, 245, 0.26);
}
.style-icon {
width: 44rpx;
height: 44rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
font-size: 18rpx;
line-height: 20rpx;
font-weight: 900;
color: #ffffff;
background: #83cfff;
}
.style-icon-natural { background: linear-gradient(135deg, #8c82ff 0%, #6b58ff 100%); }
.style-icon-goods { background: linear-gradient(135deg, #a7e8ff 0%, #69bde8 100%); }
.style-icon-emotion { background: linear-gradient(135deg, #ffd888 0%, #ffb750 100%); }
.style-icon-teach { background: linear-gradient(135deg, #dca8ff 0%, #9f66ef 100%); }
.style-icon-viral { background: linear-gradient(135deg, #ffbccd 0%, #ff7696 100%); }
.style-item-active .style-icon {
background: rgba(255, 255, 255, 0.16);
border: 1rpx solid rgba(255, 255, 255, 0.24);
}
.action-row {
display: flex;
align-items: center;
gap: 20rpx;
margin-top: 2rpx;
}
.outline-action,
.confirm-action {
height: 76rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
font-size: 27rpx;
line-height: 34rpx;
font-weight: 900;
box-sizing: border-box;
}
.outline-action {
flex: 0 0 278rpx;
border: 2rpx solid #d9d4ff;
background: rgba(255, 255, 255, 0.86);
color: #7254f4;
}
.confirm-action {
flex: 1;
color: #ffffff;
background: linear-gradient(100deg, #4e84ff 0%, #7d3ff3 100%);
box-shadow: 0 12rpx 24rpx rgba(91, 86, 245, 0.24);
}
.bottom-tip {
margin-top: 4rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
color: #8d94a5;
font-size: 23rpx;
line-height: 30rpx;
font-weight: 600;
}
.tip-dot {
width: 24rpx;
height: 24rpx;
display: flex;
align-items: center;
justify-content: center;
border: 3rpx solid #8d94a5;
border-radius: 50%;
font-size: 16rpx;
line-height: 18rpx;
font-weight: 900;
box-sizing: border-box;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+301
View File
@@ -0,0 +1,301 @@
import { taskinfo, uploadavatar, uploadrecord, checkavatar, checkvoice, saveHumanAssets } from "../../../utils/request"
const waveBars = [
{ id: 'b1', cls: 'bar-22' },
{ id: 'b2', cls: 'bar-34' },
{ id: 'b3', cls: 'bar-48' },
{ id: 'b4', cls: 'bar-34' },
{ id: 'b5', cls: 'bar-62' },
{ id: 'b6', cls: 'bar-42' },
{ id: 'b7', cls: 'bar-70' },
{ id: 'b8', cls: 'bar-52' },
{ id: 'b9', cls: 'bar-38' },
{ id: 'b10', cls: 'bar-58' },
{ id: 'b11', cls: 'bar-46' },
{ id: 'b12', cls: 'bar-30' },
]
const miniBars = [
{ id: 'm1', cls: 'mini-bar-18' },
{ id: 'm2', cls: 'mini-bar-30' },
{ id: 'm3', cls: 'mini-bar-46' },
{ id: 'm4', cls: 'mini-bar-28' },
{ id: 'm5', cls: 'mini-bar-56' },
{ id: 'm6', cls: 'mini-bar-36' },
]
function formatRecordTime(totalSeconds: number) {
const minutes = Math.floor(totalSeconds / 60)
const seconds = totalSeconds % 60
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
}
Page({
recordTimer: 0,
voiceCheckTimer: 0,
data: {
isRecording: false,
recordingClass: 'recording-active',
elapsedSeconds: 0,
recordTime: '00:00',
waveBars,
miniBars,
taskinfo: null as any,
avatar: '/static/images/my/image.png',
avatarUrl: '',
avatarChecked: false,
recordUrl: '',
recordDurationMs: 0,
voiceAsrTaskId: '',
voiceChecked: false,
voiceText: '',
voiceStatusText: ''
},
onLoad(ops: any) {
taskinfo(ops.taskid).then((res: any) => {
if (!res.code) {
this.setData({ taskinfo: res.data });
}
})
},
onShow() {
if (this.data.isRecording) {
this.startRecordTimer()
}
},
onHide() {
this.clearRecordTimer()
this.clearVoiceCheckTimer()
},
onUnload() {
this.clearRecordTimer()
this.clearVoiceCheckTimer()
},
handleBack() {
wx.navigateBack({
fail: () => wx.navigateTo({ url: '/pages/aiPackage/preview/index' }),
})
},
handleUploadPhoto() {
const that = this;
wx.chooseImage({
count: 1,
success(res: any) {
const file = res.tempFilePaths[0];
wx.showLoading({ mask: true, title: '上传照片中' });
uploadavatar(file, {}, '/api/upload/avatar').then((uploadRes: any) => {
if (uploadRes.code) {
wx.hideLoading();
wx.showModal({ title: '温馨提示', content: uploadRes.message || '照片上传失败', showCancel: false })
return;
}
wx.showLoading({ mask: true, title: '人脸检测中' });
checkavatar(uploadRes.data.url).then((checkRes: any) => {
wx.hideLoading();
if (!checkRes.code) {
that.setData({ avatar: uploadRes.data.url, avatarUrl: uploadRes.data.url, avatarChecked: true })
wx.showToast({ title: '照片检测通过', icon: 'success' })
} else {
that.setData({ avatarUrl: '', avatarChecked: false })
wx.showModal({ title: '温馨提示', content: checkRes.message || '未检测到人脸', showCancel: false })
}
}).catch(() => {
wx.hideLoading();
that.setData({ avatarUrl: '', avatarChecked: false })
wx.showModal({ title: '温馨提示', content: '人脸检测失败,请稍后再试', showCancel: false })
})
}).catch(() => {
wx.hideLoading();
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
}
})
},
handleUseDefaultAvatar() {
wx.showToast({ title: '暂未配置默认形象', icon: 'none' })
},
handleRecord() {
if (this.data.isRecording) return
if (this.recordTimer) {
clearTimeout(this.recordTimer);
clearInterval(this.recordTimer);
}
this.clearVoiceCheckTimer()
this.recordTimer = 0;
this.setData({
isRecording: true,
recordTime: '00:00',
elapsedSeconds: 0,
recordingClass: 'recording-active',
recordUrl: '',
recordDurationMs: 0,
voiceAsrTaskId: '',
voiceChecked: false,
voiceText: '',
voiceStatusText: ''
})
this.startRecordTimer()
},
handleUseDefaultVoice() {
this.clearRecordTimer()
this.clearVoiceCheckTimer()
this.setData({ isRecording: false, recordingClass: '' })
wx.showToast({ title: '暂未配置默认音色', icon: 'none' })
},
handleStopRecord() {
this.clearRecordTimer()
this.recordTimer = 0;
this.setData({ isRecording: false, recordingClass: '' })
wx.getRecorderManager().stop()
},
handleNext() {
if (!this.data.taskinfo || !this.data.taskinfo.task_no) {
wx.showModal({ title: '温馨提示', content: '创作记录不存在', showCancel: false })
return
}
if (!this.data.avatarChecked || !this.data.avatarUrl) {
wx.showModal({ title: '温馨提示', content: '请先上传并通过数字人照片检测', showCancel: false })
return
}
if (!this.data.voiceChecked || !this.data.recordUrl || !this.data.voiceText) {
wx.showModal({ title: '温馨提示', content: '请先录制并通过音色检测', showCancel: false })
return
}
wx.showLoading({ mask: true, title: '保存中' })
saveHumanAssets({
taskId: this.data.taskinfo.task_no,
avatarUrl: this.data.avatarUrl,
voiceUrl: this.data.recordUrl,
voiceText: this.data.voiceText,
durationMs: this.data.recordDurationMs,
asrTaskId: this.data.voiceAsrTaskId
}).then((res: any) => {
wx.hideLoading()
if (!res.code) {
wx.redirectTo({ url: '/pages/aiPackage/bg/index?taskId=' + encodeURIComponent(this.data.taskinfo.task_no) })
} else {
wx.showModal({ title: '温馨提示', content: res.message || '保存失败', showCancel: false })
}
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
startRecordTimer() {
let that = this;
that.clearRecordTimer()
that.recordTimer = setInterval(() => {
const elapsedSeconds = that.data.elapsedSeconds + 1
that.setData({ elapsedSeconds, recordTime: formatRecordTime(elapsedSeconds) })
}, 1000) as unknown as number
let recorder = wx.getRecorderManager();
recorder.onError(that.onRecordError.bind(that));
recorder.onStop(that.onRecordStop.bind(that))
recorder.start({ duration: 10000, format: 'mp3' });
},
onRecordError(err: any) {
console.log(err)
this.clearRecordTimer()
this.setData({ isRecording: false, recordingClass: '' })
wx.showModal({ title: '温馨提示', content: '录音失败,请重新录制', showCancel: false })
},
onRecordStop(resource: { duration: number, fileSize: number, tempFilePath: string }) {
this.clearRecordTimer()
this.recordTimer = 0;
this.setData({ isRecording: false, recordingClass: '', voiceStatusText: '录音上传中' })
wx.showLoading({ mask: true, title: '上传录音中' })
uploadrecord(resource.tempFilePath).then((res: any) => {
if (res.code) {
wx.hideLoading()
this.setData({ voiceStatusText: '', voiceChecked: false })
wx.showModal({ title: '温馨提示', content: res.message || '录音上传失败', showCancel: false })
return
}
this.setData({
recordUrl: res.data.url,
recordDurationMs: res.data.durationMs || resource.duration || 0,
voiceAsrTaskId: '',
voiceChecked: false,
voiceText: '',
voiceStatusText: '录音识别中'
})
this.pollVoiceCheck('')
}).catch(() => {
wx.hideLoading()
this.setData({ voiceStatusText: '', voiceChecked: false })
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
pollVoiceCheck(asrTaskId: string) {
checkvoice(this.data.recordUrl, asrTaskId).then((res: any) => {
if (res.code === 200) {
wx.hideLoading()
const nextTaskId = res.data && res.data.asrTaskId ? res.data.asrTaskId : asrTaskId
this.setData({ voiceAsrTaskId: nextTaskId, voiceStatusText: '录音识别中' })
this.clearVoiceCheckTimer()
this.voiceCheckTimer = setTimeout(() => {
this.pollVoiceCheck(nextTaskId)
}, 1500) as unknown as number
return
}
wx.hideLoading()
if (!res.code) {
this.clearVoiceCheckTimer()
this.setData({
voiceAsrTaskId: res.data.asrTaskId,
voiceText: res.data.recognizedText,
voiceChecked: true,
voiceStatusText: '录音检测通过'
})
wx.showToast({ title: '音色检测通过', icon: 'success' })
} else {
this.clearVoiceCheckTimer()
this.setData({ voiceChecked: false, voiceStatusText: '' })
wx.showModal({ title: '温馨提示', content: res.message || '录音识别失败,请重新录制', showCancel: false })
}
}).catch(() => {
wx.hideLoading()
this.clearVoiceCheckTimer()
this.setData({ voiceChecked: false, voiceStatusText: '' })
wx.showModal({ title: '温馨提示', content: '录音识别失败,请稍后再试', showCancel: false })
})
},
clearRecordTimer() {
if (!this.recordTimer) return
clearInterval(this.recordTimer)
this.recordTimer = 0
},
clearVoiceCheckTimer() {
if (!this.voiceCheckTimer) return
clearTimeout(this.voiceCheckTimer)
this.voiceCheckTimer = 0
},
})
@@ -0,0 +1,100 @@
<view class="record-page">
<custom-nav content-height="86" padding-x="34">
<view class="record-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<view class="nav-title-wrap">
<text class="nav-title">设置数字人</text>
<text class="nav-subtitle">上传照片和录音,生成你的专属数字人</text>
</view>
<view class="nav-space"></view>
</view>
</custom-nav>
<view class="record-content">
<view class="panel photo-panel">
<text class="panel-title">上传数字人照片</text>
<view class="photo-body">
<view class="photo-preview">
<image class="portrait-bg icon-placeholder" src="{{ avatar }}" mode="aspectFit"></image>
<view class="camera-badge"><image class="camera-icon icon-placeholder" style="width: 60%;height: 60%;" src="/static/images/carram_white.png" mode="aspectFit"></image></view>
</view>
<view class="photo-actions">
<view class="primary-small" bindtap="handleUploadPhoto">上传照片</view>
<view class="ghost-small" bindtap="handleUseDefaultAvatar">使用默认形象</view>
</view>
</view>
<view class="hint-row">
<image class="hint-dot icon-placeholder" src="/static/images/zhuyi1.png" mode="aspectFit"></image>
<text>建议上传清晰正脸照片,生成效果更自然</text>
</view>
<!-- <view wx:if="{{avatarChecked}}" class="asset-status success-status">照片检测通过</view> -->
</view>
<view class="panel voice-panel">
<text class="panel-title">录取专属音色</text>
<view class="voice-body">
<view class="voice-visual {{recordingClass}}">
<view wx:if="{{isRecording}}" class="recording-pill"><text></text><text>录制中</text></view>
<view class="wave-row wave-left">
<text wx:for="{{waveBars}}" wx:key="id" class="{{item.cls}}"></text>
</view>
<view class="mic-wrap">
<view class="pulse-ring ring-one"></view>
<view class="pulse-ring ring-two"></view>
<view class="pulse-ring ring-three"></view>
<view class="mic-core">
<image class="mic-icon icon-placeholder" style="width: 60%;height: 60%;" src="/static/images/mic.png" mode="aspectFit"></image>
</view>
</view>
<view class="wave-row wave-right">
<text wx:for="{{waveBars}}" wx:key="id" class="{{item.cls}}"></text>
</view>
<text class="record-time">{{recordTime}}</text>
</view>
<view class="voice-actions">
<view class="primary-small" wx:if="{{!isRecording}}" bindtap="handleRecord">录取音色</view>
<view class="stop-small" wx:if="{{isRecording}}" bindtap="handleStopRecord">
<image class="stop-dot icon-placeholder" style="width: 40rpx;height: 40rpx;" src="/static/images/stop.png" mode="aspectFit"></image>
<text>停止录制</text>
</view>
<view class="ghost-small" bindtap="handleUseDefaultVoice">使用默认音色</view>
</view>
</view>
<view class="hint-row voice-hint">
<image class="hint-dot icon-placeholder" src="/static/images/zhuyi1.png" mode="aspectFit"></image>
<text>建议录制 10 秒以上,环境安静效果更佳</text>
</view>
<!-- <view wx:if="{{voiceStatusText}}" class="asset-status {{voiceChecked ? 'success-status' : 'pending-status'}}">{{voiceStatusText}}</view> -->
<view class="script-section">
<text class="script-title">朗读以下文案(约10秒)</text>
<view class="script-card">
<text>大家好,我是你的数字人口播助手。今天为大家带来一段精彩的内容介绍。</text>
</view>
</view>
</view>
<view class="default-section">
<text class="default-title">默认选项(可直接体验)</text>
<view class="default-row">
<view class="default-card">
<image class="mini-avatar icon-placeholder" src="https://cdn.zhuangb123.com/chat-square/EB3D71A23725-6CD76-956EFD1DB-0C25B0.jpg" mode="aspectFit"></image>
<text class="default-name">默认数字人</text>
<text class="trial-pill">可直接体验</text>
</view>
<view class="default-card">
<image class="mini-voice icon-placeholder" src="/static/images/ys.png" mode="aspectFit"></image>
<text class="default-name">默认音色</text>
<text class="trial-pill blue-pill">可直接体验</text>
</view>
</view>
</view>
<view class="next-action" bindtap="handleNext">下一步:选择视频背景</view>
</view>
</view>
@@ -0,0 +1,668 @@
page {
min-height: 100vh;
background: #f8faff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.record-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 10% 0%, rgba(116, 98, 255, 0.08), transparent 34%),
radial-gradient(circle at 88% 10%, rgba(73, 132, 255, 0.08), transparent 30%),
linear-gradient(180deg, #fbfcff 0%, #f7f9ff 46%, #ffffff 100%);
}
.record-nav {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back,
.nav-space {
width: 60rpx;
height: 60rpx;
flex: 0 0 60rpx;
display: flex;
align-items: center;
}
.nav-back text {
width: 26rpx;
height: 26rpx;
display: block;
border-left: 5rpx solid #111827;
border-bottom: 5rpx solid #111827;
transform: rotate(45deg);
box-sizing: border-box;
}
.nav-title-wrap {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10rpx;
}
.nav-title {
font-size: 34rpx;
line-height: 40rpx;
font-weight: 900;
color: #111827;
}
.nav-subtitle {
max-width: 560rpx;
color: #687086;
font-size: 22rpx;
line-height: 28rpx;
font-weight: 600;
text-align: center;
white-space: nowrap;
}
.record-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 22rpx;
padding: 20rpx 32rpx 30rpx;
box-sizing: border-box;
}
.panel {
width: 100%;
display: flex;
flex-direction: column;
border: 2rpx solid #e6e9ff;
border-radius: 18rpx;
background: rgba(255, 255, 255, 0.96);
box-shadow: 0 14rpx 34rpx rgba(80, 92, 170, 0.08);
box-sizing: border-box;
}
.photo-panel {
padding: 24rpx 26rpx 24rpx;
}
.panel-title {
color: #111827;
font-size: 28rpx;
line-height: 36rpx;
font-weight: 900;
}
.photo-body {
margin-top: 14rpx;
display: flex;
align-items: center;
gap: 56rpx;
}
.photo-preview {
width: 306rpx;
height: 224rpx;
display: flex;
align-items: flex-end;
justify-content: center;
border: 2rpx solid #dce1ff;
border-radius: 14rpx;
background: linear-gradient(180deg, #f8fbff 0%, #eef3ff 100%);
overflow: hidden;
box-sizing: border-box;
position: relative;
}
.portrait-bg {
width: 190rpx;
height: 218rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
}
.portrait-head {
width: 120rpx;
height: 138rpx;
display: flex;
align-items: flex-end;
justify-content: center;
border-radius: 60rpx 60rpx 44rpx 44rpx;
background: linear-gradient(160deg, #2e1e19 0%, #684235 56%, #9c6a58 100%);
box-shadow: 0 4rpx 12rpx rgba(85, 50, 44, 0.18);
}
.face {
width: 78rpx;
height: 98rpx;
margin-bottom: 10rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
border-radius: 42rpx 42rpx 36rpx 36rpx;
background: linear-gradient(180deg, #ffe8dc 0%, #ffd0c2 100%);
}
.eye-row {
width: 48rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.eye-row text {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #251918;
}
.nose {
width: 8rpx;
height: 12rpx;
border-radius: 8rpx;
background: rgba(196, 116, 101, 0.46);
}
.mouth {
width: 24rpx;
height: 7rpx;
border-radius: 0 0 16rpx 16rpx;
background: #ca6975;
}
.hair,
.hair-main {
display: none;
}
.portrait-neck {
width: 32rpx;
height: 22rpx;
background: #ffd3c4;
}
.portrait-cloth {
width: 178rpx;
height: 68rpx;
border-radius: 40rpx 40rpx 0 0;
background: linear-gradient(120deg, #ffffff 0%, #f7f6f2 50%, #ffffff 100%);
}
.camera-badge {
width: 60rpx;
height: 60rpx;
/* margin-bottom: 20rpx; */
display: flex;
align-items: center;
justify-content: center;
border: 4rpx solid rgba(255, 255, 255, 0.88);
border-radius: 50%;
background: rgba(66, 56, 49, 0.72);
box-sizing: border-box;
flex: 0 0 60rpx;
position: absolute;
bottom: 20rpx;
}
.camera-icon {
width: 28rpx;
height: 21rpx;
border: 4rpx solid #ffffff;
border-radius: 5rpx;
box-sizing: border-box;
}
.camera-icon::after {
content: '';
width: 8rpx;
height: 8rpx;
margin: 2rpx auto 0;
display: block;
border: 3rpx solid #ffffff;
border-radius: 50%;
box-sizing: border-box;
}
.photo-actions,
.voice-actions {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 24rpx;
}
.primary-small,
.ghost-small,
.stop-small {
height: 70rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
font-size: 26rpx;
line-height: 32rpx;
font-weight: 900;
box-sizing: border-box;
}
.primary-small {
color: #ffffff;
background: linear-gradient(100deg, #4e84ff 0%, #7d3ff3 100%);
box-shadow: 0 12rpx 24rpx rgba(91, 86, 245, 0.22);
}
.ghost-small,
.stop-small {
border: 2rpx solid #d9dcf2;
background: #fafbff;
color: #596083;
}
.hint-row {
margin-top: 18rpx;
display: flex;
align-items: center;
gap: 8rpx;
color: #7a8196;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 600;
}
.hint-dot {
width: 18rpx;
height: 18rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #b9bfd2;
color: #ffffff;
font-size: 12rpx;
line-height: 16rpx;
font-weight: 900;
}
.voice-panel {
padding: 24rpx 26rpx 24rpx;
}
.voice-body {
margin-top: 12rpx;
display: flex;
align-items: center;
gap: 32rpx;
}
.voice-visual {
width: 340rpx;
min-height: 240rpx;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
}
.recording-pill {
width: 116rpx;
height: 44rpx;
margin-right: 224rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
border-radius: 22rpx;
color: #ffffff;
background: linear-gradient(90deg, #7380ff 0%, #8a55ed 100%);
box-shadow: 0 8rpx 18rpx rgba(117, 94, 239, 0.3);
font-size: 21rpx;
line-height: 26rpx;
font-weight: 800;
}
.recording-pill text:first-child {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #ffffff;
}
.recording-active .recording-pill text:first-child {
animation: blinkDot 1s ease-in-out infinite;
}
.wave-row {
width: 118rpx;
height: 82rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 7rpx;
}
.wave-row text {
width: 5rpx;
border-radius: 6rpx;
background: linear-gradient(180deg, #5f86ff 0%, #bd67f2 100%);
transform-origin: center;
}
.recording-active .wave-row text:nth-child(2n) {
animation: waveScaleA 0.78s ease-in-out infinite;
}
.recording-active .wave-row text:nth-child(2n + 1) {
animation: waveScaleB 0.92s ease-in-out infinite;
}
.recording-active .wave-row text:nth-child(3n) {
animation-delay: 0.18s;
}
.mic-wrap {
width: 98rpx;
height: 98rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: radial-gradient(circle, rgba(139, 102, 255, 0.13) 0%, rgba(139, 102, 255, 0.05) 68%, transparent 70%);
}
.pulse-ring {
width: 90rpx;
height: 90rpx;
margin-right: -90rpx;
border: 3rpx solid rgba(135, 98, 247, 0.28);
border-radius: 50%;
box-sizing: border-box;
}
.recording-active .ring-one { animation: pulseRing 1.5s ease-out infinite; }
.recording-active .ring-two { animation: pulseRing 1.5s ease-out 0.3s infinite; }
.recording-active .ring-three { animation: pulseRing 1.5s ease-out 0.6s infinite; }
.mic-core {
width: 90rpx;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: linear-gradient(160deg, #77a0ff 0%, #8346f5 100%);
box-shadow: 0 10rpx 24rpx rgba(117, 86, 243, 0.32);
}
.mic-icon {
width: 32rpx;
height: 45rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.mic-icon::before {
content: '';
width: 24rpx;
height: 34rpx;
border-radius: 14rpx;
background: #ffffff;
}
.mic-icon text {
width: 34rpx;
height: 14rpx;
display: block;
border-bottom: 5rpx solid #ffffff;
border-left: 5rpx solid #ffffff;
border-right: 5rpx solid #ffffff;
border-radius: 0 0 18rpx 18rpx;
box-sizing: border-box;
}
.record-time {
width: 100%;
margin-top: 2rpx;
color: #586085;
font-size: 31rpx;
line-height: 38rpx;
font-weight: 600;
text-align: center;
}
.stop-small {
gap: 16rpx;
}
.stop-dot {
width: 16rpx;
height: 16rpx;
border-radius: 3rpx;
background: #ff6b6b;
}
.voice-hint {
margin-top: 8rpx;
}
.script-section {
margin-top: 24rpx;
display: flex;
flex-direction: column;
gap: 12rpx;
}
.script-title {
color: #111827;
font-size: 24rpx;
line-height: 32rpx;
font-weight: 900;
}
.script-card {
min-height: 92rpx;
padding: 18rpx 22rpx;
display: flex;
align-items: center;
border: 2rpx solid #dfe3ff;
border-radius: 10rpx;
background: linear-gradient(180deg, #fbfbff 0%, #f6f7ff 100%);
box-sizing: border-box;
color: #4d557d;
font-size: 24rpx;
line-height: 36rpx;
font-weight: 600;
}
.default-section {
display: flex;
flex-direction: column;
gap: 12rpx;
}
.default-title {
color: #4f47ff;
font-size: 23rpx;
line-height: 30rpx;
font-weight: 900;
}
.default-row {
display: flex;
gap: 10rpx;
}
.default-card {
flex: 1;
height: 88rpx;
min-width: 0;
padding: 10rpx 12rpx;
display: flex;
align-items: center;
gap: 12rpx;
border: 2rpx solid #e4e7f6;
border-radius: 14rpx;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 10rpx 24rpx rgba(80, 92, 170, 0.08);
box-sizing: border-box;
}
.mini-avatar {
width: 58rpx;
height: 58rpx;
border-radius: 12rpx;
background:
radial-gradient(circle at 50% 32%, #ffe0d2 0 16rpx, transparent 17rpx),
radial-gradient(circle at 50% 35%, #4b2d25 0 24rpx, transparent 25rpx),
linear-gradient(180deg, #eef4ff 0%, #ffffff 100%);
flex: 0 0 58rpx;
}
.mini-voice {
width: 58rpx;
height: 58rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 5rpx;
border-radius: 12rpx;
background: #eef2ff;
flex: 0 0 58rpx;
}
.mini-voice text {
width: 5rpx;
border-radius: 5rpx;
background: linear-gradient(180deg, #5f86ff 0%, #8550f2 100%);
}
.default-name {
flex: 1;
min-width: 0;
color: #111827;
font-size: 23rpx;
line-height: 30rpx;
font-weight: 900;
white-space: nowrap;
}
.trial-pill {
padding: 6rpx 12rpx;
border-radius: 18rpx;
background: #fff3db;
color: #f4a326;
font-size: 18rpx;
line-height: 22rpx;
font-weight: 800;
white-space: nowrap;
}
.blue-pill {
background: #e9f3ff;
color: #5a96f0;
}
.next-action {
height: 78rpx;
margin-top: -2rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx;
color: #ffffff;
background: linear-gradient(100deg, #4e84ff 0%, #7d3ff3 100%);
box-shadow: 0 12rpx 24rpx rgba(91, 86, 245, 0.24);
font-size: 31rpx;
line-height: 38rpx;
font-weight: 900;
}
@keyframes waveScaleA {
0%, 100% { transform: scaleY(0.62); opacity: 0.62; }
50% { transform: scaleY(1.18); opacity: 1; }
}
@keyframes waveScaleB {
0%, 100% { transform: scaleY(1); opacity: 0.88; }
50% { transform: scaleY(0.55); opacity: 0.55; }
}
@keyframes pulseRing {
0% { transform: scale(0.72); opacity: 0.7; }
100% { transform: scale(1.72); opacity: 0; }
}
@keyframes blinkDot {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.38; transform: scale(0.72); }
}
.bar-22 { height: 22rpx; }
.bar-30 { height: 30rpx; }
.bar-34 { height: 34rpx; }
.bar-38 { height: 38rpx; }
.bar-42 { height: 42rpx; }
.bar-46 { height: 46rpx; }
.bar-48 { height: 48rpx; }
.bar-52 { height: 52rpx; }
.bar-58 { height: 58rpx; }
.bar-62 { height: 62rpx; }
.bar-70 { height: 70rpx; }
.mini-bar-18 { height: 18rpx; }
.mini-bar-28 { height: 28rpx; }
.mini-bar-30 { height: 30rpx; }
.mini-bar-36 { height: 36rpx; }
.mini-bar-46 { height: 46rpx; }
.mini-bar-56 { height: 56rpx; }
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
.asset-status {
margin-top: 14rpx;
height: 44rpx;
padding: 0 18rpx;
display: inline-flex;
align-items: center;
align-self: flex-start;
border-radius: 22rpx;
font-size: 21rpx;
line-height: 28rpx;
font-weight: 800;
box-sizing: border-box;
}
.success-status {
color: #16854f;
background: #e9f8f0;
border: 2rpx solid #bdebd3;
}
.pending-status {
color: #5a5f89;
background: #f1f3ff;
border: 2rpx solid #dfe3ff;
}
+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;
}
@@ -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;
}
+7
View File
@@ -0,0 +1,7 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav",
"checkin-popup": "/components/checkin-popup/checkin-popup"
},
"navigationStyle": "custom"
}
+197
View File
@@ -0,0 +1,197 @@
import { analysis, tasksearch, polishScript, createTask } from "../../utils/request"
const sampleText = '解析中...'
const checkinStorageKey = 'lastCheckinDate'
function getTodayKey() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
Component({
data: {
parsed: false,
videoLink: '',
scriptText: sampleText,
showCheckinPopup: false,
taskId: '',
parseId: '',
polishing: false
},
lifetimes: {
attached() {
const today = getTodayKey()
const lastCheckinDate = wx.getStorageSync(checkinStorageKey)
if (lastCheckinDate !== today) {
this.setData({ showCheckinPopup: true })
}
},
},
methods: {
handleInput(e: any) {
this.setData({
videoLink: e.detail.value,
})
},
onUnload() {
this.setData({ videoLink: '', parsed: false, scriptText: '' })
},
handlePaste() {
wx.getClipboardData({
success: (res) => {
this.setData({
videoLink: res.data || '0.58 J@i.cN 08/24 :0pm ULw:/ # 新到 # 国五排放 # 货车出售转让 https://v.douyin.com/tHoCmTcQT2Q/ 复制此链接,打开Dou音搜索,直接观看视频!',
})
},
fail: () => {
this.setData({
videoLink: '0.58 J@i.cN 08/24 :0pm ULw:/ # 新到 # 国五排放 # 货车出售转让 https://v.douyin.com/tHoCmTcQT2Q/ 复制此链接,打开Dou音搜索,直接观看视频!',
})
},
})
},
handleParse() {
if (this.data.videoLink == '') {
wx.showModal({
title: '温馨提示',
content: '解析链接不能为空',
showCancel: false
})
return;
}
wx.showLoading({ title: '解析中.' });
analysis(this.data.videoLink).then((res: any) => {
console.log(res);
wx.hideLoading();
if (!res.code) {
this.setData({
parsed: true,
taskId: '',
parseId: String(res.data.parseId || res.data.taskId || '')
})
wx.showLoading({ title: '等待结果中...', mask: true });
this.tasksearch(String(res.data.parseId || res.data.taskId || ''))
} else {
wx.showModal({
title: '温馨提示',
content: '解析失败,请确认链接的有效性',
showCancel: false
})
}
}).catch((err) => {
console.log(err)
wx.hideLoading();
wx.showModal({
title: '温馨提示',
content: '解析失败,请重试',
showCancel: false
})
})
},
tasksearch(taskId: string) {
let that = this;
tasksearch(taskId).then((res: any) => {
if (!res.code) {
wx.hideLoading();
this.setData({ scriptText: (res.data && res.data.scriptText) || '' })
} else {
setTimeout(() => that.tasksearch(taskId), 500);
}
})
},
handleReset() {
this.setData({
parsed: false,
scriptText: '',
})
this.handleParse();
},
handleCopy() {
wx.setClipboardData({
data: this.data.scriptText,
fail(err) {
console.log(err);
wx.showModal({
title: '温馨提示',
content: '您还未授于剪切板权限'
})
}
})
},
handlePolish() {
if (!(this.data.parseId || this.data.taskId) || !this.data.scriptText) {
wx.showToast({ title: '暂无可润色文案', icon: 'none' })
return
}
if (this.data.polishing) return
this.setData({ polishing: true })
wx.showLoading({ title: '润色中...', mask: true })
polishScript(this.data.parseId || this.data.taskId, this.data.scriptText, 'optimize').then((res: any) => {
wx.hideLoading()
this.setData({ polishing: false })
if (!res.code) {
this.setData({ scriptText: res.data.scriptText })
wx.showToast({ title: '已润色', icon: 'success' })
return
}
wx.showModal({ title: '温馨提示', content: res.message || '润色失败,请稍后再试', showCancel: false })
}).catch((err) => {
console.log(err)
wx.hideLoading()
this.setData({ polishing: false })
wx.showModal({ title: '温馨提示', content: '润色失败,请稍后再试', showCancel: false })
})
},
handleGoCreate() {
const parseId = this.data.parseId || this.data.taskId
if (!parseId || !this.data.scriptText) {
wx.showToast({ title: '请先完成文案提取', icon: 'none' })
return
}
wx.showLoading({ title: '创建任务中', mask: true })
createTask(parseId, this.data.scriptText).then((res: any) => {
wx.hideLoading()
if (!res.code && res.data && res.data.taskId) {
this.setData({ taskId: res.data.taskId })
wx.navigateTo({
url: '/pages/aiPackage/preview/index?taskId=' + res.data.taskId,
})
return
}
wx.showModal({ title: '温馨提示', content: res.message || '创建任务失败,请稍后再试', showCancel: false })
}).catch(() => {
wx.hideLoading()
wx.showModal({ title: '温馨提示', content: '网络异常,请稍后再试', showCancel: false })
})
},
handleExperience() {
this.setData({
videoLink: 'https://v.douyin.com/example/',
parsed: true,
})
},
handleCheckinClose() {
wx.setStorageSync(checkinStorageKey, getTodayKey())
this.setData({ showCheckinPopup: false })
},
handleCheckinRule() {
wx.showToast({ title: '每日签到可领取金币', icon: 'none' })
},
},
})
+96
View File
@@ -0,0 +1,96 @@
<view class="home-page">
<custom-nav padding-x="28">
<view class="topbar">
<text class="brand">数字人视频工厂</text>
<view class="coin-pill">
<image class="coin-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text class="coin-label">金币</text>
<text class="coin-count">128</text>
</view>
</view>
</custom-nav>
<view class="home-content">
<view class="hero">
<image src="../../static/images/indexed/top.png" style="width: 698rpx;height: 228rpx;"></image>
</view>
<view class="card parse-card">
<view class="card-head">
<view class="section-title">
<text class="step-dot">1</text>
<text>粘贴视频链接</text>
</view>
<view class="help-link">
<text class="help-dot">?</text>
<text>如何获取链接?</text>
</view>
</view>
<view class="input-row">
<input class="link-input" value="{{videoLink}}" placeholder="粘贴视频链接到这里" placeholder-class="input-placeholder" bindinput="handleInput" />
<view class="paste-btn" bindtap="handlePaste">一键粘贴</view>
</view>
<view class="primary-action" bindtap="handleParse">
<image class="plane-icon icon-placeholder" src="/static/images/xfj.png" mode="aspectFit"></image>
<text>开始解析</text>
</view>
</view>
<block wx:if="{{parsed}}">
<view class="success-strip">
<image class="success-icon icon-placeholder" src="/static/images/checked.png" mode="aspectFit"></image>
<view class="success-copy">
<text class="success-title">链接解析成功</text>
<text class="success-subtitle">已为你提取到口播文案</text>
</view>
<view class="outline-btn" bindtap="handleReset">重新解析</view>
</view>
<view class="card copy-card">
<view class="card-head copy-head">
<text class="copy-title">提取到的文案</text>
<view class="copy-btn" bindtap="handleCopy">
<image class="copy-icon icon-placeholder" src="/static/images/file_copy.png" mode="aspectFit"></image>
<text>复制文案</text>
</view>
</view>
<view class="copy-box">
<text>{{scriptText}}</text>
</view>
<view class="polish-btn" bindtap="handlePolish">
<view class="polish-text">
<image class="spark-icon icon-placeholder" src="/static/images/filter-3-filled.png" mode="aspectFit"></image>
<text class="polish-title">AI 一键润色</text>
</view>
<view><text class="polish-subtitle">让口播更自然,更吸引人</text></view>
</view>
</view>
<view class="tip-strip">
<image class="tip-icon icon-placeholder" src="/static/images/lightbulb.png" mode="aspectFit"></image>
<text>润色后可进入下一步,设置数字人和视频背景</text>
</view>
<view class="generate-btn" bindtap="handleGoCreate">
<text>去生成数字人视频</text>
<image class="arrow-icon icon-placeholder" src="/static/images/chevron-down.png" mode="aspectFit"></image>
</view>
</block>
<block wx:else>
<view class="flow-card">
<image src="../../static/images/indexed/lc_card.png" style="width: 699rpx;height: 298rpx;"></image>
</view>
<view class="sample-card" bindtap="handleExperience">
<image src="../../static/images/indexed/down_bottom.png" style="width: 100%;" mode="widthFix"></image>
</view>
</block>
</view>
<checkin-popup visible="{{showCheckinPopup}}" bind:close="handleCheckinClose" bind:rule="handleCheckinRule"></checkin-popup>
</view>
+809
View File
@@ -0,0 +1,809 @@
page {
min-height: 100vh;
background: #f8faff;
color: #101522;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.home-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
/* background:
radial-gradient(circle at 74% 11%, rgba(116, 114, 255, 0.13), transparent 18%),
linear-gradient(180deg, #ffffff 0%, #f7f9ff 56%, #ffffff 100%); */
}
.home-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 24rpx;
padding: 22rpx 28rpx 22rpx;
box-sizing: border-box;
}
.topbar {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 44px;
}
.brand {
font-size: 32rpx;
line-height: 1.2;
font-weight: 700;
letter-spacing: 0;
color: #121521;
}
.coin-pill {
display: flex;
align-items: center;
gap: 8rpx;
height: 52rpx;
margin-right: 160rpx;
margin-bottom: 10rpx;
padding: 0 22rpx 0 12rpx;
border-radius: 999rpx;
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 6rpx 18rpx rgba(84, 91, 167, 0.14);
border: 1rpx solid rgba(222, 226, 255, 0.88);
}
.coin-icon {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 18rpx;
font-weight: 800;
background: linear-gradient(135deg, #ffd875, #ff9f25);
}
.coin-label {
font-size: 26rpx;
font-weight: 700;
color: #25283a;
}
.coin-count {
font-size: 26rpx;
font-weight: 800;
color: #6763ff;
}
.hero {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 238rpx;
padding-top: 16rpx;
}
.hero-copy {
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
.hero-title-accent,
.hero-title-main {
display: block;
font-size: 44rpx;
line-height: 1.2;
font-weight: 900;
letter-spacing: 0;
}
.hero-title-accent {
color: #6356ff;
text-shadow: 0 5rpx 0 rgba(99, 86, 255, 0.12);
}
.hero-title-main {
margin-top: 10rpx;
color: #070a16;
}
.hero-subtitle {
margin-top: 32rpx;
font-size: 24rpx;
line-height: 1.4;
color: #4a4e5d;
}
.hero-visual {
width: 236rpx;
min-height: 202rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-left: 8rpx;
}
.visual-halo {
width: 162rpx;
height: 162rpx;
border-radius: 50%;
background: linear-gradient(135deg, rgba(111, 107, 255, 0.18), rgba(92, 191, 255, 0.05));
margin-bottom: -138rpx;
transform: translateX(18rpx);
}
.visual-row {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: -16rpx;
}
.video-tile {
width: 110rpx;
height: 100rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #7d6dff 0%, #358cf3 100%);
box-shadow: 22rpx 12rpx 0 rgba(81, 210, 255, 0.34), 0 16rpx 34rpx rgba(77, 83, 234, 0.34);
transform: translateY(-18rpx);
}
.play-mark {
width: 0;
height: 0;
border-top: 24rpx solid transparent;
border-bottom: 24rpx solid transparent;
border-left: 34rpx solid #ffffff;
margin-left: 8rpx;
}
.ai-chip {
width: 48rpx;
height: 50rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 23rpx;
font-weight: 800;
background: linear-gradient(135deg, #579dff, #8852ff);
box-shadow: 0 10rpx 22rpx rgba(109, 88, 255, 0.28);
margin-left: -4rpx;
transform: translateY(8rpx);
}
.orbital-base {
width: 178rpx;
height: 58rpx;
border-radius: 50%;
background: linear-gradient(90deg, #6c55f8, #edf6ff 46%, #8873ff);
box-shadow: 0 12rpx 22rpx rgba(96, 116, 255, 0.22);
display: flex;
align-items: center;
justify-content: center;
}
.base-center {
width: 110rpx;
height: 34rpx;
border-radius: 50%;
background: #ffffff;
box-shadow: inset 0 0 14rpx rgba(92, 115, 255, 0.2);
}
.card,
.sample-card,
.success-strip,
.tip-strip {
/* border: 1rpx solid rgba(215, 220, 249, 0.95); */
box-shadow: 0 8rpx 24rpx rgba(74, 88, 148, 0.06);
background: rgba(255, 255, 255, 0.94);
box-sizing: border-box;
}
.card {
border-radius: 18rpx;
padding: 26rpx 24rpx;
display: flex;
flex-direction: column;
}
.card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
}
.section-title {
display: flex;
align-items: center;
gap: 12rpx;
font-size: 26rpx;
line-height: 1.2;
font-weight: 800;
color: #141720;
}
.step-dot {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
font-weight: 800;
background: linear-gradient(135deg, #506bff, #8155ff);
}
.help-link {
display: flex;
align-items: center;
gap: 8rpx;
color: #8389a5;
font-size: 21rpx;
font-weight: 600;
white-space: nowrap;
}
.help-dot {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3rpx solid #a7add3;
font-size: 17rpx;
line-height: 1;
box-sizing: border-box;
}
.input-row {
margin-top: 26rpx;
height: 84rpx;
padding: 0 16rpx 0 22rpx;
border-radius: 14rpx;
border: 2rpx solid #d8d5ff;
display: flex;
align-items: center;
box-sizing: border-box;
background: #ffffff;
}
.link-input {
flex: 1;
min-width: 0;
height: 80rpx;
font-size: 25rpx;
color: #2a3041;
}
.input-placeholder {
color: #a9aec6;
font-weight: 500;
}
.paste-btn {
width: 124rpx;
height: 50rpx;
text-align: center;
line-height: 50rpx;
border-radius: 10rpx;
color: #ffffff;
font-size: 22rpx;
font-weight: 800;
background: linear-gradient(135deg, #528bff, #7b52ff);
}
.primary-action,
.polish-btn,
.generate-btn,
.sample-btn {
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
background: linear-gradient(135deg, #4f85ff 0%, #8b3df0 100%);
box-shadow: 0 8rpx 18rpx rgba(84, 96, 255, 0.18);
}
.primary-action {
margin-top: 28rpx;
height: 74rpx;
border-radius: 14rpx;
gap: 18rpx;
font-size: 28rpx;
font-weight: 800;
}
.plane-icon {
width: 28rpx;
height: 22rpx;
display: block;
border-top: 10rpx solid transparent;
border-bottom: 10rpx solid transparent;
border-left: 32rpx solid #ffffff;
box-sizing: border-box;
transform: rotate(-22deg);
}
.success-strip {
min-height: 70rpx;
border-radius: 18rpx;
padding: 12rpx 16rpx;
display: flex;
align-items: center;
gap: 14rpx;
background: linear-gradient(90deg, #e7fbf0 0%, #f6fffb 100%);
border-color: #c8f1dc;
}
.success-icon {
width: 42rpx;
height: 42rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: #24c878;
}
.check-mark {
width: 18rpx;
height: 10rpx;
display: block;
border-left: 5rpx solid #ffffff;
border-bottom: 5rpx solid #ffffff;
transform: rotate(-45deg) translateY(-2rpx);
}
.success-copy {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.success-title {
color: #131720;
font-size: 25rpx;
font-weight: 800;
line-height: 1.18;
}
.success-subtitle {
margin-top: 4rpx;
color: #747b8d;
font-size: 21rpx;
line-height: 1.15;
}
.outline-btn {
width: 118rpx;
height: 46rpx;
border-radius: 999rpx;
border: 2rpx solid #c4c4ff;
color: #7165ff;
text-align: center;
line-height: 46rpx;
font-size: 21rpx;
font-weight: 800;
background: #ffffff;
}
.copy-card {
gap: 18rpx;
padding-bottom: 18rpx;
}
.copy-head {
min-height: 36rpx;
}
.copy-title {
font-size: 26rpx;
font-weight: 800;
color: #121722;
}
.copy-btn {
height: 38rpx;
padding: 0 12rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
gap: 8rpx;
border: 1rpx solid #e0ddff;
color: #665fff;
background: #ffffff;
font-size: 21rpx;
font-weight: 800;
}
.copy-icon {
width: 18rpx;
height: 18rpx;
display: block;
border: 3rpx solid currentColor;
border-radius: 3rpx;
box-sizing: border-box;
box-shadow: 6rpx -5rpx 0 -2rpx #ffffff, 6rpx -5rpx 0 0 currentColor;
}
.copy-box {
min-height: 168rpx;
padding: 12rpx 16rpx;
border-radius: 12rpx;
border: 2rpx solid #d7d5ff;
color: #232838;
font-size: 22rpx;
line-height: 1.55;
font-weight: 600;
background: #ffffff;
box-sizing: border-box;
}
.polish-btn {
height: 72rpx;
border-radius: 13rpx;
display: flex;
flex-direction: column;
}
.spark-icon {
width: 26rpx;
height: 26rpx;
display: block;
margin-right: 10rpx;
background: #ffffff;
border-radius: 4rpx;
transform: rotate(45deg);
}
.polish-text {
display: flex;
flex-direction: row;
align-items: center;
/* gap: 4rpx; */
}
.polish-title {
font-size: 28rpx;
line-height: 1;
font-weight: 800;
}
.polish-subtitle {
font-size: 20rpx;
line-height: 1;
color: rgba(255, 255, 255, 0.82);
}
.tip-strip {
min-height: 44rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
color: #6569ff;
font-size: 22rpx;
font-weight: 700;
background: #f8faff;
}
.tip-icon {
width: 28rpx;
height: 28rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 3rpx solid #6867ff;
box-sizing: border-box;
font-size: 18rpx;
font-weight: 900;
}
.generate-btn {
height: 58rpx;
border-radius: 13rpx;
gap: 28rpx;
font-size: 27rpx;
font-weight: 800;
}
.arrow-icon {
width: 18rpx;
height: 18rpx;
display: block;
border-top: 5rpx solid #ffffff;
border-right: 5rpx solid #ffffff;
transform: rotate(45deg);
}
.flow-card {
/* padding-top: 28rpx;
padding-bottom: 30rpx;
*/
}
.flow-heading {
font-size: 27rpx;
font-weight: 800;
color: #161923;
}
.flow-list {
margin-top: 30rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.flow-item {
width: 178rpx;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.flow-icon {
width: 86rpx;
height: 82rpx;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
color: #6455ff;
font-weight: 900;
background: linear-gradient(145deg, #ffffff, #eef1ff);
box-shadow: inset 0 0 0 2rpx rgba(213, 218, 255, 0.9), 0 8rpx 16rpx rgba(85, 96, 160, 0.08);
}
.chain-icon text,
.chain-icon::after {
width: 34rpx;
height: 20rpx;
border: 6rpx solid currentColor;
border-radius: 999rpx;
box-sizing: border-box;
display: block;
}
.chain-icon {
gap: 0;
transform: rotate(-36deg);
}
.chain-icon::after {
content: "";
margin-left: -10rpx;
}
.doc-icon {
font-size: 24rpx;
}
.bot-icon text {
width: 48rpx;
height: 34rpx;
display: block;
border-radius: 16rpx;
background: currentColor;
}
.bot-icon text::before,
.bot-icon text::after {
content: "";
display: block;
}
.flow-title {
margin-top: 18rpx;
color: #141720;
font-size: 22rpx;
font-weight: 800;
line-height: 1.2;
}
.flow-desc {
margin-top: 10rpx;
color: #888fa5;
font-size: 19rpx;
line-height: 1.2;
white-space: nowrap;
}
.flow-arrow {
width: 28rpx;
height: 4rpx;
border-radius: 999rpx;
background: #c1c2ec;
align-self: center;
margin-bottom: 50rpx;
}
.flow-arrow::after {
content: "";
display: block;
width: 12rpx;
height: 12rpx;
border-top: 4rpx solid #c1c2ec;
border-right: 4rpx solid #c1c2ec;
transform: rotate(45deg) translate(8rpx, -9rpx);
}
.sample-card {
/* min-height: 124rpx; */
border-radius: 18rpx;
/* padding: 18rpx 28rpx 18rpx 24rpx; */
display: flex;
align-items: center;
/* gap: 20rpx; */
background: linear-gradient(100deg, #f0f4ff 0%, #fbfcff 100%);
}
.gift-box {
width: 88rpx;
height: 86rpx;
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
filter: drop-shadow(0 8rpx 14rpx rgba(112, 111, 255, 0.22));
}
.gift-top {
width: 72rpx;
height: 18rpx;
border-radius: 7rpx 7rpx 3rpx 3rpx;
background: linear-gradient(90deg, #dfe8ff, #7874ff, #dfe8ff);
}
.gift-body {
width: 62rpx;
height: 54rpx;
border-radius: 4rpx 4rpx 9rpx 9rpx;
background: linear-gradient(90deg, #d7e3ff 0 35%, #8777ff 35% 52%, #c8d9ff 52% 100%);
}
.sample-copy {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.sample-title {
color: #141721;
font-size: 26rpx;
font-weight: 800;
line-height: 1.1;
}
.sample-desc {
color: #7f859b;
font-size: 20rpx;
line-height: 1.2;
white-space: nowrap;
}
.sample-btn {
width: 150rpx;
height: 62rpx;
border-radius: 999rpx;
flex-shrink: 0;
font-size: 23rpx;
font-weight: 800;
}
.tabbar {
height: 110rpx;
padding-bottom: env(safe-area-inset-bottom);
display: flex;
align-items: center;
justify-content: space-around;
background: rgba(255, 255, 255, 0.96);
border-top: 1rpx solid rgba(219, 224, 241, 0.86);
box-sizing: content-box;
}
.tab-item {
width: 170rpx;
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
color: #626777;
font-size: 21rpx;
font-weight: 600;
}
.tab-item.active {
color: #665cff;
font-weight: 800;
}
.tab-icon {
width: 34rpx;
height: 34rpx;
border-radius: 9rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 22rpx;
line-height: 1;
color: currentColor;
box-sizing: border-box;
}
.tab-item.active .tab-icon {
color: #ffffff;
background: linear-gradient(135deg, #536dff, #7b55ff);
}
.record-icon {
border: 4rpx solid currentColor;
border-radius: 9rpx;
}
.record-icon text {
width: 0;
height: 0;
display: block;
border-top: 7rpx solid transparent;
border-bottom: 7rpx solid transparent;
border-left: 11rpx solid currentColor;
margin-left: 3rpx;
}
.mine-icon {
border: 4rpx solid currentColor;
border-radius: 50%;
}
.home-icon text {
width: 14rpx;
height: 14rpx;
display: block;
border-radius: 4rpx;
background: currentColor;
transform: rotate(45deg);
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
+4
View File
@@ -0,0 +1,4 @@
{
"usingComponents": {
}
}
+21
View File
@@ -0,0 +1,21 @@
// logs.ts
// const util = require('../../utils/util.js')
import { formatTime } from '../../utils/util'
Component({
data: {
logs: [],
},
lifetimes: {
attached() {
this.setData({
logs: (wx.getStorageSync('logs') || []).map((log: string) => {
return {
date: formatTime(new Date(log)),
timeStamp: log
}
}),
})
}
},
})
+6
View File
@@ -0,0 +1,6 @@
<!--logs.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
<block wx:for="{{logs}}" wx:key="timeStamp" wx:for-item="log">
<view class="log-item">{{index + 1}}. {{log.date}}</view>
</block>
</scroll-view>
+16
View File
@@ -0,0 +1,16 @@
page {
height: 100vh;
display: flex;
flex-direction: column;
}
.scrollarea {
flex: 1;
overflow-y: hidden;
}
.log-item {
margin-top: 20rpx;
text-align: center;
}
.log-item:last-child {
padding-bottom: env(safe-area-inset-bottom);
}
+8
View File
@@ -0,0 +1,8 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav",
"vip-popup": "/components/vip-popup/vip-popup",
"checkin-popup": "/components/checkin-popup/checkin-popup"
},
"navigationStyle": "custom"
}
+80
View File
@@ -0,0 +1,80 @@
import { customEvent } from "../../utils/event"
Page({
data: {
showVipPopup: false,
showCheckinPopup: false,
menus: [
{ key: 'feedback', title: '意见反馈', icon: '/static/images/my/my_icon_8.png' },
{ key: 'privacy', title: '帮助中心', icon: '/static/images/my/my_icon_3.png' },
{ key: 'privacy', title: '隐私协议', icon: '/static/images/my/my_icon_7.png' },
],
userinfo: {
nickname: '创作小助手',
id: '10010',
avatar: 'https://cdn.zhuangb123.com/avatar/boy/a2cfe723-98ddc-12de-723b8f51b-71b1e3.jpeg',
description: 'AI 创作,让想象力触手可及 ✨'
}
},
onLoad() {
this.setData({
userinfo: wx.getStorageSync('userInfo')
})
customEvent.on('update:userinfo', this.onResetUserinfo)
},
onUnload() {
customEvent.off('update:userinfo', this.onResetUserinfo)
},
onResetUserinfo() {
this.setData({ userinfo: wx.getStorageSync('userInfo') })
},
handleEdit() {
wx.navigateTo({
url: '/pages/myPackage/edit/index',
})
},
handleCheckin() {
this.setData({ showCheckinPopup: true })
},
handleCheckinClose() {
this.setData({ showCheckinPopup: false })
},
handleCheckinRule() {
wx.showToast({ title: '每日签到可领取金币', icon: 'none' })
},
handleAddCoin() {
wx.navigateTo({
url: '/pages/myPackage/golb/index',
})
},
handleOpenVip() {
this.setData({ showVipPopup: true })
},
handleVipClose() {
this.setData({ showVipPopup: false })
},
handleVipConfirm(e: WechatMiniprogram.CustomEvent) {
this.setData({ showVipPopup: false })
const plan = e.detail.plan
wx.showToast({ title: plan ? '已选择' + plan.name : '立即开通', icon: 'none' })
},
handleMenuTap(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key
const routeMap: Record<string, string> = {
feedback: '/pages/myPackage/yjfk/index',
privacy: '/pages/myPackage/ysxy/index',
}
const url = routeMap[String(key || '')]
if (url) {
wx.navigateTo({ url })
}
},
})
+64
View File
@@ -0,0 +1,64 @@
<view class="profile-page">
<custom-nav padding-x="38">
<view class="profile-nav">
<text class="nav-title">个人中心</text>
</view>
</custom-nav>
<view class="profile-content">
<view class="user-section">
<view class="avatar">
<image src="{{userinfo.avatar}}"></image>
</view>
<view class="user-info">
<view class="name-row">
<text class="user-name">{{userinfo.nickname}}</text>
<text class="level-badge">普通用户</text>
</view>
<text class="user-id">ID{{userinfo.id}}</text>
<text class="user-desc">{{userinfo.description}}</text>
</view>
<view class="user-actions">
<view class="outline-action" bindtap="handleEdit">
<!-- <text class="edit-icon"></text> -->
<image src="../../static/images/my/edit.png" style="width: 24rpx;height: 24rpx;"></image>
<text>编辑资料</text>
</view>
<view class="outline-action checkin" bindtap="handleCheckin">
<image src="../../static/images/my/Signin.png" style="width: 24rpx;height: 24rpx;"></image>
<text>签到领金币</text>
</view>
</view>
</view>
<view class="coin-card" bind:tap="handleAddCoin">
<image src="/static/images/my/my_icon_1.png"
style="width: 700rpx;height: 186rpx;position: absolute;left: 0;top: 0;"></image>
<view class="coin-copy">
<text>{{userinfo.diamond}}</text>
<image src="/static/images/my/add.png" style="width: 40rpx;height: 40rpx;"></image>
</view>
</view>
<view class="vip-card" bindtap="handleOpenVip">
<image class="crown-icon icon-placeholder" src="/static/images/my/my_icon_2.png" mode="widthFix"></image>
<!-- <view class="vip-left">
<image class="crown-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text class="vip-title">会员中心</text>
</view>
<text class="vip-desc">解锁更多创作权益,生成更鲨金币</text>
<view class="vip-btn" bindtap="handleOpenVip">开通会员</view> -->
</view>
<view class="menu-card">
<view wx:for="{{menus}}" wx:key="key" class="menu-item" bindtap="handleMenuTap" data-key="{{item.key}}">
<image src="{{item.icon}}" style="width: 38rpx;height: 38rpx;"></image>
<text class="menu-title">{{item.title}}</text>
<text class="menu-arrow"></text>
</view>
</view>
</view>
<vip-popup visible="{{showVipPopup}}" bind:close="handleVipClose" bind:confirm="handleVipConfirm"></vip-popup>
<checkin-popup visible="{{showCheckinPopup}}" bind:close="handleCheckinClose" bind:rule="handleCheckinRule">
</checkin-popup>
</view>
+462
View File
@@ -0,0 +1,462 @@
page {
min-height: 100vh;
background: #f8f8ff;
color: #111521;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.profile-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 80% 6%, rgba(129, 93, 255, 0.22), transparent 20%),
radial-gradient(circle at 18% 9%, rgba(116, 118, 255, 0.16), transparent 24%),
linear-gradient(180deg, #f1efff 0%, #fbfbff 36%, #ffffff 100%);
}
.profile-nav {
width: 100%;
display: flex;
align-items: center;
}
.nav-title {
font-size: 31rpx;
font-weight: 800;
color: #090c15;
letter-spacing: 0;
}
.profile-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 20rpx;
padding: 34rpx 27rpx 28rpx;
box-sizing: border-box;
}
.user-section {
min-height: 136rpx;
display: flex;
align-items: center;
gap: 26rpx;
}
.avatar {
width: 94rpx;
height: 94rpx;
border-radius: 50%;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
/* background: linear-gradient(135deg, #f4c2d0, #b985ff); */
box-shadow: 0 10rpx 18rpx rgba(136, 95, 186, 0.18);
}
.avatar image {
width: 100%;
height: 100%;
}
.user-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 10rpx;
}
.name-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.user-name {
font-size: 30rpx;
line-height: 1.2;
font-weight: 900;
color: #111521;
}
.level-badge {
height: 30rpx;
padding: 0 14rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
color: #ffffff;
font-size: 18rpx;
font-weight: 800;
background: linear-gradient(135deg, #5c7cff, #7e39eb);
}
.user-id,
.user-desc {
color: #72727c;
font-size: 21rpx;
line-height: 1.2;
white-space: nowrap;
}
.user-actions {
width: 176rpx;
display: flex;
flex-direction: column;
gap: 14rpx;
flex-shrink: 0;
}
.outline-action {
height: 52rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
color: #664cff;
font-size: 21rpx;
font-weight: 800;
background: rgba(255, 255, 255, 0.72);
border: 2rpx solid #8b72ff;
box-sizing: border-box;
}
.checkin {
border-color: transparent;
background: rgba(255, 255, 255, 0.88);
box-shadow: 0 6rpx 14rpx rgba(122, 102, 255, 0.08);
}
.edit-icon {
width: 20rpx;
height: 20rpx;
display: block;
border-left: 4rpx solid currentColor;
border-bottom: 4rpx solid currentColor;
transform: rotate(-45deg);
box-sizing: border-box;
}
.calendar-icon {
width: 22rpx;
height: 24rpx;
display: block;
border: 3rpx solid currentColor;
border-radius: 5rpx;
box-sizing: border-box;
}
.coin-card {
min-height: 180rpx;
margin-top: 26rpx;
padding: 30rpx 38rpx;
border-radius: 22rpx;
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
overflow: hidden;
background:
radial-gradient(circle at 78% 50%, rgba(255, 210, 105, 0.18), transparent 26%),
linear-gradient(100deg, #fffdf9 0%, #fff8eb 100%);
border: 1rpx solid #f1d8a9;
box-shadow: 0 8rpx 18rpx rgba(159, 117, 46, 0.06);
box-sizing: border-box;
}
.coin-copy {
display: flex;
flex-direction: column;
gap: 20rpx;
position: absolute;
left: 100rpx;
top: 80rpx;
display: flex;
flex-direction: row;
align-items: center;
}
.coin-copy text:first-child{
font-size: 50rpx;
font-weight: bold;
line-height: 1.2;
}
.coin-copy image{
margin-top: -4rpx;
}
.coin-title {
font-size: 24rpx;
font-weight: 700;
color: #141925;
}
.coin-row {
display: flex;
align-items: center;
gap: 14rpx;
}
.coin-symbol,
.coin-add {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-weight: 900;
background: linear-gradient(135deg, #ffd46a, #ff9900);
box-shadow: inset 0 0 0 3rpx rgba(255, 255, 255, 0.42), 0 6rpx 12rpx rgba(245, 154, 16, 0.2);
}
.coin-symbol {
font-size: 18rpx;
}
.coin-count {
font-size: 50rpx;
line-height: 1;
font-weight: 900;
color: #111925;
}
.coin-add {
font-size: 32rpx;
}
.treasure-scene {
width: 284rpx;
height: 122rpx;
display: flex;
align-items: flex-end;
justify-content: center;
}
.treasure-box {
width: 116rpx;
height: 92rpx;
display: flex;
flex-direction: column;
align-items: center;
filter: drop-shadow(0 8rpx 12rpx rgba(127, 76, 216, 0.22));
}
.treasure-lid {
width: 120rpx;
height: 36rpx;
border-radius: 18rpx 18rpx 8rpx 8rpx;
background: linear-gradient(90deg, #ce79ff 0 24%, #ffc05d 24% 38%, #8d57e8 38% 76%, #ffc05d 76% 100%);
transform: skewX(8deg);
}
.treasure-body {
width: 104rpx;
height: 58rpx;
border-radius: 8rpx 8rpx 16rpx 16rpx;
background: linear-gradient(90deg, #7e55d9 0 28%, #ffb845 28% 44%, #9f62ef 44% 100%);
}
.coin-stack {
width: 74rpx;
height: 28rpx;
border-radius: 50%;
background: linear-gradient(180deg, #ffd366, #f39b12);
box-shadow: 10rpx -13rpx 0 -1rpx #ffc457, 24rpx 6rpx 0 -2rpx #f39b12;
}
.left-stack {
margin-right: -10rpx;
margin-bottom: 18rpx;
}
.right-stack {
margin-left: -8rpx;
margin-bottom: 8rpx;
}
.spark {
width: 22rpx;
height: 22rpx;
background: #ffc95d;
border-radius: 4rpx;
transform: rotate(45deg);
}
.spark-one {
margin-right: 18rpx;
margin-bottom: 74rpx;
}
.spark-two {
width: 14rpx;
height: 14rpx;
margin-right: 18rpx;
margin-bottom: 88rpx;
}
.vip-card {
/* height: 126rpx; */
/* padding: 20rpx 16rpx 20rpx 24rpx; */
/* border-radius: 16rpx; */
/* display: flex;
align-items: center; */
/* gap: 22rpx; */
/* background: linear-gradient(100deg, #fffdf8, #fff8ea); */
/* border: 1rpx solid #f0d8aa; */
box-sizing: border-box;
}
.vip-left {
display: flex;
align-items: center;
gap: 16rpx;
flex-shrink: 0;
}
.crown-icon {
width: 100%;
/* height: 100%; */
}
.crown-icon text {
flex: 1;
height: 28rpx;
border-radius: 3rpx 3rpx 7rpx 7rpx;
background: linear-gradient(180deg, #ffd45d, #f8a50a);
}
.crown-icon text:nth-child(2) {
height: 38rpx;
}
.vip-title {
font-size: 28rpx;
font-weight: 900;
color: #151925;
}
.vip-desc {
flex: 1;
min-width: 0;
font-size: 20rpx;
line-height: 1.2;
color: #9a5a1f;
white-space: nowrap;
}
.vip-btn {
width: 132rpx;
height: 54rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #7b4b12;
font-size: 23rpx;
font-weight: 900;
background: linear-gradient(135deg, #fff0bd, #ffd886);
flex-shrink: 0;
}
.menu-card {
margin-top: 8rpx;
padding: 20rpx 28rpx;
border-radius: 18rpx;
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 8rpx 22rpx rgba(76, 84, 135, 0.06);
box-sizing: border-box;
}
.menu-item {
min-height: 96rpx;
display: flex;
align-items: center;
gap: 22rpx;
}
.menu-icon {
width: 46rpx;
height: 46rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: #ffffff;
}
.feedback-icon {
background: linear-gradient(135deg, #ed8aff, #9d5cff);
}
.help-icon {
background: linear-gradient(135deg, #7fb8ff, #4e7dff);
}
.privacy-icon {
background: linear-gradient(135deg, #31dec2, #15bd96);
}
.feedback-icon text {
width: 24rpx;
height: 18rpx;
border-radius: 6rpx;
display: block;
background: currentColor;
color: rgba(255, 255, 255, 0.92);
box-shadow: inset 7rpx 0 0 rgba(255, 255, 255, 0.2), inset 14rpx 0 0 rgba(255, 255, 255, 0.35);
}
.help-icon text {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
display: block;
border: 5rpx solid rgba(255, 255, 255, 0.92);
box-sizing: border-box;
}
.privacy-icon text {
width: 22rpx;
height: 26rpx;
border-radius: 5rpx 5rpx 10rpx 10rpx;
display: block;
background: rgba(255, 255, 255, 0.94);
}
.menu-title {
flex: 1;
font-size: 25rpx;
font-weight: 700;
color: #151925;
}
.menu-arrow {
width: 18rpx;
height: 18rpx;
border-top: 4rpx solid #bdc1ca;
border-right: 4rpx solid #bdc1ca;
transform: rotate(45deg);
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,3 @@
{
"usingComponents": {}
}
+66
View File
@@ -0,0 +1,66 @@
// pages/myPackage/bzzx/index.ts
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
@@ -0,0 +1,2 @@
<!--pages/myPackage/bzzx/index.wxml-->
<text>pages/myPackage/bzzx/index.wxml</text>
@@ -0,0 +1 @@
/* pages/myPackage/bzzx/index.wxss */
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+81
View File
@@ -0,0 +1,81 @@
import { client } from "../../../utils/api";
import { updateuserinfo, userInfo } from "../../../utils/request";
Page({
data: {
userinfo: {
nickname: '创作小助手',
id: '10010',
avatar: 'https://cdn.zhuangb123.com/avatar/boy/a2cfe723-98ddc-12de-723b8f51b-71b1e3.jpeg',
description: 'AI 创作,让想象力触手可及 ✨'
},
oldAvatar: '',
newAvatar: ''
},
onLoad() {
this.setData({ userinfo: wx.getStorageSync('userInfo') });
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/my/my' }),
})
},
handleChangeAvatar() {
let that = this
wx.chooseImage({
count: 1,
success(res) {
let userInfo = that.data.userinfo;
let oldAvatar = userInfo.avatar;
userInfo.avatar = res.tempFilePaths[0];
that.setData({
userinfo: userInfo,
oldAvatar: oldAvatar
})
}
});
},
handleNicknameInput(e: any) {
let userinfo = this.data.userinfo;
userinfo.nickname = e.detail.value;
this.setData({
userinfo: userinfo,
})
},
handleSave() {
let nickname = this.data.userinfo;
if (nickname.nickname.length < 2) {
wx.showToast({ title: '昵称至少2个字符', icon: 'none' })
return
}
let that = this;
let update = () => {
updateuserinfo(nickname.nickname, nickname.avatar).then((res: any) => {
if (!res.code) {
userInfo().then((res: any) => {
wx.setStorageSync('userInfo', res.data);
that.setData({ userinfo: res.data });
})
}
})
}
if (this.data.userinfo.avatar.indexOf('http://tmp/') === -1) {
update();
return;
}
client.upload(this.data.userinfo.avatar, {}).then((res: any) => {
if (res.code == 0) {
nickname.avatar = res.data.url;
update();
} else {
wx.showModal({ title: '温馨提示', content: '头像上传失败' })
}
})
},
})
@@ -0,0 +1,58 @@
<view class="edit-page">
<custom-nav padding-x="42">
<view class="edit-nav">
<view class="nav-back" bindtap="handleBack">
<text></text>
</view>
<text class="nav-title">编辑资料</text>
<view class="nav-placeholder"></view>
</view>
</custom-nav>
<view class="edit-content">
<view class="edit-card">
<view class="section-title">
<text class="section-mark"></text>
<text>头像</text>
</view>
<view class="avatar-row">
<view class="avatar-wrap">
<view class="avatar-large">
<image src="{{userinfo.avatar}}"></image>
</view>
<view class="camera-badge">
<image class="camera-icon icon-placeholder" src="/static/images/my/caram.png" mode="aspectFit"></image>
</view>
</view>
<view class="change-avatar" bindtap="handleChangeAvatar">
<image class="image-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>更换头像</text>
</view>
</view>
<view class="divider"></view>
<view class="section-title nickname-title">
<text class="section-mark"></text>
<text>昵称</text>
</view>
<view class="input-shell">
<input class="nickname-input" value="{{userinfo.nickname}}" maxlength="12" placeholder="请输入昵称" bindinput="handleNicknameInput" />
<image class="edit-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
</view>
<text class="input-tip">212个字符</text>
<view class="card-spacer"></view>
<view class="sync-tip">
<image class="shield-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>修改后将同步更新个人中心信息</text>
</view>
<view class="save-btn" bindtap="handleSave">保存修改</view>
</view>
</view>
</view>
+299
View File
@@ -0,0 +1,299 @@
page {
min-height: 100vh;
background: #f8f6ff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.edit-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 82% 4%, rgba(137, 95, 255, 0.18), transparent 20%),
radial-gradient(circle at 16% 10%, rgba(118, 129, 255, 0.14), transparent 24%),
linear-gradient(180deg, #f4f1ff 0%, #fbfaff 42%, #ffffff 100%);
}
.edit-nav {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back,
.nav-placeholder {
width: 56rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
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: 32rpx;
line-height: 1.2;
font-weight: 900;
color: #101522;
}
.edit-content {
flex: 1;
padding: 50rpx 32rpx 78rpx;
box-sizing: border-box;
display: flex;
}
.edit-card {
width: 100%;
min-height: 1074rpx;
padding: 54rpx 40rpx 50rpx;
border-radius: 34rpx;
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 16rpx 42rpx rgba(91, 84, 142, 0.08);
box-sizing: border-box;
}
.section-title {
display: flex;
align-items: center;
gap: 14rpx;
color: #111827;
font-size: 30rpx;
line-height: 1.2;
font-weight: 900;
}
.section-mark {
width: 7rpx;
height: 28rpx;
border-radius: 999rpx;
background: linear-gradient(180deg, #aa6dff, #6f67ff);
}
.avatar-row {
margin-top: 32rpx;
padding: 0 44rpx 0 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.avatar-wrap {
width: 246rpx;
height: 246rpx;
display: flex;
align-items: center;
justify-content: center;
}
.avatar-large {
width: 222rpx;
height: 222rpx;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: linear-gradient(135deg, #f3e0d7, #fff7f4);
/* border: 4rpx solid #e6dcff; */
box-shadow: inset 0 0 0 5rpx #ffffff;
}
.avatar-large>image {
width: 100%;
height: 100%;
}
.camera-badge {
width: 58rpx;
height: 58rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
/* background: linear-gradient(135deg, #9a6cff, #7656ef); */
background: #ffffff;
box-shadow: 0 8rpx 18rpx rgba(115, 83, 238, 0.24), 0 0 0 8rpx #ffffff;
margin-left: -70rpx;
margin-top: 142rpx;
}
.camera-icon {
width: 50rpx;
height: 50rpx;
display: block;
border-radius: 6rpx;
/* border: 4rpx solid #ffffff; */
box-sizing: border-box;
}
/* .camera-icon::before {
content: "";
display: block;
width: 10rpx;
height: 10rpx;
margin: 2rpx auto 0;
border-radius: 50%;
background: #ffffff;
} */
.change-avatar {
width: 220rpx;
height: 58rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 14rpx;
color: #7d5cff;
font-size: 24rpx;
font-weight: 800;
border: 2rpx solid #8d6cff;
background: rgba(255, 255, 255, 0.72);
box-sizing: border-box;
}
.image-icon {
width: 22rpx;
height: 22rpx;
display: block;
border: 4rpx solid currentColor;
border-radius: 3rpx;
box-sizing: border-box;
}
.image-icon::after {
content: "";
display: block;
width: 9rpx;
height: 9rpx;
margin: 5rpx 0 0 6rpx;
border-left: 4rpx solid currentColor;
border-top: 4rpx solid currentColor;
transform: rotate(45deg);
}
.divider {
height: 1rpx;
margin: 42rpx 0 54rpx;
background: #e5e7ef;
}
.nickname-title {
margin-bottom: 32rpx;
}
.input-shell {
height: 86rpx;
padding: 0 30rpx;
border-radius: 18rpx;
display: flex;
align-items: center;
gap: 18rpx;
border: 2rpx solid #ddd7ff;
background: rgba(255, 255, 255, 0.82);
box-sizing: border-box;
}
.nickname-input {
flex: 1;
min-width: 0;
height: 82rpx;
color: #111827;
font-size: 27rpx;
font-weight: 700;
}
.edit-icon {
width: 24rpx;
height: 24rpx;
display: block;
border-left: 5rpx solid #9a79ff;
border-bottom: 5rpx solid #9a79ff;
transform: rotate(-45deg);
box-sizing: border-box;
}
.input-tip {
margin-top: 18rpx;
color: #8b90a2;
font-size: 20rpx;
line-height: 1.2;
}
.card-spacer {
flex: 1;
min-height: 210rpx;
}
.sync-tip {
display: flex;
align-items: center;
justify-content: center;
gap: 14rpx;
color: #9095a5;
font-size: 20rpx;
line-height: 1.2;
}
.shield-icon {
width: 24rpx;
height: 26rpx;
border-radius: 6rpx 6rpx 12rpx 12rpx;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #a56dff, #715eff);
}
.shield-icon::after {
content: "";
width: 9rpx;
height: 5rpx;
display: block;
border-left: 3rpx solid #ffffff;
border-bottom: 3rpx solid #ffffff;
transform: rotate(-45deg);
}
.save-btn {
height: 86rpx;
margin-top: 28rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 32rpx;
font-weight: 900;
background: linear-gradient(135deg, #b26bff 0%, #7549f5 100%);
box-shadow: 0 14rpx 26rpx rgba(117, 73, 245, 0.2);
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+112
View File
@@ -0,0 +1,112 @@
import { diamonds, diamondVirtualPayment, updatesessionkey, updateuserinfo, userInfo } from '../../../utils/request';
interface CoinPackage {
id: number
coins: number
price: number
ribbon?: string
ribbonClass?: string
artClass: string
chest?: boolean
selected: boolean
}
Page({
data: {
packages: [],
selectedPackage: null as CoinPackage | null,
userInfo: { diamond: 0 }
},
onLoad() {
this.setData({ userInfo: wx.getStorageSync('userInfo') })
diamonds().then((res: any) => {
if (!res.code) {
this.setData({ packages: res.data });
}
})
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/my/my' }),
})
},
handlePackageTap(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id
const selectedPackage = this.data.packages.find((item: CoinPackage) => item.id === id) || this.data.selectedPackage
this.setData({
selectedPackage,
})
},
handleBuy() {
const that = this;
if (that.data.selectedPackage == null) {
return;
}
wx.showLoading({ title: '处理中, 请稍后.', mask: true });
wx.checkSession({
success() {
that.payment();
},
fail() {
wx.login({
success(login) {
updatesessionkey(login.code).then((res: any) => {
if (!res.code) {
that.payment();
} else {
wx.showModal({
title: '温馨提示',
content: res.message,
showCancel: false
})
}
})
}
})
wx.hideLoading();
}
})
},
payment() {
let that = this;
diamondVirtualPayment((that.data.selectedPackage || { id: 0 }).id).then((res: any) => {
wx.requestVirtualPayment({
signData: JSON.stringify(res.param.signData),
paySig: res.param.paySig,
signature: res.param.signature,
mode: res.param.mode,
success(result: any) {
wx.hideLoading();
console.log(result)
userInfo().then((res: any) => {
if (!res.code) {
wx.setStorageSync('userInfo', res.data);
that.setData({ userInfo: res.data });
}
})
},
fail(error: any) {
wx.hideLoading();
console.log(error);
wx.showModal({
title: '温馨提示',
content: '支付失败',
showCancel: false
})
}
});
}).catch(() => {
wx.hideLoading();
wx.showModal({
title: '温馨提示',
content: '系统繁忙, 请稍后再试',
showCancel: false
})
})
}
})
@@ -0,0 +1,71 @@
<view class="coin-page">
<custom-nav padding-x="42">
<view class="coin-nav">
<view class="nav-back" bindtap="handleBack"><text></text></view>
<text class="nav-title">购买金币</text>
<view class="nav-placeholder"></view>
</view>
</custom-nav>
<view class="coin-content">
<view class="balance-card">
<image class="balance-art icon-placeholder" src="/static/images/icons/frame_1.png" mode="widthFix"></image>
<view class="balance-copy">{{userInfo.diamond}}</view>
</view>
<view class="section-head">
<text class="section-title">金币套餐</text>
<view class="section-note">
<image class="info-icon icon-placeholder" src="/static/images/info-circle.png" mode="aspectFit"></image>
<text>套餐数量与价格以实际配置为准</text>
</view>
</view>
<view class="package-grid">
<view
wx:for="{{packages}}"
wx:key="id"
class="package-card {{selectedPackage.id == item.id ? 'selected' : ''}}"
data-id="{{item.id}}"
bindtap="handlePackageTap"
>
<view wx:if="{{item.ribbon}}" class="ribbon {{item.ribbonClass}}">{{item.ribbon}}</view>
<text class="package-name">{{item.coins}} 金币</text>
<image class="package-art {{item.artClass}} icon-placeholder" src="{{item.icon}}" mode="widthFix"></image>
<view class="package-price">
<text>¥</text>
<text>{{item.price}}</text>
</view>
<view wx:if="{{item.selected}}" class="selected-check"><text></text></view>
<view wx:if="{{item.tag == 'best_seller'}}" class="right-jiao best_seller">热卖</view>
<view wx:if="{{item.tag == 'hot'}}" class="right-jiao">爆款</view>
<view wx:if="{{item.tag == 'recommend'}}" class="right-jiao recommend">推荐</view>
</view>
</view>
<view class="tips-card">
<view class="tip-row">
<image class="tip-icon flash-tip icon-placeholder" src="/static/images/icons/frame_7.png" mode="aspectFit"></image>
<text>充值后金币实时到账</text>
</view>
<view class="tip-row">
<image class="tip-icon shield-tip icon-placeholder" src="/static/images/icons/frame_.png" mode="aspectFit"></image>
<text>金币仅用于平台内数字人视频相关功能</text>
</view>
</view>
<view class="selected-bar">
<view class="selected-left">
<image class="selected-icon icon-placeholder" src="/static/images/icons/check-circle-filled.png" mode="aspectFit"></image>
<text>已选:</text>
<text class="selected-coins">{{selectedPackage.coin || 0}}</text>
<text>金币</text>
</view>
<text class="selected-price">¥{{selectedPackage.price || 0.00}}</text>
</view>
<view class="buy-btn" bindtap="handleBuy">立即购买</view>
</view>
</view>
+509
View File
@@ -0,0 +1,509 @@
page {
min-height: 100vh;
background: #fbfaff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.coin-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 80% 5%, rgba(138, 103, 255, 0.16), transparent 20%),
radial-gradient(circle at 18% 8%, rgba(115, 129, 255, 0.12), transparent 24%),
linear-gradient(180deg, #f9f6ff 0%, #fbfbff 42%, #ffffff 100%);
}
.coin-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;
line-height: 1.2;
font-weight: 900;
color: #111827;
}
.coin-content {
flex: 1;
display: flex;
flex-direction: column;
padding: 24rpx;
box-sizing: border-box;
}
.balance-card {
position: relative;
/* height: 198rpx; */
/* padding: 36rpx 38rpx; */
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
/* overflow: hidden; */
/* background:
radial-gradient(circle at 82% 46%, rgba(126, 83, 255, 0.22), transparent 30%),
linear-gradient(100deg, #ffffff 0%, #eee8ff 100%); */
/* border: 1rpx solid #ded6ff; */
box-shadow: 0 10rpx 22rpx rgba(103, 86, 176, 0.1);
box-sizing: border-box;
}
.balance-copy {
display: flex;
flex-direction: column;
position: absolute;
z-index: 2026;
font-weight: bold;
font-size: 72rpx;
left: 110rpx;
bottom: 45rpx;
}
.balance-title {
font-size: 24rpx;
font-weight: 700;
color: #111827;
}
.balance-row {
display: flex;
align-items: center;
gap: 18rpx;
}
.coin-icon {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 20rpx;
font-weight: 900;
background: linear-gradient(135deg, #ffd56b, #ff9900);
box-shadow: inset 0 0 0 4rpx rgba(255, 255, 255, 0.38), 0 7rpx 13rpx rgba(248, 154, 12, 0.2);
}
.balance-count {
font-size: 68rpx;
line-height: 1;
font-weight: 900;
color: #0e1118;
}
.balance-art {
width: 100%;
height: 100%;
display: flex;
align-items: flex-end;
justify-content: center;
/* position: absolute; */
/* left: 0;
top: 0; */
}
.open-box {
width: 142rpx;
height: 104rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
}
.box-lid {
width: 142rpx;
height: 44rpx;
border-radius: 24rpx 24rpx 8rpx 8rpx;
background: linear-gradient(135deg, rgba(196, 163, 255, 0.55), rgba(138, 89, 255, 0.28));
transform: rotate(6deg) translateY(12rpx);
}
.box-body {
width: 126rpx;
height: 60rpx;
border-radius: 12rpx 12rpx 28rpx 28rpx;
background: linear-gradient(135deg, rgba(126, 83, 255, 0.28), rgba(196, 163, 255, 0.5));
box-shadow: 0 8rpx 18rpx rgba(126, 83, 255, 0.18);
}
.big-coin {
width: 76rpx;
height: 76rpx;
margin-top: -108rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #ffe28b, #ffac19);
box-shadow: inset 0 0 0 8rpx rgba(255, 255, 255, 0.34), 0 10rpx 18rpx rgba(255, 172, 25, 0.24);
}
.big-coin text {
width: 34rpx;
height: 34rpx;
border-radius: 8rpx;
background: rgba(255, 255, 255, 0.8);
transform: rotate(45deg);
}
.art-spark {
width: 30rpx;
height: 30rpx;
border-radius: 5rpx;
background: #ffffff;
transform: rotate(45deg);
}
.spark-one {
margin-right: -12rpx;
margin-bottom: 98rpx;
}
.spark-two {
width: 18rpx;
height: 18rpx;
margin-left: -4rpx;
margin-bottom: 46rpx;
background: #8d73ff;
}
.section-head {
margin-top: 34rpx;
margin-bottom: 22rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.section-title {
font-size: 27rpx;
font-weight: 900;
color: #121722;
}
.section-note {
display: flex;
align-items: center;
gap: 8rpx;
color: #85899a;
font-size: 20rpx;
white-space: nowrap;
}
.info-icon {
width: 18rpx;
height: 18rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid currentColor;
font-size: 14rpx;
font-weight: 900;
}
.package-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22rpx 18rpx;
}
.package-card {
position: relative;
height: 230rpx;
border-radius: 22rpx;
padding: 28rpx 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
overflow: hidden;
background: rgba(255, 255, 255, 1);
box-shadow: 0 8rpx 20rpx rgba(82, 90, 146, 0.08);
border-width: 2rpx;
border-style: solid;
border-color: transparent;
}
.package-card.selected {
border-color: #7351ff;
background-color: rgba(196, 163, 255, 0.1);
}
.ribbon {
position: absolute;
top: 0;
left: 0;
width: 74rpx;
height: 42rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 18rpx;
font-weight: 800;
transform: rotate(-45deg) translate(-22rpx, -10rpx);
}
.red-ribbon {
background: #ff5b78;
}
.purple-ribbon {
background: #7452ff;
}
.gold-ribbon {
background: #ffb629;
}
.package-name {
color: #111827;
font-size: 24rpx;
line-height: 1.1;
font-weight: 800;
}
.package-art {
width: 82rpx;
margin-top: 18rpx;
display: flex;
align-items: flex-end;
justify-content: center;
}
.coin-pile {
display: flex;
align-items: flex-end;
justify-content: center;
}
.coin-pile text {
width: 54rpx;
height: 22rpx;
border-radius: 50%;
display: block;
background: linear-gradient(180deg, #ffe28b, #f8a113);
box-shadow: inset 0 0 0 4rpx rgba(255, 255, 255, 0.36), 0 8rpx 0 -2rpx #e98a0d;
}
.coin-pile text:nth-child(1) {
margin-right: -26rpx;
}
.coin-pile text:nth-child(2) {
margin-bottom: 26rpx;
}
.coin-pile text:nth-child(3) {
margin-left: -26rpx;
margin-bottom: 8rpx;
}
.large-pile .coin-pile text {
width: 66rpx;
height: 26rpx;
}
.mini-chest {
width: 74rpx;
height: 54rpx;
margin-left: -18rpx;
border-radius: 8rpx 8rpx 14rpx 14rpx;
background: linear-gradient(90deg, #7b56d9 0 28%, #ffbd47 28% 46%, #a166f0 46% 100%);
box-shadow: 0 8rpx 12rpx rgba(113, 83, 226, 0.18);
}
.package-price {
margin-top: auto;
display: flex;
align-items: baseline;
gap: 4rpx;
color: #6d4cff;
font-weight: 900;
}
.package-price text:first-child {
font-size: 24rpx;
}
.package-price text:last-child {
font-size: 32rpx;
}
.right-jiao {
position: absolute;
left: -90rpx;
top: -9rpx;
width: 100%;
height: 50rpx;
line-height: 70rpx;
background-color: #6d4cff;
z-index: 9999;
color: #ffffff;
text-align: center;
font-size: 18rpx;
transform: rotate(-45deg);
}
.right-jiao.best_seller {
background-color: orange;
}
.right-jiao.recommend{
background-color: red;
}
.selected-check {
position: absolute;
right: 12rpx;
bottom: 12rpx;
width: 38rpx;
height: 38rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: #6f4cff;
}
.selected-check text {
width: 16rpx;
height: 9rpx;
border-left: 4rpx solid #ffffff;
border-bottom: 4rpx solid #ffffff;
transform: rotate(-45deg);
}
.tips-card {
margin-top: 26rpx;
padding: 16rpx 24rpx;
border-radius: 18rpx;
display: flex;
flex-direction: column;
gap: 12rpx;
background: #f5f3ff;
color: #73788a;
font-size: 20rpx;
}
.tip-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.tip-icon {
width: 22rpx;
height: 22rpx;
border-radius: 50%;
display: block;
background: #6f4cff;
}
.shield-tip {
border-radius: 6rpx 6rpx 12rpx 12rpx;
}
.selected-bar {
height: 62rpx;
margin-top: 14rpx;
padding: 0 30rpx 0 24rpx;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #ffffff;
border: 1rpx solid #e4e2f2;
box-sizing: border-box;
}
.selected-left {
display: flex;
align-items: center;
gap: 8rpx;
color: #171b26;
font-size: 23rpx;
font-weight: 800;
}
.selected-icon {
width: 34rpx;
height: 34rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background: #6f4cff;
}
.selected-icon text {
width: 14rpx;
height: 8rpx;
border-left: 4rpx solid #ffffff;
border-bottom: 4rpx solid #ffffff;
transform: rotate(-45deg);
}
.selected-coins,
.selected-price {
color: #6f4cff;
font-weight: 900;
}
.selected-price {
font-size: 29rpx;
}
.buy-btn {
height: 68rpx;
margin-top: 12rpx;
border-radius: 14rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 29rpx;
font-weight: 800;
background: linear-gradient(135deg, #4c88ff 0%, #7b3df2 100%);
box-shadow: 0 10rpx 18rpx rgba(91, 83, 244, 0.2);
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,3 @@
{
"usingComponents": {}
}
+66
View File
@@ -0,0 +1,66 @@
// pages/myPackage/vip/index.ts
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
@@ -0,0 +1,2 @@
<!--pages/myPackage/vip/index.wxml-->
<text>pages/myPackage/vip/index.wxml</text>
@@ -0,0 +1 @@
/* pages/myPackage/vip/index.wxss */
@@ -0,0 +1,6 @@
{
"usingComponents": {
"custom-nav": "/components/custom-nav/custom-nav"
},
"navigationStyle": "custom"
}
+113
View File
@@ -0,0 +1,113 @@
import { client } from "../../../utils/api"
import { feedback, uploadavatar } from "../../../utils/request"
interface FeedbackType {
key: number
label: string
icon: string
active: boolean
}
interface submits {
categoryes: number[],
content: string,
images: string[],
phone: string
}
Page({
data: {
description: '',
contact: '',
types: [
{ key: 1, label: '功能问题', icon: '/static/images/my/debug.png', active: true },
{ key: 2, label: '体验优化', active: false, icon: '/static/images/my/star.png' },
{ key: 3, label: '内容建议', active: false, icon: '/static/images/my/msg.png' },
{ key: 4, label: '其他问题', active: false, icon: '/static/images/my/more.png' },
] as FeedbackType[],
datas: {} as submits
},
onLoad() {
this.setData({ datas: { categoryes: [], content: '', images: [], phone: '' } })
},
handleBack() {
wx.navigateBack({
fail: () => wx.switchTab({ url: '/pages/my/my' }),
})
},
handleRecord() {
wx.showToast({ title: '反馈记录', icon: 'none' })
},
handleTypeTap(e: WechatMiniprogram.TouchEvent) {
const key = e.currentTarget.dataset.key
this.setData({
types: this.data.types.map((item) => ({
...item,
active: item.key === key ? !item.active : item.active,
})),
})
},
handleDescriptionInput(e: any) {
this.setData({ description: e.detail.value })
},
handleContactInput(e: any) {
this.setData({ contact: e.detail.value })
},
handleUpload() {
let that = this;
wx.showLoading({ mask: true, title: '请稍后...' })
wx.chooseMedia({
count: 3 - this.data.datas.images.length,
mediaType: ['image', 'video'],
sourceType: ['album', 'camera'],
maxDuration: 30,
camera: 'back',
success(res) {
client.upload(res.tempFiles[0].tempFilePath, {}).then((res: any) => {
wx.hideLoading();
if (res.code == 0) {
let images = that.data.datas;
images.images.push(res.data.url);
that.setData({ datas: images })
}
})
},
fail(err) {
wx.hideLoading();
console.log(err);
}
})
},
handleSubmit() {
let that = this;
if (!this.data.description.trim()) {
wx.showToast({ title: '请填写问题描述', icon: 'none' })
return
}
let datas = this.data.datas;
let types: FeedbackType[] = [];
for (let q = 0; q < this.data.types.length; q++) {
let item = that.data.types[q];
if (item.active) {
datas.categoryes.push(item.key)
}
item.active = false;
types.push(item);
}
datas.content = this.data.description;
datas.phone = this.data.contact;
feedback(JSON.stringify(datas)).then((res: any) => {
if (!res.code) {
this.setData({ datas: { categoryes: [], content: '', images: [], phone: '' }, contact: '', description: '', types: types })
wx.showModal({ title: '温馨提示', content: '提交成功', showCancel: false });
} else {
wx.showModal({ title: '温馨提示', content: '提交失败, 请稍后重试', showCancel: false });
}
})
},
})
@@ -0,0 +1,86 @@
<view class="feedback-page">
<custom-nav padding-x="42">
<view class="feedback-nav">
<view class="nav-back" bindtap="handleBack">
<text></text>
</view>
<text class="nav-title">意见反馈</text>
<view class="record-link" bindtap="handleRecord"></view>
</view>
</custom-nav>
<view class="feedback-content">
<view class="hero-row">
<image class="hero-illustration" src="/static/images/my/yjfk_top.png" mode="widthFix"></image>
<!-- <view class="hero-copy">
<view class="hero-title">
<text>你的意见对我们很</text>
<text class="accent">重要</text>
</view>
<text class="hero-desc">欢迎提出宝贵的建议</text>
<text class="hero-desc">帮助我们做得更好</text>
<view class="hero-line"></view>
</view> -->
</view>
<view class="form-card type-card">
<view class="card-title">
<image class="title-icon type-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit"></image>
<text>反馈类型</text>
<text class="optional">(可多选)</text>
</view>
<view class="type-grid">
<view wx:for="{{types}}" wx:key="key" class="type-chip {{item.active ? 'active' : 'gray'}}"
data-key="{{item.key}}" bindtap="handleTypeTap">
<image class="type-chip-icon icon-placeholder" src="{{item.icon}}" mode="aspectFit"></image>
<text>{{item.label}}</text>
</view>
</view>
</view>
<view class="form-card desc-card">
<view class="card-title">
<image class="title-icon pen-icon icon-placeholder" src="/static/images/my/edit.png" mode="aspectFit"></image>
<text>问题描述</text>
</view>
<view class="textarea-shell">
<textarea class="desc-input" value="{{description}}" maxlength="500" placeholder="请详细描述您遇到的问题或建议…"
placeholder-class="placeholder" bindinput="handleDescriptionInput"></textarea>
<text class="counter">{{description.length}}/500</text>
</view>
</view>
<view class="form-card image-card">
<view class="card-title">
<image class="title-icon image-title-icon icon-placeholder" src="/static/images/my/image.png" mode="aspectFit">
</image>
<text>添加图片</text>
<text class="optional">(选填)</text>
</view>
<view style="display: flex;flex-direction: row;gap: 20;">
<view class="upload-box image-full" wx:for="{{datas.images}}" wx:key="index" bindtap="handleUpload">
<image class="plus-icon icon-placeholder" src="{{item}}"></image>
</view>
<view class="upload-box" wx:if="{{datas.images.length < 3}}" bindtap="handleUpload">
<image class="plus-icon icon-placeholder" src="/static/images/my/Addimages.png"
style="width: 26rpx;height: 26rpx;"></image>
<text>上传图片</text>
</view>
</view>
<text class="upload-tip">最多可上传 3 张图片</text>
</view>
<view class="form-card contact-card">
<view class="card-title">
<image class="title-icon phone-icon icon-placeholder" src="/static/images/my/call.png" mode="aspectFit"></image>
<text>联系方式</text>
<text class="optional">(选填)</text>
</view>
<input class="contact-input" value="{{contact}}" placeholder="请输入手机号或微信号,方便我们与您联系" placeholder-class="placeholder"
bindinput="handleContactInput" />
</view>
<view class="submit-btn" bindtap="handleSubmit">提交反馈</view>
<view class="thanks-text">感谢您的反馈,我们会认真对待每一条建议 💜</view>
</view>
</view>
+441
View File
@@ -0,0 +1,441 @@
page {
min-height: 100vh;
background: #fbfaff;
color: #111827;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.feedback-page {
width: 750rpx;
min-height: 100vh;
margin: 0 auto;
display: flex;
flex-direction: column;
box-sizing: border-box;
background:
radial-gradient(circle at 78% 4%, rgba(137, 95, 255, 0.16), transparent 20%),
radial-gradient(circle at 18% 8%, rgba(118, 129, 255, 0.12), transparent 24%),
linear-gradient(180deg, #f7f3ff 0%, #fbfaff 48%, #ffffff 100%);
}
.feedback-nav {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-back {
width: 56rpx;
height: 56rpx;
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;
line-height: 1.2;
font-weight: 900;
color: #111827;
}
.record-link {
width: 142rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: flex-end;
color: #111827;
font-size: 24rpx;
font-weight: 700;
}
.feedback-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 14rpx;
padding: 26rpx 38rpx 38rpx;
box-sizing: border-box;
}
.hero-row {
/* min-height: 184rpx; */
display: flex;
align-items: center;
gap: 30rpx;
}
.hero-illustration {
/* width: 250rpx;
height: 174rpx; */
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.paper {
width: 116rpx;
height: 126rpx;
border-radius: 18rpx;
display: flex;
flex-direction: column;
gap: 14rpx;
padding: 30rpx 20rpx 0 34rpx;
background: linear-gradient(145deg, #ffffff, #ece5ff);
box-shadow: 0 14rpx 24rpx rgba(120, 92, 219, 0.2);
transform: rotate(-7deg);
box-sizing: border-box;
}
.paper text {
height: 8rpx;
border-radius: 999rpx;
background: #cfc2ff;
}
.chat-bubble {
width: 114rpx;
height: 82rpx;
margin-left: -168rpx;
margin-top: 44rpx;
border-radius: 42rpx 42rpx 42rpx 18rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
background: linear-gradient(135deg, #9d79ff, #6d4cef);
box-shadow: 0 16rpx 24rpx rgba(108, 76, 239, 0.25);
}
.chat-bubble text {
width: 14rpx;
height: 14rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.78);
}
.pencil {
width: 76rpx;
height: 22rpx;
margin-left: 92rpx;
margin-top: -18rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, #7654f7 0 20%, #ffd05e 20% 78%, #ff6b91 78% 100%);
transform: rotate(-42deg);
}
.heart {
width: 38rpx;
height: 38rpx;
margin-right: -40rpx;
margin-top: -88rpx;
border-radius: 50% 50% 8rpx 50%;
background: linear-gradient(135deg, #ff9ec2, #ff679a);
transform: rotate(45deg);
box-shadow: 0 8rpx 14rpx rgba(255, 105, 154, 0.22);
}
.hero-copy {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.hero-title {
display: flex;
flex-wrap: wrap;
align-items: baseline;
font-size: 28rpx;
line-height: 1.25;
font-weight: 900;
color: #111827;
}
.accent {
color: #744dff;
}
.hero-desc {
margin-top: 14rpx;
color: #6c7280;
font-size: 23rpx;
line-height: 1.15;
}
.hero-line {
width: 72rpx;
height: 30rpx;
margin-top: 8rpx;
border-bottom: 5rpx solid #7c57ff;
border-radius: 50%;
transform: rotate(10deg);
}
.form-card {
border-radius: 18rpx;
padding: 24rpx;
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.94);
box-shadow: 0 8rpx 24rpx rgba(84, 91, 148, 0.06);
box-sizing: border-box;
}
.card-title {
display: flex;
align-items: center;
gap: 12rpx;
color: #111827;
font-size: 25rpx;
font-weight: 900;
}
.optional {
color: #8b91a1;
font-size: 18rpx;
font-weight: 600;
}
.title-icon {
width: 30rpx;
height: 30rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #7454ff;
background: #f3efff;
}
.title-icon text {
display: block;
}
.type-icon text {
width: 17rpx;
height: 19rpx;
border: 4rpx solid currentColor;
border-radius: 5rpx 5rpx 9rpx 9rpx;
box-sizing: border-box;
}
.pen-icon text {
width: 19rpx;
height: 19rpx;
border-left: 5rpx solid currentColor;
border-bottom: 5rpx solid currentColor;
transform: rotate(-45deg);
box-sizing: border-box;
}
.image-title-icon text {
width: 18rpx;
height: 16rpx;
border: 4rpx solid currentColor;
border-radius: 3rpx;
box-sizing: border-box;
}
.phone-icon text {
width: 18rpx;
height: 22rpx;
border-left: 5rpx solid currentColor;
border-bottom: 5rpx solid currentColor;
border-radius: 0 0 0 10rpx;
transform: rotate(-25deg);
box-sizing: border-box;
}
.type-grid {
margin-top: 22rpx;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12rpx;
}
.type-chip {
height: 56rpx;
border-radius: 11rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
color: #737887;
font-size: 20rpx;
font-weight: 800;
background: #f8f8fb;
border: 2rpx solid #ececf3;
box-sizing: border-box;
white-space: nowrap;
}
.type-chip.active {
color: #6e4cff;
background: #ffffff;
border-color: #805cff;
filter: hue-rotate(180deg); /* 旋转色相 */
filter: sepia(1) saturate(5) hue-rotate(200deg); /* 强制转为暖色调 */
}
.type-chip-icon {
width: 22rpx;
height: 22rpx;
display: block;
box-sizing: border-box;
}
.bug-icon {
border-radius: 50%;
border: 4rpx solid currentColor;
}
.star-icon {
border-radius: 5rpx;
border: 4rpx solid currentColor;
transform: rotate(45deg);
}
.message-icon,
.more-icon {
border: 4rpx solid currentColor;
border-radius: 8rpx;
}
.desc-card {
padding-bottom: 18rpx;
}
.textarea-shell {
min-height: 158rpx;
margin-top: 20rpx;
padding: 20rpx 20rpx 34rpx;
border-radius: 14rpx;
border: 1rpx solid #e4e4eb;
background: #fbfbfd;
box-sizing: border-box;
}
.desc-input {
width: 100%;
height: 104rpx;
color: #2d3340;
font-size: 22rpx;
line-height: 1.45;
}
.placeholder {
color: #a2a6b2;
}
.counter {
display: flex;
justify-content: flex-end;
color: #8f94a2;
font-size: 18rpx;
line-height: 1;
}
.image-card {
min-height: 218rpx;
}
.upload-box {
width: 106rpx;
height: 106rpx;
margin-top: 22rpx;
border-radius: 13rpx;
border: 2rpx dashed #d8d1f8;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
/* color: #7a57ff; */
font-size: 19rpx;
/* font-weight: 700; */
margin-right: 20rpx;
box-sizing: border-box;
}
.upload-box.image-full image {
width: 100%;
height: 100%;
padding: 4rpx;
box-sizing: border-box;
}
.plus-icon {
font-size: 44rpx;
line-height: 0.75;
font-weight: 300;
}
.upload-tip {
margin-top: 12rpx;
color: #9ca0ad;
font-size: 18rpx;
line-height: 1;
}
.contact-card {
min-height: 152rpx;
}
.contact-input {
height: 54rpx;
margin-top: 20rpx;
padding: 0 20rpx;
border-radius: 12rpx;
border: 1rpx solid #e6e6ee;
background: #fbfbfd;
color: #2d3340;
font-size: 21rpx;
box-sizing: border-box;
}
.submit-btn {
height: 68rpx;
margin: 12rpx 14rpx 0;
border-radius: 999rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 28rpx;
font-weight: 900;
background: linear-gradient(180deg, #a45fff 0%, #6d2df1 100%);
box-shadow: 0 12rpx 24rpx rgba(111, 48, 241, 0.24);
}
.thanks-text {
margin-top: 8rpx;
text-align: center;
color: #969aa7;
font-size: 19rpx;
line-height: 1.2;
}
.icon-placeholder {
display: block;
flex-shrink: 0;
background: none;
border: 0;
box-shadow: none;
transform: none;
}
@@ -0,0 +1,3 @@
{
"usingComponents": {}
}
+66
View File
@@ -0,0 +1,66 @@
// pages/myPackage/ysxy/index.ts
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad() {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
@@ -0,0 +1 @@
<web-view style="width: 100px;height: 100px;" src="https://veil-chat-audio-speed.zhuangb123.com/ashdas.html"></web-view>
@@ -0,0 +1 @@
/* pages/myPackage/ysxy/index.wxss */
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Some files were not shown because too many files have changed in this diff Show More