feat: 黄精粉前端功能集成 + 个人中心/资产/公排页面优化 + 去除admin copyright
主要改动: - 个人中心: 去除HjfMemberBadge徽章, 会员等级改显示vip_name, "我的资产"/"公排查询"导航项改为与member-points一致风格 - 我的资产页面: 去除HjfMemberBadge, 美化卡片圆角和阴影 - 公排查询页面: 美化顶部渐变和订单卡片样式 - Admin登录页和后台布局: 彻底删除footer copyright信息 - 新增黄精粉业务页面/组件/API/Mock数据(Phase 1) - 新增PHP环境配置文档和启动脚本 Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Conversation - 2026-03-10 18:12:21
|
||||
|
||||
## Prompt
|
||||
|
||||
创建页面 history.vue(P13 公排历史页):
|
||||
|
||||
文件路径: /Users/apple/scott2026/huangjingfen/pro_v3.5.1/view/uniapp/pages/queue/history.vue
|
||||
|
||||
内容要求:
|
||||
1. 使用 Vue 2 Options API
|
||||
2. 导入 getQueueHistory from '@/api/hjfQueue.js'
|
||||
3. data: list(Array), page(Number), loading(Boolean), finished(Boolean)
|
||||
4. onLoad + onReachBottom: 加载和分页
|
||||
5. 模板: 按时间分组显示退款记录列表
|
||||
6. 样式: <style scoped lang="scss">
|
||||
7. 参考: docs/frontend-new-pages-spec.md 第 2.3.2 节
|
||||
|
||||
完整 JSDoc。
|
||||
|
||||
## Response
|
||||
|
||||
文件已创建。下面是关键实现点的说明:
|
||||
|
||||
**`history.vue` — P13 公排历史页**
|
||||
|
||||
**数据层**
|
||||
- `list` / `page` / `loading` / `finished` 四个核心状态
|
||||
- `limit: 15`,与 Mock 数据的分页规格一致
|
||||
- `timeKeys` 辅助字段用于历史版本兼容(当前分组逻辑已移入 computed)
|
||||
|
||||
**分组逻辑(computed `groupedList`)**
|
||||
- 先按 `activeTab` 过滤(0=全部,1=`status===0`,2=`status===1`)
|
||||
- 再按 `time_key` 字段聚合,保持原始顺序,返回 `[{ date, children[] }]`
|
||||
|
||||
**生命周期**
|
||||
- `onLoad` → 首次加载
|
||||
- `onReachBottom` → 触底分页,`loading` / `finished` 双重守卫防止重复请求
|
||||
|
||||
**`changeTab`**
|
||||
- 切换 Tab 时重置 `list / page / finished`,重新请求;Tab 相同则 early return
|
||||
|
||||
**`formatTime`**
|
||||
- 将秒级 Unix 时间戳转为 `YYYY-MM-DD HH:mm`,`timestamp=0` 时返回 `--`
|
||||
|
||||
**样式**
|
||||
- `<style scoped lang="scss">`,全部使用 `rpx`
|
||||
- Tab 激活指示器复用 `user_bill` 的 `::after` 伪元素模式
|
||||
- 状态标签:排队中绿色胶囊,已退款灰色胶囊
|
||||
- `sticky` Tab 导航,滚动时悬浮在顶部
|
||||
@@ -0,0 +1,39 @@
|
||||
# Conversation - 2026-03-10 18:13:41
|
||||
|
||||
## Prompt
|
||||
|
||||
创建页面 rules.vue(P14 公排规则页):
|
||||
|
||||
文件路径: /Users/apple/scott2026/huangjingfen/pro_v3.5.1/view/uniapp/pages/queue/rules.vue
|
||||
|
||||
内容要求:
|
||||
1. 使用 Vue 2 Options API
|
||||
2. 静态展示页面,无需 API 调用
|
||||
3. 模板: 公排规则说明(进4退1机制说明、示例计算)
|
||||
4. 参考: docs/frontend-new-pages-spec.md 第 2.3.3 节
|
||||
|
||||
完整 JSDoc。
|
||||
|
||||
## Response
|
||||
|
||||
`rules.vue` 已创建完成。以下是文件内容概览:
|
||||
|
||||
**文件路径**:`pro_v3.5.1/view/uniapp/pages/queue/rules.vue`
|
||||
|
||||
**三大区块**:
|
||||
|
||||
1. **公排机制图示**(`section`)
|
||||
- 进四退一流程图:5个步骤节点(4个"进"步骤 + 1个触发退款的"退"步骤),带颜色区分(红色入队圆标、绿色退款圆标)
|
||||
- 触发节点有橙色"触发退款!"角标
|
||||
- 示例计算卡片:报单 ¥3,600 → 退款 ¥3,600(全额),平均等待约3单
|
||||
|
||||
2. **规则条款列表**(7条)
|
||||
- 带序号圆圈(主题色背景)
|
||||
- 覆盖:全额退款保障、进四退一机制、退款自动到账、入队资格、多单独立排队、积分叠加、规则变更通知
|
||||
|
||||
3. **FAQ 手风琴**(7条)
|
||||
- 点击 `toggleFaq(index)` 切换 `open` 状态
|
||||
- 箭头 `›` 旋转动画(`rotate(90deg)` ↔ `rotate(-90deg)`)
|
||||
- 问题覆盖:等待时长、退款去向、多次参与、循环入队、序号说明、积分叠加、查看进度
|
||||
|
||||
**技术规范符合**:Vue 2 Options API、`<style scoped lang="scss">`、`rpx` 单位、`var(--view-theme)` 主题色、完整 JSDoc 注释(`@file`、`@description`、`@type`、`@param`)。
|
||||
BIN
pro_v3.5.1/view/uniapp/pages/queue/.cursor-cli/sessions.db
Normal file
BIN
pro_v3.5.1/view/uniapp/pages/queue/.cursor-cli/sessions.db
Normal file
Binary file not shown.
373
pro_v3.5.1/view/uniapp/pages/queue/history.vue
Normal file
373
pro_v3.5.1/view/uniapp/pages/queue/history.vue
Normal file
@@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<view :style="colorStyle" class="queue-history">
|
||||
<!-- Tab 筛选 -->
|
||||
<view class="nav acea-row">
|
||||
<view
|
||||
class="item"
|
||||
:class="activeTab === 0 ? 'on' : ''"
|
||||
@click="changeTab(0)"
|
||||
>全部</view>
|
||||
<view
|
||||
class="item"
|
||||
:class="activeTab === 1 ? 'on' : ''"
|
||||
@click="changeTab(1)"
|
||||
>排队中</view>
|
||||
<view
|
||||
class="item"
|
||||
:class="activeTab === 2 ? 'on' : ''"
|
||||
@click="changeTab(2)"
|
||||
>已退款</view>
|
||||
</view>
|
||||
|
||||
<!-- 按日期分组的记录列表 -->
|
||||
<view class="record-list">
|
||||
<view
|
||||
class="date-group"
|
||||
v-for="(group, gIndex) in groupedList"
|
||||
:key="gIndex"
|
||||
>
|
||||
<view class="date-label">{{ group.date }}</view>
|
||||
<view class="group-card">
|
||||
<view
|
||||
class="record-item acea-row row-between-wrapper"
|
||||
v-for="(item, iIndex) in group.children"
|
||||
:key="iIndex"
|
||||
>
|
||||
<!-- 左侧:订单号 + 批次号 -->
|
||||
<view class="record-left">
|
||||
<view class="order-id line1">订单号:{{ item.order_id }}</view>
|
||||
<view class="record-meta">
|
||||
<text v-if="item.trigger_batch > 0">批次号:#{{ item.trigger_batch }}</text>
|
||||
<text v-else>入队序号:#{{ item.queue_no }}</text>
|
||||
</view>
|
||||
<view class="record-time" v-if="item.status === 1 && item.refund_time > 0">
|
||||
退款时间:{{ formatTime(item.refund_time) }}
|
||||
</view>
|
||||
<view class="record-time" v-else>
|
||||
入队时间:{{ formatTime(item.add_time) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右侧:金额 + 状态标签 -->
|
||||
<view class="record-right">
|
||||
<view class="amount">¥{{ Number(item.amount).toFixed(2) }}</view>
|
||||
<view class="status-tag" :class="item.status === 1 ? 'refunded' : 'queuing'">
|
||||
{{ item.status === 1 ? '已退款' : '排队中' }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view class="loading-bar acea-row row-center-wrapper" v-if="list.length > 0">
|
||||
<text class="loading iconfont icon-jiazai" :hidden="!loading"></text>
|
||||
{{ loadTitle }}
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="px-20 mt-20" v-if="list.length === 0 && !loading">
|
||||
<emptyPage title="暂无公排记录~" src="/statics/images/noOrder.gif"></emptyPage>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getQueueHistory } from '@/api/hjfQueue.js';
|
||||
import emptyPage from '@/components/emptyPage.vue';
|
||||
import colors from '@/mixins/color';
|
||||
|
||||
/**
|
||||
* P13 公排历史记录页
|
||||
*
|
||||
* 展示当前用户的所有公排历史,支持 Tab 筛选(全部/排队中/已退款),
|
||||
* 记录按日期分组显示,支持上拉翻页加载。
|
||||
*
|
||||
* @see docs/frontend-new-pages-spec.md 2.2.5
|
||||
*/
|
||||
export default {
|
||||
name: 'QueueHistory',
|
||||
|
||||
components: { emptyPage },
|
||||
|
||||
mixins: [colors],
|
||||
|
||||
data() {
|
||||
return {
|
||||
/** @type {Array<Object>} 原始记录列表(所有已加载页的合并) */
|
||||
list: [],
|
||||
|
||||
/** @type {number} 当前页码(从 1 开始) */
|
||||
page: 1,
|
||||
|
||||
/** @type {number} 每页条数 */
|
||||
limit: 15,
|
||||
|
||||
/** @type {boolean} 是否正在加载 */
|
||||
loading: false,
|
||||
|
||||
/** @type {boolean} 是否已加载完全部数据 */
|
||||
finished: false,
|
||||
|
||||
/** @type {string} 底部加载提示文字 */
|
||||
loadTitle: '加载更多',
|
||||
|
||||
/**
|
||||
* 当前激活的 Tab
|
||||
* 0 = 全部,1 = 排队中,2 = 已退款
|
||||
* @type {number}
|
||||
*/
|
||||
activeTab: 0,
|
||||
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* 按 time_key(日期字符串)将 list 分组
|
||||
* 过滤规则:activeTab=0 不过滤;1 只显示 status=0;2 只显示 status=1
|
||||
* @returns {Array<{ date: string, children: Object[] }>}
|
||||
*/
|
||||
groupedList() {
|
||||
const filtered = this.list.filter(item => {
|
||||
if (this.activeTab === 1) return item.status === 0;
|
||||
if (this.activeTab === 2) return item.status === 1;
|
||||
return true;
|
||||
});
|
||||
|
||||
const map = {};
|
||||
const order = [];
|
||||
filtered.forEach(item => {
|
||||
const key = item.time_key;
|
||||
if (!map[key]) {
|
||||
map[key] = [];
|
||||
order.push(key);
|
||||
}
|
||||
map[key].push(item);
|
||||
});
|
||||
|
||||
return order.map(date => ({ date, children: map[date] }));
|
||||
},
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadHistory();
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.loadHistory();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 加载公排历史记录(分页追加)
|
||||
* 若正在加载或已全部加载则直接返回。
|
||||
* @returns {void}
|
||||
*/
|
||||
loadHistory() {
|
||||
if (this.loading || this.finished) return;
|
||||
|
||||
this.loading = true;
|
||||
this.loadTitle = '';
|
||||
|
||||
getQueueHistory({
|
||||
page: this.page,
|
||||
limit: this.limit,
|
||||
status: this.activeTab === 0 ? undefined : this.activeTab === 1 ? 0 : 1,
|
||||
})
|
||||
.then(res => {
|
||||
const newItems = (res.data && res.data.list) ? res.data.list : [];
|
||||
|
||||
newItems.forEach(item => {
|
||||
this.list.push(item);
|
||||
});
|
||||
|
||||
const isEnd = newItems.length < this.limit;
|
||||
this.finished = isEnd;
|
||||
this.loadTitle = isEnd ? '没有更多内容啦~' : '加载更多';
|
||||
this.page += 1;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loadTitle = '加载更多';
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换 Tab 筛选,重置列表并重新加载
|
||||
* @param {number} tab - 目标 Tab 索引(0/1/2)
|
||||
* @returns {void}
|
||||
*/
|
||||
changeTab(tab) {
|
||||
if (tab === this.activeTab) return;
|
||||
this.activeTab = tab;
|
||||
this.list = [];
|
||||
this.page = 1;
|
||||
this.finished = false;
|
||||
this.loadHistory();
|
||||
},
|
||||
|
||||
/**
|
||||
* 将 Unix 时间戳格式化为 YYYY-MM-DD HH:mm
|
||||
* @param {number} timestamp - Unix 秒级时间戳
|
||||
* @returns {string} 格式化后的时间字符串
|
||||
*/
|
||||
formatTime(timestamp) {
|
||||
if (!timestamp) return '--';
|
||||
const d = new Date(timestamp * 1000);
|
||||
const pad = n => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.queue-history {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* ===== Tab 导航 ===== */
|
||||
.nav {
|
||||
background: #fff;
|
||||
height: 88rpx;
|
||||
width: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
line-height: 88rpx;
|
||||
position: relative;
|
||||
|
||||
&.on {
|
||||
color: var(--view-theme);
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 64rpx;
|
||||
height: 6rpx;
|
||||
border-radius: 20rpx;
|
||||
background: var(--view-theme);
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
margin-left: -32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 记录列表 ===== */
|
||||
.record-list {
|
||||
padding: 24rpx 24rpx 40rpx;
|
||||
}
|
||||
|
||||
.date-group {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.date-label {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 12rpx;
|
||||
padding-left: 4rpx;
|
||||
}
|
||||
|
||||
.group-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ===== 单条记录 ===== */
|
||||
.record-item {
|
||||
padding: 28rpx 30rpx;
|
||||
align-items: center;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
.record-left {
|
||||
flex: 1;
|
||||
margin-right: 24rpx;
|
||||
|
||||
.order-id {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.record-meta {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 22rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
}
|
||||
|
||||
.record-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
flex-shrink: 0;
|
||||
|
||||
.amount {
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 状态标签 ===== */
|
||||
.status-tag {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
|
||||
&.queuing {
|
||||
color: #19be6b;
|
||||
background: rgba(25, 190, 107, 0.1);
|
||||
}
|
||||
|
||||
&.refunded {
|
||||
color: #999;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 底部加载提示 ===== */
|
||||
.loading-bar {
|
||||
font-size: 26rpx;
|
||||
color: #aaa;
|
||||
padding: 24rpx 0;
|
||||
|
||||
.loading {
|
||||
margin-right: 8rpx;
|
||||
animation: rotate 1s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
534
pro_v3.5.1/view/uniapp/pages/queue/rules.vue
Normal file
534
pro_v3.5.1/view/uniapp/pages/queue/rules.vue
Normal file
@@ -0,0 +1,534 @@
|
||||
<template>
|
||||
<view :style="colorStyle" class="queue-rules">
|
||||
|
||||
<!-- 公排机制图示 -->
|
||||
<view class="section">
|
||||
<view class="section-title">公排机制图示</view>
|
||||
<view class="mechanism-card">
|
||||
<view class="mechanism-title">进四退一流程</view>
|
||||
<view class="flow-diagram">
|
||||
<!-- 第一步 -->
|
||||
<view class="flow-step">
|
||||
<view class="step-circle step-in">进</view>
|
||||
<view class="step-desc">用户A 购买报单商品</view>
|
||||
<view class="step-sub">¥3,600 → 自动入队(#1)</view>
|
||||
</view>
|
||||
<view class="flow-arrow">↓</view>
|
||||
<!-- 第二步 -->
|
||||
<view class="flow-step">
|
||||
<view class="step-circle step-in">进</view>
|
||||
<view class="step-desc">用户B 购买报单商品</view>
|
||||
<view class="step-sub">¥3,600 → 自动入队(#2)</view>
|
||||
</view>
|
||||
<view class="flow-arrow">↓</view>
|
||||
<!-- 第三步 -->
|
||||
<view class="flow-step">
|
||||
<view class="step-circle step-in">进</view>
|
||||
<view class="step-desc">用户C 购买报单商品</view>
|
||||
<view class="step-sub">¥3,600 → 自动入队(#3)</view>
|
||||
</view>
|
||||
<view class="flow-arrow">↓</view>
|
||||
<!-- 第四步触发退款 -->
|
||||
<view class="flow-step trigger">
|
||||
<view class="step-circle step-in">进</view>
|
||||
<view class="step-desc">用户D 购买报单商品</view>
|
||||
<view class="step-sub">¥3,600 → 自动入队(#4)</view>
|
||||
<view class="trigger-badge">触发退款!</view>
|
||||
</view>
|
||||
<view class="flow-arrow refund-arrow">↓</view>
|
||||
<!-- 退款 -->
|
||||
<view class="flow-step refund-step">
|
||||
<view class="step-circle step-out">退</view>
|
||||
<view class="step-desc">用户A 全额退款</view>
|
||||
<view class="step-sub">¥3,600 → 返还至现金余额</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 循环说明 -->
|
||||
<view class="cycle-note">
|
||||
<text class="cycle-icon">🔄</text>
|
||||
<text class="cycle-text">如此循环:每进入4单,最早的1单全额退款</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 示例计算卡片 -->
|
||||
<view class="example-card">
|
||||
<view class="example-title">示例计算</view>
|
||||
<view class="example-row">
|
||||
<text class="example-label">报单金额</text>
|
||||
<text class="example-value">¥3,600.00</text>
|
||||
</view>
|
||||
<view class="example-row">
|
||||
<text class="example-label">退款金额</text>
|
||||
<text class="example-value highlight">¥3,600.00(全额)</text>
|
||||
</view>
|
||||
<view class="example-row">
|
||||
<text class="example-label">平均等待单数</text>
|
||||
<text class="example-value">约 3 单(即约3倍报单量)</text>
|
||||
</view>
|
||||
<view class="example-divider"></view>
|
||||
<view class="example-desc">
|
||||
假设公排池每天新增20单,您的排队序号为第14位,预计约需等待 3 天可触发退款。
|
||||
实际等待时间取决于整体报单速度,报单越活跃退款越快。
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 规则条款列表 -->
|
||||
<view class="section">
|
||||
<view class="section-title">规则条款</view>
|
||||
<view class="rules-card">
|
||||
<view
|
||||
class="rule-item"
|
||||
v-for="(rule, index) in ruleItems"
|
||||
:key="index"
|
||||
>
|
||||
<view class="rule-index">{{ index + 1 }}</view>
|
||||
<view class="rule-content">
|
||||
<view class="rule-title-text">{{ rule.title }}</view>
|
||||
<view class="rule-body">{{ rule.content }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 常见问题 FAQ 手风琴 -->
|
||||
<view class="section">
|
||||
<view class="section-title">常见问题</view>
|
||||
<view class="faq-list">
|
||||
<view
|
||||
class="faq-item"
|
||||
v-for="(item, index) in faqItems"
|
||||
:key="index"
|
||||
@click="toggleFaq(index)"
|
||||
>
|
||||
<view class="faq-header acea-row row-between-wrapper">
|
||||
<view class="faq-question">{{ item.question }}</view>
|
||||
<view class="faq-arrow" :class="{ 'arrow-up': item.open }">›</view>
|
||||
</view>
|
||||
<view class="faq-answer" v-if="item.open">{{ item.answer }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部声明 -->
|
||||
<view class="footer-note">
|
||||
<text>本平台公排规则最终解释权归黄精粉健康商城所有</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file pages/queue/rules.vue
|
||||
* @description P14 公排规则说明页 — 静态展示页面,无 API 调用
|
||||
*
|
||||
* 页面结构:
|
||||
* 1. 公排机制图示(进四退一流程图 + 示例计算)
|
||||
* 2. 规则条款列表(编号条款)
|
||||
* 3. 常见问题 FAQ 手风琴(点击展开/收起)
|
||||
*
|
||||
* @module pages/queue/rules
|
||||
* @requires mixin/color.js — 提供 colorStyle 计算属性(主题色 CSS 变量注入)
|
||||
*/
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'QueueRules',
|
||||
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* @type {Array<{title: string, content: string}>}
|
||||
* @description 规则条款列表,静态数据
|
||||
*/
|
||||
ruleItems: [
|
||||
{
|
||||
title: '全额退款保障',
|
||||
content: '参与公排的报单金额为 ¥3,600,退款时按原金额全额返还至您的现金余额,不收取任何手续费。'
|
||||
},
|
||||
{
|
||||
title: '进四退一机制',
|
||||
content: '公排池按照入队时间先后排队。每当新入队单数达到当前最早排队单数的4倍时,最早的1单自动触发退款。'
|
||||
},
|
||||
{
|
||||
title: '退款自动到账',
|
||||
content: '退款金额将在触发退款后立即到账至您的现金余额,无需手动申请,可在"我的资产"中查看。'
|
||||
},
|
||||
{
|
||||
title: '入队资格',
|
||||
content: '购买报单商品(黄精粉套餐 ¥3,600)后,系统自动为该订单分配全局唯一的排队序号,按购买时间先后排序。'
|
||||
},
|
||||
{
|
||||
title: '多单独立排队',
|
||||
content: '同一用户购买多单报单商品,每单独立分配排队序号,各自独立参与公排循环,互不影响。'
|
||||
},
|
||||
{
|
||||
title: '积分奖励叠加',
|
||||
content: '公排退款与积分奖励体系相互独立,参与公排的同时可正常获得推荐积分奖励,两者并行不悖。'
|
||||
},
|
||||
{
|
||||
title: '规则变更通知',
|
||||
content: '若平台对公排规则进行调整,将提前通过公告及消息通知用户,变更后的规则不溯及既往订单。'
|
||||
}
|
||||
],
|
||||
|
||||
/**
|
||||
* @type {Array<{question: string, answer: string, open: boolean}>}
|
||||
* @description 常见问题列表,open 字段控制手风琴展开状态
|
||||
*/
|
||||
faqItems: [
|
||||
{
|
||||
question: '参与公排后多久能收到退款?',
|
||||
answer: '等待时间取决于公排池的整体报单速度。每进入4单触发最早1单退款。若每天新增约20单,一般约3-5天可收到退款。您可在"公排状态"页查看实时进度和预估等待时间。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '退款会到哪里?',
|
||||
answer: '退款金额将全额返还至您在平台的现金余额,可在"我的资产"中查看,并可随时申请提现(提现手续费7%)。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '一个人可以参与多次公排吗?',
|
||||
answer: '可以。每次购买报单商品均会独立进入公排队列,获得新的排队序号。多单独立排队,各自触发退款,相互不影响。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '公排退款后还能继续参与吗?',
|
||||
answer: '可以。退款到账后您可以再次购买报单商品重新入队,循环享受公排返利。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '为什么我的排队序号不是第1位?',
|
||||
answer: '公排池是全平台共享队列,您的排队序号代表全局位置。序号前面的用户将优先触发退款,请耐心等待。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '公排和积分奖励可以同时获得吗?',
|
||||
answer: '可以。购买报单商品后,您的直接推荐人可获得积分奖励,同时该订单进入公排队列。两套机制并行运作,互不影响。',
|
||||
open: false
|
||||
},
|
||||
{
|
||||
question: '如何查看我的排队进度?',
|
||||
answer: '进入"公排状态"页可查看:您的排队序号、当前批次进度(X/4)、预计等待时间。页面实时展示全局公排进度。',
|
||||
open: false
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['colorStyle'])
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* @description 切换 FAQ 手风琴展开/收起状态
|
||||
* @param {number} index - FAQ 条目的索引
|
||||
*/
|
||||
toggleFaq(index) {
|
||||
this.faqItems[index].open = !this.faqItems[index].open;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.queue-rules {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
/* ===== 通用区块 ===== */
|
||||
.section {
|
||||
margin: 20rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #282828;
|
||||
padding: 24rpx 0 16rpx;
|
||||
}
|
||||
|
||||
/* ===== 机制图示卡片 ===== */
|
||||
.mechanism-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx 28rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.mechanism-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--view-theme, #e93323);
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
/* 流程图 */
|
||||
.flow-diagram {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flow-step {
|
||||
width: 100%;
|
||||
background: #f9f9f9;
|
||||
border-radius: 12rpx;
|
||||
padding: 18rpx 24rpx;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
|
||||
&.trigger {
|
||||
background: #fff7f0;
|
||||
border: 2rpx solid #ff9d4d;
|
||||
}
|
||||
|
||||
&.refund-step {
|
||||
background: #f0fff4;
|
||||
border: 2rpx solid #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.step-circle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
border-radius: 50%;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
&.step-in {
|
||||
background: var(--view-theme, #e93323);
|
||||
}
|
||||
|
||||
&.step-out {
|
||||
background: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.step-sub {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.trigger-badge {
|
||||
display: inline-block;
|
||||
background: #ff9d4d;
|
||||
color: #fff;
|
||||
font-size: 20rpx;
|
||||
border-radius: 20rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.flow-arrow {
|
||||
font-size: 36rpx;
|
||||
color: #ccc;
|
||||
line-height: 50rpx;
|
||||
text-align: center;
|
||||
|
||||
&.refund-arrow {
|
||||
color: #52c41a;
|
||||
}
|
||||
}
|
||||
|
||||
/* 循环说明 */
|
||||
.cycle-note {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 24rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
background: #f0f4ff;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.cycle-icon {
|
||||
font-size: 28rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.cycle-text {
|
||||
font-size: 24rpx;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* 示例计算卡片 */
|
||||
.example-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 28rpx;
|
||||
margin-top: 20rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.example-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #282828;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.example-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
.example-label {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.example-value {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
|
||||
&.highlight {
|
||||
color: #52c41a;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.example-divider {
|
||||
height: 1rpx;
|
||||
background: #f0f0f0;
|
||||
margin: 16rpx 0;
|
||||
}
|
||||
|
||||
.example-desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
/* ===== 规则条款 ===== */
|
||||
.rules-card {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.rule-item {
|
||||
display: flex;
|
||||
padding: 24rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rule-index {
|
||||
flex-shrink: 0;
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border-radius: 50%;
|
||||
background: var(--view-theme, #e93323);
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.rule-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rule-title-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.rule-body {
|
||||
font-size: 24rpx;
|
||||
color: #777;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* ===== FAQ 手风琴 ===== */
|
||||
.faq-list {
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.faq-header {
|
||||
padding: 28rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.faq-question {
|
||||
flex: 1;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
padding-right: 20rpx;
|
||||
}
|
||||
|
||||
.faq-arrow {
|
||||
flex-shrink: 0;
|
||||
font-size: 40rpx;
|
||||
color: #ccc;
|
||||
transform: rotate(90deg);
|
||||
transition: transform 0.25s ease;
|
||||
line-height: 1;
|
||||
|
||||
&.arrow-up {
|
||||
transform: rotate(-90deg);
|
||||
color: var(--view-theme, #e93323);
|
||||
}
|
||||
}
|
||||
|
||||
.faq-answer {
|
||||
padding: 0 28rpx 24rpx;
|
||||
font-size: 24rpx;
|
||||
color: #888;
|
||||
line-height: 1.8;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
/* ===== 底部声明 ===== */
|
||||
.footer-note {
|
||||
margin: 30rpx 24rpx 0;
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
color: #bbb;
|
||||
}
|
||||
</style>
|
||||
480
pro_v3.5.1/view/uniapp/pages/queue/status.vue
Normal file
480
pro_v3.5.1/view/uniapp/pages/queue/status.vue
Normal file
@@ -0,0 +1,480 @@
|
||||
<template>
|
||||
<view class="queue-status-page" :style="colorStyle">
|
||||
<view class="header-gradient">
|
||||
<view class="header-gradient__bg-circle header-gradient__bg-circle--1"></view>
|
||||
<view class="header-gradient__bg-circle header-gradient__bg-circle--2"></view>
|
||||
|
||||
<view class="header-card">
|
||||
<view class="header-card__label">公排池总单数</view>
|
||||
<view class="header-card__total">{{ queueStatus.totalOrders || 0 }}</view>
|
||||
<view class="header-card__progress" v-if="queueStatus.progress">
|
||||
<HjfQueueProgress
|
||||
:current-count="queueStatus.progress.current_batch_count"
|
||||
:trigger-multiple="queueStatus.progress.trigger_multiple"
|
||||
:next-refund-no="queueStatus.progress.next_refund_queue_no"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="order-list-section">
|
||||
<view class="section-header">
|
||||
<view class="section-header__dot"></view>
|
||||
<view class="section-header__title">我的排队订单</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="loading-wrap">
|
||||
<view class="loading-dots">
|
||||
<view class="loading-dot loading-dot--1"></view>
|
||||
<view class="loading-dot loading-dot--2"></view>
|
||||
<view class="loading-dot loading-dot--3"></view>
|
||||
</view>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="!queueStatus.myOrders || queueStatus.myOrders.length === 0" class="empty-wrap">
|
||||
<emptyPage title="暂无排队记录~" src="/statics/images/noOrder.gif" />
|
||||
</view>
|
||||
|
||||
<view v-else class="order-list">
|
||||
<view
|
||||
v-for="(order, index) in queueStatus.myOrders"
|
||||
:key="order.id || index"
|
||||
class="order-item"
|
||||
>
|
||||
<view class="order-item__top">
|
||||
<view class="order-item__no">
|
||||
<text class="order-item__no-hash">#</text>
|
||||
<text class="order-item__no-value">{{ order.queue_no }}</text>
|
||||
</view>
|
||||
<view
|
||||
class="order-item__tag"
|
||||
:class="order.status === 0 ? 'order-item__tag--active' : 'order-item__tag--refunded'"
|
||||
>
|
||||
<view class="order-item__tag-dot" :class="order.status === 0 ? 'order-item__tag-dot--active' : 'order-item__tag-dot--refunded'"></view>
|
||||
{{ order.status === 0 ? '排队中' : '已退款' }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="order-item__id">{{ order.order_id }}</view>
|
||||
|
||||
<view class="order-item__bottom">
|
||||
<text class="order-item__amount">¥{{ Number(order.amount).toFixed(2) }}</text>
|
||||
<text class="order-item__wait">{{ order.estimated_wait }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="queueStatus.myOrders && queueStatus.myOrders.length > 0" class="load-more-bar">
|
||||
<text v-if="loadingMore" class="load-more-text">加载中...</text>
|
||||
<text v-else-if="finished" class="load-more-text">没有更多内容啦~</text>
|
||||
<text v-else class="load-more-text">上拉加载更多</text>
|
||||
</view>
|
||||
|
||||
<HjfRefundNotice
|
||||
:visible="showRefund"
|
||||
:amount="refundData.amount"
|
||||
:order-id="refundData.orderId"
|
||||
@close="handleRefundClose"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file pages/queue/status.vue
|
||||
* @description P12 公排状态页 — 展示公排池总单数、当前批次进度及我的排队订单列表,
|
||||
* 并在退款到账时弹出 HjfRefundNotice 通知。
|
||||
* @see docs/frontend-new-pages-spec.md 第 2.2.4 节
|
||||
*/
|
||||
|
||||
import { getQueueStatus } from '@/api/hjfQueue.js';
|
||||
import HjfQueueProgress from '@/components/HjfQueueProgress.vue';
|
||||
import HjfRefundNotice from '@/components/HjfRefundNotice.vue';
|
||||
import emptyPage from '@/components/emptyPage.vue';
|
||||
import colors from '@/mixins/color.js';
|
||||
|
||||
export default {
|
||||
name: 'QueueStatus',
|
||||
|
||||
mixins: [colors],
|
||||
|
||||
components: {
|
||||
HjfQueueProgress,
|
||||
HjfRefundNotice,
|
||||
emptyPage
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 公排状态数据,包含 totalOrders、myOrders、progress
|
||||
* @type {{ totalOrders: number, myOrders: Array, progress: Object }}
|
||||
*/
|
||||
queueStatus: {},
|
||||
|
||||
/**
|
||||
* 是否正在加载数据
|
||||
* @type {boolean}
|
||||
*/
|
||||
loading: false,
|
||||
|
||||
/**
|
||||
* 是否显示退款通知弹窗
|
||||
* @type {boolean}
|
||||
*/
|
||||
showRefund: false,
|
||||
|
||||
/**
|
||||
* 退款弹窗所需数据
|
||||
* @type {{ amount: number, orderId: string }}
|
||||
*/
|
||||
refundData: {
|
||||
amount: 0,
|
||||
orderId: ''
|
||||
},
|
||||
|
||||
/** @type {boolean} 是否正在上拉加载更多 */
|
||||
loadingMore: false,
|
||||
|
||||
/** @type {boolean} 是否已加载完全部数据 */
|
||||
finished: false
|
||||
};
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadQueueStatus();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.checkPendingRefundNotice();
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.loadMoreOrders();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 加载公排状态数据
|
||||
* 调用 getQueueStatus(),将返回值赋给 queueStatus,
|
||||
* 并在检测到已退款订单时触发退款弹窗。
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadQueueStatus() {
|
||||
this.loading = true;
|
||||
getQueueStatus()
|
||||
.then(res => {
|
||||
if (res && res.data) {
|
||||
this.$set(this, 'queueStatus', res.data);
|
||||
this.detectNewRefund(res.data.myOrders || []);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[QueueStatus] loadQueueStatus error:', err);
|
||||
uni.showToast({ title: '加载失败,请稍后重试', icon: 'none' });
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 检测是否存在刚完成退款的订单,有则弹出退款通知。
|
||||
* 策略:取 status=1 且 refund_time 最大的一条(最近退款),
|
||||
* 结合页面跳转参数 show_refund=1 触发弹窗。
|
||||
* @param {Array} orders - 我的排队订单列表
|
||||
*/
|
||||
detectNewRefund(orders) {
|
||||
const refunded = orders.filter(o => o.status === 1 && o.refund_time > 0);
|
||||
if (!refunded.length) return;
|
||||
refunded.sort((a, b) => b.refund_time - a.refund_time);
|
||||
const latest = refunded[0];
|
||||
const showParam = this._pageParams && this._pageParams.show_refund;
|
||||
if (showParam === '1') {
|
||||
this.refundData = {
|
||||
amount: latest.amount,
|
||||
orderId: latest.order_id
|
||||
};
|
||||
this.showRefund = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面显示时检查是否需要弹出退款通知(从外部跳转携带参数时使用)
|
||||
*/
|
||||
checkPendingRefundNotice() {
|
||||
const pages = getCurrentPages();
|
||||
const current = pages[pages.length - 1];
|
||||
const options = (current && current.options) || {};
|
||||
if (options.show_refund === '1' && this.queueStatus.myOrders) {
|
||||
this.detectNewRefund(this.queueStatus.myOrders);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 上拉加载更多(当前数据由单次接口全量返回,模拟已到底状态)
|
||||
* Phase 4 接入分页接口后替换为真实分页逻辑。
|
||||
*/
|
||||
loadMoreOrders() {
|
||||
if (this.loadingMore || this.finished) return;
|
||||
this.loadingMore = true;
|
||||
setTimeout(() => {
|
||||
this.finished = true;
|
||||
this.loadingMore = false;
|
||||
}, 500);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭退款通知弹窗
|
||||
*/
|
||||
handleRefundClose() {
|
||||
this.showRefund = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.queue-status-page {
|
||||
min-height: 100vh;
|
||||
background: #f4f5f7;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.header-gradient {
|
||||
background: linear-gradient(135deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 100%);
|
||||
padding: 36rpx 30rpx 48rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header-gradient__bg-circle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
opacity: 0.06;
|
||||
}
|
||||
|
||||
.header-gradient__bg-circle--1 {
|
||||
width: 400rpx;
|
||||
height: 400rpx;
|
||||
top: -160rpx;
|
||||
right: -100rpx;
|
||||
}
|
||||
|
||||
.header-gradient__bg-circle--2 {
|
||||
width: 240rpx;
|
||||
height: 240rpx;
|
||||
bottom: -60rpx;
|
||||
left: -50rpx;
|
||||
}
|
||||
|
||||
.header-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background: rgba(255, 255, 255, 0.14);
|
||||
border-radius: 28rpx;
|
||||
padding: 36rpx 32rpx;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.header-card__label {
|
||||
font-size: 26rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.header-card__total {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
font-family: 'SemiBold', sans-serif;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.header-card__progress {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 18rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.order-list-section {
|
||||
margin: -16rpx 20rpx 0;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 24rpx 4rpx 20rpx;
|
||||
}
|
||||
|
||||
.section-header__dot {
|
||||
width: 8rpx;
|
||||
height: 30rpx;
|
||||
border-radius: 4rpx;
|
||||
background: var(--view-theme, #e93323);
|
||||
margin-right: 14rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.section-header__title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.loading-wrap {
|
||||
padding: 60rpx 0;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.loading-dots {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.loading-dot {
|
||||
width: 14rpx;
|
||||
height: 14rpx;
|
||||
border-radius: 50%;
|
||||
background: var(--view-theme, #e93323);
|
||||
animation: dot-bounce 1.2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.loading-dot--2 { animation-delay: 0.2s; }
|
||||
.loading-dot--3 { animation-delay: 0.4s; }
|
||||
|
||||
@keyframes dot-bounce {
|
||||
0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
|
||||
40% { opacity: 1; transform: scale(1.2); }
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-wrap {
|
||||
padding: 40rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 30rpx 32rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.order-item__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.order-item__no {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.order-item__no-hash {
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: var(--view-theme, #e93323);
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
|
||||
.order-item__no-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
font-family: 'SemiBold', sans-serif;
|
||||
}
|
||||
|
||||
.order-item__tag {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.order-item__tag-dot {
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.order-item__tag--active {
|
||||
background: #e6f7ee;
|
||||
color: #389e0d;
|
||||
}
|
||||
|
||||
.order-item__tag-dot--active {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.order-item__tag--refunded {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.order-item__tag-dot--refunded {
|
||||
background: #bbb;
|
||||
}
|
||||
|
||||
.order-item__id {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
padding-bottom: 18rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.order-item__bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 18rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.order-item__amount {
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
font-family: 'SemiBold', sans-serif;
|
||||
}
|
||||
|
||||
.order-item__wait {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
background: #fafafa;
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.load-more-bar {
|
||||
padding: 32rpx 0 48rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.load-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #aaa;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user