feat(uniapp_v2): 二开功能迁移与小程序主包优化

- 从 uniapp 迁移 HJF 页面、API、组件及用户/订单相关改动
- queue、assets 使用独立分包以降低主包体积
- 修复首页单根节点与支付结果页 v-if 链
- 关闭 HjfDemoPanel 全局注册;uniNoticeBar 注释 $getAppWebview 避免 __webviewId__ 报错
- 配置域名与 manifest 应用名称;cache/store 防御性处理

Made-with: Cursor
This commit is contained in:
apple
2026-03-26 12:16:01 +08:00
parent c84aeda062
commit 8e17762510
742 changed files with 184117 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
# Conversation - 2026-03-10 18:17:19
## Prompt
创建页面 points_detail.vueP17 积分明细页):
文件路径: /Users/apple/scott2026/huangjingfen/pro_v3.5.1/view/uniapp/pages/assets/points_detail.vue
内容要求:
1. 使用 Vue 2 Options API
2. 导入 getPointsDetail from '@/api/hjfAssets.js'
3. data: list, page, loading, finished, activeTab
4. onLoad + onReachBottom: 加载和分页
5. 模板: Tab切换全部/待释放/已释放)+ 积分流水列表
6. 样式: <style scoped lang="scss">
7. 参考: docs/frontend-new-pages-spec.md 第 3.3.2 节
完整 JSDoc。
## Response
The file was created successfully (377 lines total). Here's a summary of what was built:
**`pages/assets/points_detail.vue`** — P17 积分明细页,共 377 行:
**模板结构:**
- **Tab 导航栏**3 个 Tab全部 / 待释放 / 已释放),`sticky` 吸顶,激活项下划线用 `var(--view-theme)` 着色
- **按日期分组列表**:每个分组显示日期标题 + 白色卡片,卡片内每条流水展示标题、时间、状态标签(橙色=待释放 / 绿色=已释放)、右侧积分增减(`+N` 主题色 / `-N` 黑色)
- **加载更多**:旋转 loading 图标 + 文案;**空状态**:引入 `emptyPage` 组件
**脚本Vue 2 Options API**
- `data``tabs``activeTab``list``dateKeys``page``limit``loading``finished``loadTitle`
- `onLoad`:支持 `?type=frozen|released` 参数直接定位 Tab
- `onReachBottom`:触底自动翻页
- `changeTab()`:切 Tab 时 `resetAndLoad()` 重置所有状态
- `loadList()`:防重入(`loading || finished` 守卫),按 `add_time` 日期前缀分组追加数据
**样式:** `<style scoped lang="scss">`,全 `rpx` 单位,使用 `var(--view-theme)` 主题色。

View File

@@ -0,0 +1,561 @@
<template>
<view class="hjf-assets-page" :style="colorStyle">
<view class="assets-wrapper">
<view class="assets-header">
<view v-if="loading" class="skeleton-card"></view>
<view v-else class="hero-card">
<view class="hero-card__bg-circle hero-card__bg-circle--1"></view>
<view class="hero-card__bg-circle hero-card__bg-circle--2"></view>
<view class="hero-card__main">
<view class="hero-card__label">推荐佣金余额()</view>
<view class="hero-card__money">
<text class="hero-card__yen">¥</text>{{ assetsInfo ? Number(assetsInfo.brokerage_price || 0).toFixed(2) : '0.00' }}
</view>
</view>
<view class="hero-card__row">
<view class="hero-card__col">
<view class="hero-card__col-val">{{ formattedFrozenPoints }}</view>
<view class="hero-card__col-key">待释放积分</view>
</view>
<view class="hero-card__sep"></view>
<view class="hero-card__col">
<view class="hero-card__col-val">{{ formattedAvailablePoints }}</view>
<view class="hero-card__col-key">已释放积分</view>
</view>
<view class="hero-card__sep"></view>
<view class="hero-card__col" v-if="assetsInfo && assetsInfo.today_release != null">
<view class="hero-card__col-val hero-card__col-val--accent">{{ assetsInfo.today_release }}</view>
<view class="hero-card__col-key">今日释放</view>
</view>
</view>
</view>
</view>
<view class="quick-nav">
<view class="quick-nav__item" hover-class="quick-nav__item--hover" @tap="goPointsDetail">
<view class="quick-nav__icon quick-nav__icon--points">
<text class="iconfont icon-jifen"></text>
</view>
<view class="quick-nav__label">积分明细</view>
<view class="quick-nav__desc">待释放 / 已释放</view>
</view>
<view class="quick-nav__item" hover-class="quick-nav__item--hover" @tap="goCashDetail">
<view class="quick-nav__icon quick-nav__icon--cash">
<text class="iconfont icon-qianbao"></text>
</view>
<view class="quick-nav__label">现金明细</view>
<view class="quick-nav__desc">收支流水记录</view>
</view>
<view class="quick-nav__item" hover-class="quick-nav__item--hover" @tap="goWithdraw">
<view class="quick-nav__icon quick-nav__icon--withdraw">
<text class="iconfont icon-tixian"></text>
</view>
<view class="quick-nav__label">申请提现</view>
<view class="quick-nav__desc">可用余额提现</view>
</view>
</view>
</view>
<view class="release-card" v-if="!loading && assetsInfo">
<view class="release-card__header">
<view class="release-card__dot"></view>
<view class="release-card__title">今日释放预告</view>
<view class="release-card__date">{{ todayDateStr }}</view>
</view>
<view class="release-card__body">
<view class="release-card__item">
<view class="release-card__value release-card__value--highlight">
{{ assetsInfo.today_release != null ? assetsInfo.today_release : 0 }}
</view>
<view class="release-card__key">今日预计释放(积分)</view>
</view>
<view class="release-card__divider"></view>
<view class="release-card__item">
<view class="release-card__value">{{ formattedFrozenPoints }}</view>
<view class="release-card__key">待释放总积分</view>
</view>
<view class="release-card__divider"></view>
<view class="release-card__item">
<view class="release-card__value">{{ formattedAvailablePoints }}</view>
<view class="release-card__key">已释放积分</view>
</view>
</view>
<view class="release-card__tips">
<text class="iconfont icon-tishi"></text>
积分每日自动释放释放后可用于抵扣消费
</view>
</view>
<view class="stats-row" v-if="!loading && assetsInfo">
<view class="stats-item">
<view class="stats-icon stats-icon--refund">
<text class="iconfont icon-qianbao"></text>
</view>
<view class="stats-info">
<view class="stats-value">¥{{ Number(assetsInfo.brokerage_price || 0).toFixed(2) }}</view>
<view class="stats-label">推荐累计佣金</view>
</view>
</view>
<view class="stats-divider"></view>
<view class="stats-item">
<view class="stats-icon stats-icon--points">
<text class="iconfont icon-jifen"></text>
</view>
<view class="stats-info">
<view class="stats-value">{{ formattedTotalPoints }}</view>
<view class="stats-label">累计获得积分</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { getAssetsOverview } from '@/api/hjfAssets.js';
import colors from '@/mixins/color.js';
export default {
name: 'AssetsIndex',
mixins: [colors],
data() {
return {
assetsInfo: null,
loading: false
};
},
computed: {
todayDateStr() {
const d = new Date();
const mm = String(d.getMonth() + 1).padStart(2, '0');
const dd = String(d.getDate()).padStart(2, '0');
return `${d.getFullYear()}-${mm}-${dd}`;
},
formattedFrozenPoints() {
if (!this.assetsInfo) return '0';
return Number(this.assetsInfo.frozen_points).toLocaleString();
},
formattedAvailablePoints() {
if (!this.assetsInfo) return '0';
return Number(this.assetsInfo.available_points).toLocaleString();
},
formattedTotalPoints() {
if (!this.assetsInfo) return '0';
return Number(this.assetsInfo.total_points_earned).toLocaleString();
}
},
onLoad() {
this.fetchAssetsOverview();
},
onShow() {
if (this.assetsInfo) {
this.fetchAssetsOverview();
}
},
methods: {
fetchAssetsOverview() {
this.loading = true;
getAssetsOverview()
.then(res => {
if (res && (res.status === 200 || res.data)) {
this.assetsInfo = res.data || res;
}
})
.catch(() => {
uni.showToast({ title: '加载失败,请稍后重试', icon: 'none' });
})
.finally(() => {
this.loading = false;
});
},
goPointsDetail() {
uni.navigateTo({ url: '/pages/assets/points_detail' });
},
goCashDetail() {
uni.navigateTo({ url: '/pages/users/user_bill/index?type=2' });
},
goWithdraw() {
uni.navigateTo({ url: '/pages/users/user_cash/index' });
}
}
};
</script>
<style scoped lang="scss">
.hjf-assets-page {
min-height: 100vh;
background-color: #f4f5f7;
padding-bottom: 60rpx;
}
.assets-wrapper {
background: linear-gradient(180deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 50%, #f4f5f7 100%);
padding-bottom: 4rpx;
}
.assets-header {
padding-top: 32rpx;
}
.assets-header__top {
padding: 0 30rpx 24rpx;
}
.assets-header__title {
font-size: 36rpx;
font-weight: 700;
color: #fff;
letter-spacing: 2rpx;
}
.skeleton-card {
width: 710rpx;
height: 280rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.15) 25%, rgba(255,255,255,0.25) 50%, rgba(255,255,255,0.15) 75%);
background-size: 400% 100%;
border-radius: 32rpx;
margin: 0 auto 20rpx;
animation: skeleton-shimmer 1.5s infinite;
}
@keyframes skeleton-shimmer {
0% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.hero-card {
width: 710rpx;
margin: 0 auto;
border-radius: 32rpx;
background: linear-gradient(135deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 100%);
box-sizing: border-box;
position: relative;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
}
.hero-card__bg-circle {
position: absolute;
border-radius: 50%;
opacity: 0.08;
background: #fff;
}
.hero-card__bg-circle--1 {
width: 320rpx;
height: 320rpx;
top: -80rpx;
right: -60rpx;
}
.hero-card__bg-circle--2 {
width: 200rpx;
height: 200rpx;
bottom: -40rpx;
left: -30rpx;
}
.hero-card__main {
padding: 40rpx 36rpx 28rpx;
position: relative;
z-index: 1;
}
.hero-card__label {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 12rpx;
}
.hero-card__money {
font-size: 64rpx;
font-weight: 700;
color: #fff;
font-family: 'SemiBold', sans-serif;
line-height: 1.1;
}
.hero-card__yen {
font-size: 36rpx;
font-weight: 500;
margin-right: 4rpx;
}
.hero-card__row {
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.12);
padding: 24rpx 0;
position: relative;
z-index: 1;
}
.hero-card__col {
flex: 1;
text-align: center;
}
.hero-card__col-val {
font-size: 36rpx;
font-weight: 600;
color: #fff;
font-family: 'SemiBold', sans-serif;
margin-bottom: 6rpx;
}
.hero-card__col-val--accent {
color: #ffe58f;
}
.hero-card__col-key {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.75);
}
.hero-card__sep {
width: 1rpx;
height: 48rpx;
background: rgba(255, 255, 255, 0.2);
flex-shrink: 0;
}
.quick-nav {
display: flex;
margin: 20rpx 20rpx 0;
}
.quick-nav__item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
background: #fff;
border-radius: 20rpx;
padding: 30rpx 12rpx 26rpx;
box-sizing: border-box;
margin: 0 8rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
transition: all 0.2s;
}
.quick-nav__item:first-child { margin-left: 0; }
.quick-nav__item:last-child { margin-right: 0; }
.quick-nav__item--hover {
opacity: 0.75;
transform: scale(0.97);
}
.quick-nav__icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
}
.quick-nav__icon .iconfont {
font-size: 38rpx;
color: #fff;
}
.quick-nav__icon--points {
background: linear-gradient(135deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 100%);
box-shadow: 0 6rpx 16rpx rgba(233, 51, 35, 0.25);
}
.quick-nav__icon--cash {
background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
box-shadow: 0 6rpx 16rpx rgba(82, 196, 26, 0.25);
}
.quick-nav__icon--withdraw {
background: linear-gradient(135deg, #fa8c16 0%, #ffc53d 100%);
box-shadow: 0 6rpx 16rpx rgba(250, 140, 22, 0.25);
}
.quick-nav__label {
font-size: 26rpx;
font-weight: 500;
color: #333;
margin-bottom: 6rpx;
}
.quick-nav__desc {
font-size: 20rpx;
color: #999;
text-align: center;
}
.release-card {
width: 710rpx;
margin: 24rpx auto;
background: #fff;
border-radius: 24rpx;
box-sizing: border-box;
overflow: hidden;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
}
.release-card__header {
display: flex;
align-items: center;
padding: 28rpx 32rpx 0;
}
.release-card__dot {
width: 8rpx;
height: 30rpx;
border-radius: 4rpx;
background: var(--view-theme, #e93323);
margin-right: 14rpx;
flex-shrink: 0;
}
.release-card__title {
font-size: 30rpx;
font-weight: 600;
color: #333;
flex: 1;
}
.release-card__date {
font-size: 22rpx;
color: #bbb;
}
.release-card__body {
display: flex;
align-items: center;
padding: 28rpx 20rpx 24rpx;
}
.release-card__item {
flex: 1;
text-align: center;
}
.release-card__value {
font-size: 38rpx;
font-weight: 600;
color: #333;
margin-bottom: 8rpx;
font-family: 'SemiBold', sans-serif;
}
.release-card__value--highlight {
color: var(--view-theme, #e93323);
}
.release-card__key {
font-size: 22rpx;
color: #999;
}
.release-card__divider {
width: 1rpx;
height: 60rpx;
background-color: #eee;
align-self: center;
flex-shrink: 0;
}
.release-card__tips {
font-size: 22rpx;
color: #bbb;
padding: 0 32rpx 24rpx;
display: flex;
align-items: center;
gap: 6rpx;
background: #fafafa;
padding-top: 18rpx;
}
.release-card__tips .iconfont {
font-size: 24rpx;
color: #ccc;
}
.stats-row {
width: 710rpx;
margin: 0 auto;
background: #fff;
border-radius: 24rpx;
display: flex;
align-items: center;
padding: 32rpx 24rpx;
box-sizing: border-box;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
}
.stats-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
}
.stats-icon {
width: 64rpx;
height: 64rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.stats-icon .iconfont {
font-size: 32rpx;
color: #fff;
}
.stats-icon--refund {
background: linear-gradient(135deg, #ff7875 0%, #ff4d4f 100%);
}
.stats-icon--points {
background: linear-gradient(135deg, #ffa940 0%, #fa8c16 100%);
}
.stats-info {
display: flex;
flex-direction: column;
}
.stats-value {
font-size: 32rpx;
font-weight: 600;
color: #333;
font-family: 'SemiBold', sans-serif;
line-height: 1.3;
}
.stats-label {
font-size: 22rpx;
color: #999;
margin-top: 4rpx;
}
.stats-divider {
width: 1rpx;
height: 60rpx;
background-color: #eee;
flex-shrink: 0;
margin: 0 10rpx;
}
</style>

View File

@@ -0,0 +1,376 @@
<template>
<!-- P17 积分明细页 -->
<view class="hjf-points-detail-page">
<!-- Tab 筛选栏 -->
<view class="tab-nav acea-row">
<view
v-for="(tab, idx) in tabs"
:key="idx"
class="tab-nav__item"
:class="{ on: activeTab === idx }"
@tap="changeTab(idx)"
>{{ tab.label }}</view>
</view>
<!-- 积分流水列表按日期分组 -->
<view class="points-list">
<view
v-for="(group, gIdx) in list"
:key="gIdx"
class="points-list__group"
>
<!-- 日期分组标题 -->
<view class="points-list__date">{{ group.date }}</view>
<!-- 分组内条目 -->
<view class="points-list__card">
<view
v-for="(item, iIdx) in group.children"
:key="iIdx"
class="points-list__item acea-row row-between-wrapper"
>
<!-- 左侧标题 + 时间 -->
<view class="points-list__info">
<view class="points-list__title line1">{{ item.title }}</view>
<view class="points-list__meta acea-row">
<view class="points-list__time">{{ item.add_time }}</view>
<view
class="points-list__tag"
:class="item.status === 'frozen' ? 'tag--frozen' : 'tag--released'"
>{{ item.status === 'frozen' ? '待释放' : '已释放' }}</view>
</view>
</view>
<!-- 右侧积分增减 -->
<view
class="points-list__points"
:class="item.pm === 1 ? 'points--add' : 'points--sub'"
>
{{ item.pm === 1 ? '+' : '-' }}{{ item.points }}
</view>
</view>
</view>
</view>
<!-- 加载更多提示 -->
<view v-if="list.length > 0" class="loadingicon acea-row row-center-wrapper">
<text
v-if="loading"
class="loading iconfont icon-jiazai"
></text>
<text class="load-title">{{ loadTitle }}</text>
</view>
<!-- 空状态 -->
<view v-if="!loading && list.length === 0" class="empty-wrap">
<emptyPage title="暂无积分记录~" src="/statics/images/noOrder.gif"></emptyPage>
</view>
</view>
</view>
</template>
<script>
import { getPointsDetail } from '@/api/hjfAssets.js';
import emptyPage from '@/components/emptyPage.vue';
import colors from '@/mixins/color';
/**
* P17 积分明细页
*
* 展示当前用户的积分流水,支持按类型 Tab 筛选(全部/待释放/已释放),
* 并以日期分组方式分页渲染列表,上拉触底自动加载下一页。
*
* @module pages/assets/points_detail
*/
export default {
name: 'PointsDetail',
components: { emptyPage },
mixins: [colors],
data() {
return {
/**
* Tab 配置label 展示文案type 对应 API 参数('' 表示全部)
* @type {Array<{ label: string, type: string }>}
*/
tabs: [
{ label: '全部', type: '' },
{ label: '待释放', type: 'frozen' },
{ label: '已释放', type: 'released' },
],
/** 当前选中 Tab 索引0=全部 1=待释放 2=已释放 @type {number} */
activeTab: 0,
/**
* 按日期分组后的列表,每项形如 { date: string, children: Array }
* @type {Array<{ date: string, children: Array<Object> }>}
*/
list: [],
/** 已记录的日期键,用于去重分组 @type {string[]} */
dateKeys: [],
/** 当前请求页码 @type {number} */
page: 1,
/** 每页条数 @type {number} */
limit: 15,
/** 是否正在加载(防重入锁) @type {boolean} */
loading: false,
/** 是否已加载全部数据 @type {boolean} */
finished: false,
/** 底部加载提示文案 @type {string} */
loadTitle: '加载更多',
};
},
/**
* 页面加载:支持从资产总览通过 type 参数直接定位 Tab。
* @param {Object} options - 页面跳转参数
* @param {string} [options.type] - 可选,'frozen' | 'released'
*/
onLoad(options) {
if (options && options.type) {
const idx = this.tabs.findIndex(t => t.type === options.type);
if (idx > -1) this.activeTab = idx;
}
this.loadList();
},
/**
* 上拉触底:加载下一页数据。
*/
onReachBottom() {
this.loadList();
},
methods: {
/**
* 切换 Tab 筛选,重置分页后重新加载列表。
* @param {number} idx - 点击的 Tab 索引
*/
changeTab(idx) {
if (idx === this.activeTab) return;
this.activeTab = idx;
this.resetAndLoad();
},
/**
* 重置所有分页/列表状态,然后发起首页请求。
*/
resetAndLoad() {
this.page = 1;
this.finished = false;
this.loading = false;
this.loadTitle = '加载更多';
this.dateKeys = [];
this.$set(this, 'list', []);
this.loadList();
},
/**
* 加载积分明细数据(分页追加),通过 `getPointsDetail` 获取。
* 按 `add_time` 日期前缀分组追加到 `list`。
* 防重入loading 为 true 或 finished 时直接返回。
*/
loadList() {
if (this.loading || this.finished) return;
this.loading = true;
this.loadTitle = '';
const currentType = this.tabs[this.activeTab].type;
const params = {
page: this.page,
limit: this.limit,
};
if (currentType) params.type = currentType;
getPointsDetail(params).then(res => {
const items = (res.data && res.data.list) ? res.data.list : [];
items.forEach(item => {
// 取 add_time 的日期部分作为分组 key格式 "YYYY-MM-DD"
const dateKey = (item.add_time || '').split(' ')[0] || '未知日期';
if (!this.dateKeys.includes(dateKey)) {
this.dateKeys.push(dateKey);
this.list.push({ date: dateKey, children: [] });
}
const group = this.list.find(g => g.date === dateKey);
if (group) group.children.push(item);
});
const loadend = items.length < this.limit;
this.finished = loadend;
this.loadTitle = loadend ? '没有更多内容啦~' : '加载更多';
if (!loadend) this.page += 1;
this.loading = false;
}).catch(() => {
this.loading = false;
this.loadTitle = '加载更多';
});
},
},
};
</script>
<style scoped lang="scss">
.hjf-points-detail-page {
min-height: 100vh;
background-color: #f5f5f5;
}
/* -------- Tab 导航 -------- */
.tab-nav {
background-color: #fff;
height: 88rpx;
line-height: 88rpx;
position: sticky;
top: 0;
z-index: 10;
border-bottom: 1rpx solid #f0f0f0;
&__item {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
position: relative;
transition: color 0.2s;
&.on {
color: var(--view-theme);
font-size: 30rpx;
font-weight: 500;
&::after {
position: absolute;
content: '';
width: 48rpx;
height: 6rpx;
border-radius: 10rpx;
background: var(--view-theme);
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
}
}
}
/* -------- 积分列表 -------- */
.points-list {
padding: 20rpx 24rpx;
&__group {
margin-bottom: 20rpx;
}
&__date {
font-size: 24rpx;
color: #999;
padding: 12rpx 0 10rpx;
}
&__card {
background: #fff;
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
&__item {
padding: 28rpx 28rpx;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
}
&__info {
flex: 1;
overflow: hidden;
padding-right: 20rpx;
}
&__title {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
&__meta {
align-items: center;
gap: 12rpx;
}
&__time {
font-size: 22rpx;
color: #aaa;
}
&__tag {
font-size: 20rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx;
&.tag--frozen {
color: #ff8c00;
background: rgba(255, 140, 0, 0.1);
}
&.tag--released {
color: #52c41a;
background: rgba(82, 196, 26, 0.1);
}
}
&__points {
font-size: 32rpx;
font-weight: 600;
white-space: nowrap;
&.points--add {
color: var(--view-theme);
}
&.points--sub {
color: #333;
}
}
}
/* -------- 加载更多 & 空状态 -------- */
.loadingicon {
padding: 24rpx 0;
font-size: 24rpx;
color: #aaa;
.loading {
margin-right: 8rpx;
animation: rotating 1.5s linear infinite;
}
.load-title {
font-size: 24rpx;
}
}
@keyframes rotating {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.empty-wrap {
padding: 60rpx 0;
}
</style>