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:
50
pro_v3.5.1/view/uniapp/README-RUN.md
Normal file
50
pro_v3.5.1/view/uniapp/README-RUN.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 黄精粉小程序 UniApp 运行说明
|
||||
|
||||
## 推荐方式:使用 HBuilderX 运行到浏览器(H5)
|
||||
|
||||
本 CRMEB 项目的小程序端按 HBuilderX + uni-app 标准目录设计,**推荐使用 HBuilderX 进行开发与预览**:
|
||||
|
||||
1. 安装 [HBuilderX](https://www.dcloud.io/hbuilderx.html)(正式版或 App 开发版)。
|
||||
2. 用 HBuilderX 打开本目录:`pro_v3.5.1/view/uniapp`(即当前 package.json 所在目录)。
|
||||
3. 在菜单栏选择 **运行 → 运行到浏览器 → 选择 Chrome(或其他浏览器)**。
|
||||
4. 等待编译完成后,会自动打开浏览器,即可查看 Mock 演示效果。
|
||||
|
||||
在 H5 预览下可看到:
|
||||
- 首页 / 个人中心右下角的 **演示控制面板**(紫色悬浮按钮)
|
||||
- 切换场景 A/B/C、快捷跳转、退款弹窗等 Mock 演示功能
|
||||
|
||||
---
|
||||
|
||||
## 命令行方式(可选)
|
||||
|
||||
已在 `package.json` 中配置脚本与依赖,满足以下条件时可尝试命令行启动 H5:
|
||||
|
||||
- 需在 **本目录**(`view/uniapp`)下执行。
|
||||
- 若使用 Vue CLI 5,可能与当前 uni-app 插件的 webpack 规则不兼容,出现 `No matching use for foo.js` 或 `reading 'use'` 等错误,此时请改用 HBuilderX。
|
||||
|
||||
### 启动 H5 开发服务
|
||||
|
||||
```bash
|
||||
cd pro_v3.5.1/view/uniapp
|
||||
|
||||
# 设置环境变量并启动(Mac/Linux)
|
||||
UNI_CLI_CONTEXT=$(pwd) UNI_PLATFORM=h5 UNI_INPUT_DIR=$(pwd) npx vue-cli-service uni-serve --platform h5
|
||||
|
||||
# 或使用 npm 脚本(需已安装 cross-env)
|
||||
npm run dev:h5
|
||||
```
|
||||
|
||||
默认端口:**8080**(在 `vue.config.js` 的 `devServer.port` 中配置)。
|
||||
启动成功后,在浏览器访问:**http://localhost:8080**。
|
||||
|
||||
### 微信小程序开发
|
||||
|
||||
使用 HBuilderX 打开本目录后,选择 **运行 → 运行到小程序模拟器 → 微信开发者工具**,并先在本地打开微信开发者工具。
|
||||
|
||||
---
|
||||
|
||||
## Mock 演示说明
|
||||
|
||||
- 所有 HJF 相关接口当前使用 **Mock 数据**(`utils/hjfMockData.js`),无需后端即可演示。
|
||||
- 右下角 **演示控制面板** 可切换场景 A(新用户)/ B(活跃用户)/ C(VIP),并支持快捷跳转、退款弹窗、重置引导等。
|
||||
- 完整演示路线与验收点见:**docs/mock-demo-walkthrough.md**。
|
||||
78
pro_v3.5.1/view/uniapp/api/hjfAssets.js
Normal file
78
pro_v3.5.1/view/uniapp/api/hjfAssets.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 黄精粉健康商城 - 资产相关 API
|
||||
* 资产概览、积分明细、现金明细、提现信息及申请提现
|
||||
* @module api/hjfAssets
|
||||
*/
|
||||
|
||||
import request from '@/utils/request.js';
|
||||
import {
|
||||
getMockAssetsOverview,
|
||||
getMockPointsDetail,
|
||||
getMockCashDetail,
|
||||
getMockWithdrawInfo
|
||||
} from '@/utils/hjfMockData.js';
|
||||
|
||||
/** @type {boolean} 是否使用 Mock 数据(Phase 1 开发为 true,Phase 4 集成改为 false) */
|
||||
const USE_MOCK = true;
|
||||
|
||||
/**
|
||||
* Mock 包装:返回与 request 相同形状的 Promise(status + data),带延迟模拟网络
|
||||
* @param {*} data - 要返回的响应体
|
||||
* @param {number} [delay=300] - 延迟毫秒数
|
||||
* @returns {Promise<{ status: number, data: * }>}
|
||||
*/
|
||||
function mockResponse(data, delay = 300) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({ status: 200, data: JSON.parse(JSON.stringify(data)) });
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资产概览(余额、冻结/可用积分、今日释放、公排总退款等)
|
||||
* @returns {Promise<{ status: number, data: Object }>}
|
||||
*/
|
||||
export function getAssetsOverview() {
|
||||
if (USE_MOCK) return mockResponse(getMockAssetsOverview());
|
||||
return request.get('hjf/assets/overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取积分明细(分页)
|
||||
* @param {Object} [params] - 查询参数,如 page、limit
|
||||
* @returns {Promise<{ status: number, data: Object }>}
|
||||
*/
|
||||
export function getPointsDetail(params) {
|
||||
if (USE_MOCK) return mockResponse(getMockPointsDetail());
|
||||
return request.get('hjf/assets/points_detail', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取现金明细(分页)
|
||||
* @param {Object} [params] - 查询参数,如 page、limit
|
||||
* @returns {Promise<{ status: number, data: Object }>}
|
||||
*/
|
||||
export function getCashDetail(params) {
|
||||
if (USE_MOCK) return mockResponse(getMockCashDetail());
|
||||
return request.get('hjf/assets/cash_detail', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现信息(可提现余额、最低金额、手续费率、渠道列表等)
|
||||
* @returns {Promise<{ status: number, data: Object }>}
|
||||
*/
|
||||
export function getWithdrawInfo() {
|
||||
if (USE_MOCK) return mockResponse(getMockWithdrawInfo());
|
||||
return request.get('hjf/assets/withdraw_info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请提现(POST)
|
||||
* @param {Object} data - 提现参数,如 amount、channel、bank_id 等
|
||||
* @returns {Promise<{ status: number, data?: Object }>}
|
||||
*/
|
||||
export function applyWithdraw(data) {
|
||||
if (USE_MOCK) return mockResponse({ success: true, msg: '提现申请已提交' });
|
||||
return request.post('hjf/assets/withdraw', data);
|
||||
}
|
||||
63
pro_v3.5.1/view/uniapp/api/hjfMember.js
Normal file
63
pro_v3.5.1/view/uniapp/api/hjfMember.js
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 黄精粉健康商城 - 会员模块 API
|
||||
* 会员信息、团队数据、团队收益
|
||||
* @module api/hjfMember
|
||||
*/
|
||||
|
||||
import request from '@/utils/request.js';
|
||||
import {
|
||||
getMockMemberInfo,
|
||||
getMockTeamData,
|
||||
getMockTeamIncome
|
||||
} from '@/utils/hjfMockData.js';
|
||||
|
||||
/** @type {boolean} Phase 1 前端开发为 true,Phase 4 集成时改为 false */
|
||||
const USE_MOCK = true;
|
||||
|
||||
/**
|
||||
* Mock 包装:返回与 request.get() 相同形状的 Promise
|
||||
* @param {Object} data - 要返回的响应体数据
|
||||
* @param {number} [delay=300] - 模拟网络延迟(毫秒)
|
||||
* @returns {Promise<{ status: number, data: Object }>}
|
||||
*/
|
||||
function mockResponse(data, delay = 300) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({ status: 200, data: JSON.parse(JSON.stringify(data)) });
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员信息(等级、直推/伞下人数、升级进度等)
|
||||
* @returns {Promise<{ status: number, data: Object }>} 会员信息
|
||||
*/
|
||||
export function getMemberInfo() {
|
||||
if (USE_MOCK) return mockResponse(getMockMemberInfo());
|
||||
return request.get('hjf/member/info');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队成员列表(直推/伞下成员、分页)
|
||||
* @param {Object} [params] - 查询参数
|
||||
* @param {number} [params.page] - 页码
|
||||
* @param {number} [params.limit] - 每页条数
|
||||
* @param {string} [params.type] - 筛选类型(如 direct/umbrella)
|
||||
* @returns {Promise<{ status: number, data: Object }>} 团队数据
|
||||
*/
|
||||
export function getTeamData(params) {
|
||||
if (USE_MOCK) return mockResponse(getMockTeamData());
|
||||
return request.get('hjf/member/team', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队收益明细(直推奖励、伞下奖励等)
|
||||
* @param {Object} [params] - 查询参数
|
||||
* @param {number} [params.page] - 页码
|
||||
* @param {number} [params.limit] - 每页条数
|
||||
* @returns {Promise<{ status: number, data: Object }>} 团队收益列表
|
||||
*/
|
||||
export function getTeamIncome(params) {
|
||||
if (USE_MOCK) return mockResponse(getMockTeamIncome());
|
||||
return request.get('hjf/member/income', params);
|
||||
}
|
||||
45
pro_v3.5.1/view/uniapp/api/hjfQueue.js
Normal file
45
pro_v3.5.1/view/uniapp/api/hjfQueue.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 黄精粉健康商城 - 公排相关 API
|
||||
* 公排状态、公排历史记录
|
||||
* @see docs/frontend-new-pages-spec.md 2.2.1
|
||||
*/
|
||||
|
||||
import request from '@/utils/request.js';
|
||||
import { getMockQueueStatus, getMockQueueHistory } from '@/utils/hjfMockData.js';
|
||||
|
||||
/** @type {boolean} 是否使用 Mock 数据(Phase 4 集成时改为 false) */
|
||||
const USE_MOCK = true;
|
||||
|
||||
/**
|
||||
* Mock 包装:返回与 request.get() 相同形状的 Promise
|
||||
* 300ms 延迟模拟网络,JSON 深拷贝防止数据突变
|
||||
* @param {*} data - 要返回的数据
|
||||
* @param {number} [delay=300] - 延迟毫秒数
|
||||
* @returns {Promise<{ status: number, data: * }>}
|
||||
*/
|
||||
function mockResponse(data, delay = 300) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve({ status: 200, data: JSON.parse(JSON.stringify(data)) });
|
||||
}, delay);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公排状态(我的排队 + 全局进度)
|
||||
* @returns {Promise<{ status: number, data: { totalOrders: number, myOrders: Array, progress: Object } }>}
|
||||
*/
|
||||
export function getQueueStatus() {
|
||||
if (USE_MOCK) return mockResponse(getMockQueueStatus());
|
||||
return request.get('hjf/queue/status');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取公排历史记录(分页)
|
||||
* @param {Object} [params] - 查询参数,如 page、limit
|
||||
* @returns {Promise<{ status: number, data: { list: Array, count: number, page: number, limit: number } }>}
|
||||
*/
|
||||
export function getQueueHistory(params) {
|
||||
if (USE_MOCK) return mockResponse(getMockQueueHistory());
|
||||
return request.get('hjf/queue/history', params);
|
||||
}
|
||||
147
pro_v3.5.1/view/uniapp/components/HjfAssetCard.vue
Normal file
147
pro_v3.5.1/view/uniapp/components/HjfAssetCard.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<view class="hjf-asset-card">
|
||||
<view class="card-row">
|
||||
<view class="card-item">
|
||||
<view class="label">现金余额(¥)</view>
|
||||
<view class="value">{{ formattedNowMoney }}</view>
|
||||
</view>
|
||||
<view class="card-item">
|
||||
<view class="label">待释放积分</view>
|
||||
<view class="value">{{ formattedFrozenPoints }}</view>
|
||||
</view>
|
||||
<view class="card-item">
|
||||
<view class="label">已释放积分</view>
|
||||
<view class="value">{{ formattedAvailablePoints }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="todayRelease != null" class="card-footer">
|
||||
<view class="footer-text">今日预计释放 {{ formattedTodayRelease }} 积分</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file HjfAssetCard.vue
|
||||
* @description 三栏资产展示卡片(现金余额 / 待释放积分 / 已释放积分),渐变背景。用于资产总览等页面。
|
||||
* @see docs/frontend-new-pages-spec.md 第 3.2.3 节
|
||||
* @see pages/users/user_money/index.vue 渐变卡片区域
|
||||
*/
|
||||
export default {
|
||||
name: 'HjfAssetCard',
|
||||
|
||||
props: {
|
||||
/**
|
||||
* 现金余额(元),字符串便于对接接口返回
|
||||
* @type {string}
|
||||
* @default '0.00'
|
||||
*/
|
||||
nowMoney: {
|
||||
type: String,
|
||||
default: '0.00'
|
||||
},
|
||||
/**
|
||||
* 待释放积分
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
frozenPoints: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 已释放积分
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
availablePoints: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 今日预计释放积分(不传则不显示底部提示)
|
||||
* @type {number}
|
||||
* @default null
|
||||
*/
|
||||
todayRelease: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/** 格式化后的现金余额,保留两位小数 */
|
||||
formattedNowMoney() {
|
||||
const num = parseFloat(this.nowMoney);
|
||||
return isNaN(num) ? '0.00' : num.toFixed(2);
|
||||
},
|
||||
/** 格式化后的待释放积分 */
|
||||
formattedFrozenPoints() {
|
||||
return Number(this.frozenPoints).toLocaleString();
|
||||
},
|
||||
/** 格式化后的已释放积分 */
|
||||
formattedAvailablePoints() {
|
||||
return Number(this.availablePoints).toLocaleString();
|
||||
},
|
||||
/** 格式化后的今日预计释放 */
|
||||
formattedTodayRelease() {
|
||||
if (this.todayRelease == null) return '';
|
||||
return Number(this.todayRelease).toLocaleString();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hjf-asset-card {
|
||||
width: 710rpx;
|
||||
margin: 0 auto;
|
||||
background: linear-gradient(90deg, var(--view-theme, #e93323) 0%, var(--view-gradient, #f76b1c) 100%);
|
||||
border-radius: 32rpx;
|
||||
box-sizing: border-box;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
font-size: 24rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 36rpx 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.card-item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
padding: 0 8rpx;
|
||||
|
||||
.label {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 40rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
font-family: 'SemiBold', sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
width: 100%;
|
||||
padding: 20rpx 32rpx 24rpx;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
text-align: center;
|
||||
|
||||
.footer-text {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
385
pro_v3.5.1/view/uniapp/components/HjfDemoPanel.vue
Normal file
385
pro_v3.5.1/view/uniapp/components/HjfDemoPanel.vue
Normal file
@@ -0,0 +1,385 @@
|
||||
<template>
|
||||
<view v-if="showPanel" class="hjf-demo-container">
|
||||
<!-- 悬浮按钮 -->
|
||||
<view v-if="!expanded" class="demo-fab" @tap="togglePanel">
|
||||
<text class="iconfont icon-shezhi fs-44 text-white"></text>
|
||||
</view>
|
||||
|
||||
<!-- 展开的控制面板 -->
|
||||
<view v-if="expanded" class="demo-panel">
|
||||
<view class="panel-header">
|
||||
<text class="panel-title">演示控制面板</text>
|
||||
<text class="iconfont icon-ic_close fs-40" @tap="togglePanel"></text>
|
||||
</view>
|
||||
|
||||
<view class="panel-body">
|
||||
<!-- 场景切换 -->
|
||||
<view class="section">
|
||||
<view class="section-title">当前场景:{{ scenarioName }}</view>
|
||||
<view class="scenario-btns">
|
||||
<view
|
||||
v-for="s in scenarios"
|
||||
:key="s.id"
|
||||
:class="['scenario-btn', currentScenario === s.id ? 'active' : '']"
|
||||
@tap="switchScenario(s.id)"
|
||||
>
|
||||
<text class="scenario-label">{{ s.name }}</text>
|
||||
<text class="scenario-desc">{{ s.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 特殊操作 -->
|
||||
<view class="section">
|
||||
<view class="section-title">特殊操作</view>
|
||||
<view class="action-btns">
|
||||
<view class="action-btn" @tap="triggerRefundNotice">
|
||||
<text class="iconfont icon-qianbao fs-36"></text>
|
||||
<text class="action-label">退款弹窗</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="clearGuideFlag">
|
||||
<text class="iconfont icon-shuaxin fs-36"></text>
|
||||
<text class="action-label">重置引导</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快捷跳转 -->
|
||||
<view class="section">
|
||||
<view class="section-title">快捷跳转</view>
|
||||
<scroll-view scroll-y class="nav-scroll">
|
||||
<view
|
||||
v-for="nav in quickNavs"
|
||||
:key="nav.path"
|
||||
class="nav-item"
|
||||
@tap="navigateTo(nav.path)"
|
||||
>
|
||||
<text class="nav-label">{{ nav.label }}</text>
|
||||
<text class="iconfont icon-ic_rightarrow fs-28"></text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<view v-if="expanded" class="demo-mask" @tap="togglePanel"></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { setMockScenario, getCurrentScenario } from '@/utils/hjfMockData.js';
|
||||
|
||||
export default {
|
||||
name: 'HjfDemoPanel',
|
||||
data() {
|
||||
return {
|
||||
showPanel: false,
|
||||
expanded: false,
|
||||
currentScenario: 'B',
|
||||
scenarios: [
|
||||
{
|
||||
id: 'A',
|
||||
name: '场景 A',
|
||||
desc: '新用户首次体验'
|
||||
},
|
||||
{
|
||||
id: 'B',
|
||||
name: '场景 B',
|
||||
desc: '活跃用户等待退款'
|
||||
},
|
||||
{
|
||||
id: 'C',
|
||||
name: '场景 C',
|
||||
desc: 'VIP用户刚退款'
|
||||
}
|
||||
],
|
||||
quickNavs: [
|
||||
{ label: 'P23 新用户引导', path: '/pages/guide/hjf_intro' },
|
||||
{ label: 'P01 首页', path: '/pages/index/index' },
|
||||
{ label: 'P04 个人中心', path: '/pages/user/index' },
|
||||
{ label: 'P12 公排状态', path: '/pages/queue/status' },
|
||||
{ label: 'P13 公排历史', path: '/pages/queue/history' },
|
||||
{ label: 'P14 公排规则', path: '/pages/queue/rules' },
|
||||
{ label: 'P15 我的资产', path: '/pages/assets/index' },
|
||||
{ label: 'P18 积分明细', path: '/pages/assets/points_detail' },
|
||||
{ label: 'P16 提现页', path: '/pages/users/user_cash/index' },
|
||||
{ label: 'P19 推荐收益', path: '/pages/users/user_spread_money/index' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
scenarioName() {
|
||||
const scenario = this.scenarios.find(s => s.id === this.currentScenario);
|
||||
return scenario ? `${scenario.name} - ${scenario.desc}` : '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 仅在开发环境显示
|
||||
// #ifdef H5
|
||||
this.showPanel = process.env.NODE_ENV !== 'production';
|
||||
// #endif
|
||||
// #ifndef H5
|
||||
this.showPanel = true; // 小程序默认显示,用于演示
|
||||
// #endif
|
||||
|
||||
// 获取当前场景
|
||||
this.currentScenario = getCurrentScenario();
|
||||
|
||||
console.log('[HjfDemoPanel] 演示控制面板已加载,当前场景:', this.currentScenario);
|
||||
},
|
||||
methods: {
|
||||
togglePanel() {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
|
||||
switchScenario(scenarioId) {
|
||||
if (scenarioId === this.currentScenario) return;
|
||||
|
||||
const success = setMockScenario(scenarioId);
|
||||
if (success) {
|
||||
this.currentScenario = scenarioId;
|
||||
uni.showToast({
|
||||
title: `已切换到场景 ${scenarioId}`,
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
// 延迟500ms后刷新当前页面
|
||||
setTimeout(() => {
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1];
|
||||
// 触发页面的 onShow 方法刷新数据
|
||||
if (currentPage.$vm && typeof currentPage.$vm.$options.onShow === 'function') {
|
||||
currentPage.$vm.$options.onShow.call(currentPage.$vm);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
triggerRefundNotice() {
|
||||
uni.showToast({
|
||||
title: '跳转到公排状态页查看退款弹窗',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/queue/status?show_refund=1'
|
||||
});
|
||||
this.expanded = false;
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
clearGuideFlag() {
|
||||
uni.removeStorageSync('hjf_guide_read');
|
||||
uni.showToast({
|
||||
title: '引导标记已清除',
|
||||
icon: 'success'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/guide/hjf_intro'
|
||||
});
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
navigateTo(path) {
|
||||
this.expanded = false;
|
||||
|
||||
// 处理 TabBar 页面
|
||||
const tabBarPages = ['/pages/index/index', '/pages/user/index'];
|
||||
if (tabBarPages.includes(path)) {
|
||||
uni.switchTab({ url: path });
|
||||
} else {
|
||||
uni.navigateTo({ url: path });
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hjf-demo-container {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.demo-fab {
|
||||
position: fixed;
|
||||
right: 30rpx;
|
||||
bottom: 200rpx;
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
box-shadow: 0 8rpx 20rpx rgba(102, 126, 234, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.demo-fab:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.demo-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
.demo-panel {
|
||||
position: fixed;
|
||||
right: 30rpx;
|
||||
bottom: 200rpx;
|
||||
width: 600rpx;
|
||||
max-height: 1000rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
animation: slideIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20rpx);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 32rpx;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.panel-body {
|
||||
max-height: 880rpx;
|
||||
overflow-y: auto;
|
||||
padding: 0 0 20rpx 0;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 24rpx 32rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.scenario-btns {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.scenario-btn {
|
||||
padding: 24rpx 20rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.scenario-btn.active {
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.scenario-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.scenario-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.action-btns {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.action-btn:active {
|
||||
background: #e8e8e8;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.action-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.nav-scroll {
|
||||
max-height: 400rpx;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 20rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 12rpx;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-item:active {
|
||||
background: #e8e8e8;
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
185
pro_v3.5.1/view/uniapp/components/HjfMemberBadge.vue
Normal file
185
pro_v3.5.1/view/uniapp/components/HjfMemberBadge.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<view class="hjf-member-badge" :class="sizeClass">
|
||||
<view class="badge-icon" :style="iconStyle">
|
||||
<text class="icon-text">{{ levelText }}</text>
|
||||
</view>
|
||||
<text class="badge-name">{{ displayName }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 会员等级颜色映射(0-4 对应文档 3.2.4)
|
||||
* @constant
|
||||
* @type {Object<number, string>}
|
||||
*/
|
||||
const LEVEL_COLORS = {
|
||||
0: '#999999', // 普通
|
||||
1: '#CD7F32', // 创客
|
||||
2: '#C0C0C0', // 云店
|
||||
3: '#FFD700', // 服务商
|
||||
4: '#8B5CF6' // 分公司
|
||||
};
|
||||
|
||||
/**
|
||||
* 等级默认名称(level 无 levelName 时回退)
|
||||
* @constant
|
||||
* @type {string[]}
|
||||
*/
|
||||
const LEVEL_NAMES = ['普通', '创客', '云店', '服务商', '分公司'];
|
||||
|
||||
/**
|
||||
* HjfMemberBadge — 会员等级徽章组件
|
||||
*
|
||||
* 展示会员等级图标(圆形数字徽)+ 等级名称,支持三种尺寸与五档等级颜色。
|
||||
* 参考:docs/frontend-new-pages-spec.md 第 3.2.4 节。
|
||||
*
|
||||
* @component HjfMemberBadge
|
||||
* @example
|
||||
* <HjfMemberBadge :level="2" levelName="云店" size="normal" />
|
||||
*/
|
||||
export default {
|
||||
name: 'HjfMemberBadge',
|
||||
props: {
|
||||
/**
|
||||
* 会员等级数字 (0-4)
|
||||
* 0: 普通, 1: 创客, 2: 云店, 3: 服务商, 4: 分公司
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
level: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator(val) {
|
||||
return val >= 0 && val <= 4;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 等级名称展示文案(可选,不传则按 level 回退为默认名称)
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
levelName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 尺寸:'small' | 'normal' | 'large'
|
||||
* @type {'small'|'normal'|'large'}
|
||||
* @default 'normal'
|
||||
*/
|
||||
size: {
|
||||
type: String,
|
||||
default: 'normal',
|
||||
validator(val) {
|
||||
return ['small', 'normal', 'large'].indexOf(val) !== -1;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** 尺寸类名,用于 .size-small / .size-normal / .size-large */
|
||||
sizeClass() {
|
||||
return `size-${this.size}`;
|
||||
},
|
||||
/** 当前等级对应的主题色 */
|
||||
levelColor() {
|
||||
const key = Math.min(4, Math.max(0, this.level));
|
||||
return LEVEL_COLORS[key] || LEVEL_COLORS[0];
|
||||
},
|
||||
/** 徽章图标内联样式(背景色、边框色) */
|
||||
iconStyle() {
|
||||
return {
|
||||
backgroundColor: this.levelColor,
|
||||
borderColor: this.levelColor
|
||||
};
|
||||
},
|
||||
/** 最终展示的等级名称(优先 levelName,否则 LEVEL_NAMES[level]) */
|
||||
displayName() {
|
||||
if (this.levelName && this.levelName.trim()) {
|
||||
return this.levelName.trim();
|
||||
}
|
||||
const key = Math.min(4, Math.max(0, this.level));
|
||||
return LEVEL_NAMES[key] || LEVEL_NAMES[0];
|
||||
},
|
||||
/** 徽章内显示的等级数字文案 */
|
||||
levelText() {
|
||||
const key = Math.min(4, Math.max(0, this.level));
|
||||
return String(key);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hjf-member-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.badge-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
border: 2rpx solid;
|
||||
flex-shrink: 0;
|
||||
|
||||
.icon-text {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.badge-name {
|
||||
margin-left: 8rpx;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.size-small {
|
||||
.badge-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
|
||||
.icon-text {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-name {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-normal {
|
||||
.badge-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
|
||||
.icon-text {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-name {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.size-large {
|
||||
.badge-icon {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
|
||||
.icon-text {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-name {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
177
pro_v3.5.1/view/uniapp/components/HjfQueueProgress.vue
Normal file
177
pro_v3.5.1/view/uniapp/components/HjfQueueProgress.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<view class="hjf-queue-progress">
|
||||
<!-- 环形进度 -->
|
||||
<view class="progress-ring-wrap">
|
||||
<view class="progress-ring" :style="ringStyle">
|
||||
<view class="progress-ring-inner">
|
||||
<text class="progress-text">{{ currentCount }}/{{ triggerMultiple }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 条形进度 -->
|
||||
<view class="progress-bar-wrap">
|
||||
<view class="progress-bar-track">
|
||||
<view class="progress-bar-fill" :style="barStyle" />
|
||||
</view>
|
||||
<view class="progress-label">
|
||||
<text>当前批次进度</text>
|
||||
<text class="progress-value">{{ currentCount }}/{{ triggerMultiple }}</text>
|
||||
</view>
|
||||
<view v-if="nextRefundNo != null" class="next-refund">
|
||||
下一退款序号: {{ nextRefundNo }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file HjfQueueProgress.vue
|
||||
* @description 公排批次进度组件:环形/条形进度条展示当前批次进度(如 2/4),并显示下一个退款序号。
|
||||
* @see docs/frontend-new-pages-spec.md 第 2.2.2 节
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'HjfQueueProgress',
|
||||
props: {
|
||||
/**
|
||||
* 当前批次已入队数
|
||||
* @type {number}
|
||||
*/
|
||||
currentCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 触发倍数(每多少人触发一批退款,默认 4)
|
||||
* @type {number}
|
||||
*/
|
||||
triggerMultiple: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
/**
|
||||
* 下一个退款的 queue_no(可选,有值时显示「下一退款序号」)
|
||||
* @type {number|null}
|
||||
*/
|
||||
nextRefundNo: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 进度百分比(0–100),用于条形/环形展示
|
||||
* @returns {number}
|
||||
*/
|
||||
progressPercent() {
|
||||
const total = this.triggerMultiple;
|
||||
if (!total || total <= 0) return 0;
|
||||
const p = Math.min(100, (this.currentCount / total) * 100);
|
||||
return Math.round(p * 10) / 10;
|
||||
},
|
||||
/**
|
||||
* 环形进度样式(conic-gradient 用 progressPercent)
|
||||
* @returns {Object}
|
||||
*/
|
||||
ringStyle() {
|
||||
return {
|
||||
'--progress-percent': this.progressPercent,
|
||||
'--progress-color': 'var(--view-theme)'
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 条形进度填充宽度与主题色
|
||||
* @returns {Object}
|
||||
*/
|
||||
barStyle() {
|
||||
return {
|
||||
width: this.progressPercent + '%',
|
||||
backgroundColor: 'var(--view-theme)'
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hjf-queue-progress {
|
||||
padding: 24rpx;
|
||||
}
|
||||
|
||||
.progress-ring-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.progress-ring {
|
||||
--progress-percent: 0;
|
||||
--progress-color: var(--view-theme);
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(
|
||||
var(--progress-color) 0deg,
|
||||
var(--progress-color) calc(var(--progress-percent) * 3.6deg),
|
||||
#eee calc(var(--progress-percent) * 3.6deg),
|
||||
#eee 360deg
|
||||
);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.progress-ring-inner {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.progress-bar-wrap {
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
.progress-bar-track {
|
||||
height: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #eee;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
transition: width 0.25s ease;
|
||||
}
|
||||
|
||||
.progress-label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.progress-value {
|
||||
font-weight: 600;
|
||||
color: var(--view-theme);
|
||||
}
|
||||
|
||||
.next-refund {
|
||||
margin-top: 12rpx;
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
221
pro_v3.5.1/view/uniapp/components/HjfRefundNotice.vue
Normal file
221
pro_v3.5.1/view/uniapp/components/HjfRefundNotice.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<view v-if="visible" class="hjf-refund-notice" @tap.self="handleMaskTap">
|
||||
<view class="hjf-refund-notice__mask" />
|
||||
<view class="hjf-refund-notice__box">
|
||||
<view class="hjf-refund-notice__icon-wrap">
|
||||
<text class="hjf-refund-notice__icon">✓</text>
|
||||
</view>
|
||||
<view class="hjf-refund-notice__title">恭喜!您的公排订单已退款</view>
|
||||
<view class="hjf-refund-notice__desc">已入账到现金余额</view>
|
||||
<view class="hjf-refund-notice__amount">
|
||||
<text class="hjf-refund-notice__amount-label">退款金额</text>
|
||||
<text class="hjf-refund-notice__amount-value">{{ formattedAmount }}</text>
|
||||
</view>
|
||||
<view v-if="orderId" class="hjf-refund-notice__order">
|
||||
<text class="hjf-refund-notice__order-label">订单号</text>
|
||||
<text class="hjf-refund-notice__order-value">{{ orderId }}</text>
|
||||
</view>
|
||||
<view class="hjf-refund-notice__btn" @tap="handleConfirm">确认</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file HjfRefundNotice.vue
|
||||
* @description 公排退款成功后的弹窗通知,展示退款金额、已入账到现金余额、订单号,确认按钮关闭弹窗。
|
||||
* @see docs/frontend-new-pages-spec.md 第 2.2.3 节
|
||||
*/
|
||||
export default {
|
||||
name: 'HjfRefundNotice',
|
||||
|
||||
props: {
|
||||
/**
|
||||
* 是否显示弹窗
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 退款金额(元)
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
amount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 订单号
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
orderId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* 格式化后的退款金额,格式:¥3,600.00(千分位 + 两位小数)
|
||||
* @returns {string}
|
||||
*/
|
||||
formattedAmount() {
|
||||
const num = Number(this.amount);
|
||||
if (isNaN(num)) return '¥0.00';
|
||||
const fixed = num.toFixed(2);
|
||||
const [intPart, decPart] = fixed.split('.');
|
||||
const formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
return `¥${formatted}.${decPart}`;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 点击确认按钮,关闭弹窗并触发 close 事件
|
||||
*/
|
||||
handleConfirm() {
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击遮罩层,关闭弹窗并触发 close 事件
|
||||
*/
|
||||
handleMaskTap() {
|
||||
this.$emit('close');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hjf-refund-notice {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.hjf-refund-notice__box {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 560rpx;
|
||||
background: #fff;
|
||||
border-radius: 24rpx;
|
||||
padding: 48rpx 40rpx 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__icon-wrap {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
margin: 0 auto 24rpx;
|
||||
background: var(--view-gradient, linear-gradient(135deg, #52c41a 0%, #73d13d 100%));
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__icon {
|
||||
font-size: 48rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__amount {
|
||||
background: #f5f5f5;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__amount-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__amount-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: 600;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__order {
|
||||
background: #fafafa;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
margin-bottom: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__order-label {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__order-value {
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
max-width: 360rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__btn {
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background: var(--view-gradient, linear-gradient(135deg, #1890ff 0%, #40a9ff 100%));
|
||||
border-radius: 44rpx;
|
||||
}
|
||||
|
||||
.hjf-refund-notice__btn:active {
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<view class="wf-item-page wf-page0">
|
||||
<view class='pictrue'>
|
||||
<view class='pictrue' style="position: relative;">
|
||||
<easy-loadimage
|
||||
mode="widthFix"
|
||||
:image-src="item.image"
|
||||
:borderSrc="item.activity_frame.image"
|
||||
width="100%"
|
||||
borderRadius="16rpx 16rpx 0 0"></easy-loadimage>
|
||||
<view class="queue-badge" v-if="item.is_queue_goods == 1">参与公排</view>
|
||||
</view>
|
||||
<view class="info_box">
|
||||
<view class="w-full line2 fs-28 text--w111-333 lh-40rpx">
|
||||
@@ -126,4 +127,17 @@
|
||||
.text-mer{
|
||||
color: $primary-merchant;
|
||||
}
|
||||
.queue-badge {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
right: 12rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #52c41a, #389e0d);
|
||||
z-index: 10;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -108,9 +108,9 @@
|
||||
}
|
||||
},
|
||||
tabBarData(newVal){
|
||||
if(newVal){
|
||||
if (newVal && newVal.effectConfig && newVal.menuList && newVal.menuList.length) {
|
||||
let configData = newVal;
|
||||
if(this.isTabBar){
|
||||
if (this.isTabBar) {
|
||||
this.newData = configData;
|
||||
}
|
||||
this.showTabBar = configData.effectConfig.tabVal;
|
||||
@@ -120,6 +120,10 @@
|
||||
uni.showTabBar()
|
||||
}
|
||||
this.$emit('newDataStatus', configData.effectConfig.tabVal)
|
||||
} else {
|
||||
// 数据无效或为空时显示原生 tabBar
|
||||
this.showTabBar = false;
|
||||
uni.showTabBar();
|
||||
}
|
||||
}
|
||||
// 'newData.menuList'(newValue, oldValue) {
|
||||
@@ -155,7 +159,7 @@
|
||||
methods: {
|
||||
navigationInfo() {
|
||||
//判断渲染来源是一级菜单还是微页面
|
||||
if(this.isTabBar && this.tabBarData.effectConfig){
|
||||
if (this.isTabBar && this.tabBarData && this.tabBarData.effectConfig && this.tabBarData.menuList && this.tabBarData.menuList.length) {
|
||||
let configData = this.tabBarData;
|
||||
this.newData = configData;
|
||||
this.showTabBar = configData.effectConfig.tabVal;
|
||||
@@ -165,8 +169,11 @@
|
||||
uni.showTabBar()
|
||||
}
|
||||
this.$emit('newDataStatus', configData.effectConfig.tabVal)
|
||||
} else {
|
||||
// 无自定义底部菜单数据或接口未返回时,显示原生 tabBar
|
||||
this.showTabBar = false;
|
||||
uni.showTabBar();
|
||||
}
|
||||
|
||||
},
|
||||
goRouter(item) {
|
||||
var pages = getCurrentPages();
|
||||
|
||||
@@ -27,12 +27,15 @@ import BaseMoney from './components/BaseMoney.vue';
|
||||
import BaseTag from './components/BaseTag.vue';
|
||||
import easyLoadimage from './components/easy-loadimage/easy-loadimage.vue'
|
||||
import baseDrawer from '@/components/tui-drawer/tui-drawer.vue';
|
||||
import HjfDemoPanel from './components/HjfDemoPanel.vue';
|
||||
|
||||
Vue.component('home', home)
|
||||
Vue.component('skeleton', skeleton)
|
||||
Vue.component('BaseMoney', BaseMoney)
|
||||
Vue.component('BaseTag', BaseTag)
|
||||
Vue.component('baseDrawer', baseDrawer)
|
||||
Vue.component('easyLoadimage', easyLoadimage)
|
||||
Vue.component('HjfDemoPanel', HjfDemoPanel)
|
||||
// #ifdef H5
|
||||
|
||||
|
||||
|
||||
13065
pro_v3.5.1/view/uniapp/package-lock.json
generated
13065
pro_v3.5.1/view/uniapp/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -76,6 +76,63 @@
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/queue/status",
|
||||
"style": {
|
||||
"navigationBarTitleText": "公排状态",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/queue/history",
|
||||
"style": {
|
||||
"navigationBarTitleText": "公排历史",
|
||||
"app-plus": {
|
||||
"titleNView": {
|
||||
"type": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/queue/rules",
|
||||
"style": {
|
||||
"navigationBarTitleText": "公排规则",
|
||||
"app-plus": {
|
||||
"titleNView": {
|
||||
"type": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/assets/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的资产",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/assets/points_detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "积分明细",
|
||||
"app-plus": {
|
||||
"titleNView": {
|
||||
"type": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/guide/hjf_intro",
|
||||
"style": {
|
||||
"navigationBarTitleText": "",
|
||||
"navigationStyle": "custom",
|
||||
"app-plus": {
|
||||
"titleNView": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
// "plugins": {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Conversation - 2026-03-10 18:17:19
|
||||
|
||||
## Prompt
|
||||
|
||||
创建页面 points_detail.vue(P17 积分明细页):
|
||||
|
||||
文件路径: /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)` 主题色。
|
||||
BIN
pro_v3.5.1/view/uniapp/pages/assets/.cursor-cli/sessions.db
Normal file
BIN
pro_v3.5.1/view/uniapp/pages/assets/.cursor-cli/sessions.db
Normal file
Binary file not shown.
565
pro_v3.5.1/view/uniapp/pages/assets/index.vue
Normal file
565
pro_v3.5.1/view/uniapp/pages/assets/index.vue
Normal file
@@ -0,0 +1,565 @@
|
||||
<template>
|
||||
<view class="hjf-assets-page" :style="colorStyle">
|
||||
|
||||
<view class="assets-wrapper">
|
||||
<view class="assets-header">
|
||||
<view class="assets-header__top">
|
||||
<view class="assets-header__title">我的资产</view>
|
||||
</view>
|
||||
|
||||
<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.now_money).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">¥{{ assetsInfo.total_queue_refund }}</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) {
|
||||
this.assetsInfo = res.data;
|
||||
}
|
||||
})
|
||||
.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>
|
||||
376
pro_v3.5.1/view/uniapp/pages/assets/points_detail.vue
Normal file
376
pro_v3.5.1/view/uniapp/pages/assets/points_detail.vue
Normal 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>
|
||||
@@ -207,8 +207,45 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cell flex justify-between flex-y-center mt-32" v-if="textareaStatus && send_gift_type != 2">
|
||||
<text class="text--w111-333 fs-28">留言</text>
|
||||
<!-- 报单商品公排提示:is_queue_goods=1 时显示 -->
|
||||
<view class="hjf-queue-notice mt-24" v-if="isQueueOrder">
|
||||
<view class="hjf-queue-notice__header flex-y-center">
|
||||
<text class="hjf-queue-notice__icon iconfont icon-tishi"></text>
|
||||
<text class="hjf-queue-notice__title fs-26 fw-500">公排商品提示</text>
|
||||
</view>
|
||||
<view class="hjf-queue-notice__body mt-16">
|
||||
<!-- 拆单提示:购买数量 > 1 时显示 -->
|
||||
<view class="hjf-queue-notice__item flex-y-center" v-if="queueSplitCount > 1">
|
||||
<text class="hjf-queue-notice__dot"></text>
|
||||
<text class="fs-24 text--w111-666 lh-34rpx">
|
||||
本次将拆分为
|
||||
<text class="hjf-queue-notice__num">{{ queueSplitCount }}</text>
|
||||
个独立公排订单,每单独立排队
|
||||
</text>
|
||||
</view>
|
||||
<!-- 公排规则说明 -->
|
||||
<view class="hjf-queue-notice__item flex-y-center mt-12">
|
||||
<text class="hjf-queue-notice__dot"></text>
|
||||
<text class="fs-24 text--w111-666 lh-34rpx">购买后自动加入公排池,进四退一全额返还</text>
|
||||
</view>
|
||||
<!-- 预计退款批次 -->
|
||||
<view class="hjf-queue-notice__item flex-y-center mt-12">
|
||||
<text class="hjf-queue-notice__dot"></text>
|
||||
<text class="fs-24 text--w111-666 lh-34rpx">
|
||||
预计退款时间:购买后第
|
||||
<text class="hjf-queue-notice__num">{{ estimatedRefundBatch }}</text>
|
||||
个批次(仅供参考,以实际排队进度为准)
|
||||
</text>
|
||||
</view>
|
||||
<!-- 跳转公排状态页 -->
|
||||
<view class="hjf-queue-notice__link flex-y-center justify-end mt-16" @tap="goPage(1, '/pages/queue/status')">
|
||||
<text class="fs-24 hjf-queue-notice__link-text">查看公排进度</text>
|
||||
<text class="iconfont icon-ic_rightarrow fs-20 hjf-queue-notice__link-text pl-4"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="cell flex justify-between flex-y-center mt-32" v-if="textareaStatus && send_gift_type != 2">
|
||||
<text class="text--w111-333 fs-28">留言</text>
|
||||
<textarea
|
||||
class="w-450 fs-28 text-right h-auto"
|
||||
:auto-height="true"
|
||||
@@ -613,6 +650,7 @@
|
||||
const CACHE_CITY = {};
|
||||
import dayjs from '@/plugin/dayjs/dayjs.min.js';
|
||||
import { orderConfirm, getCouponsOrderPrice, orderCreate, postOrderComputed, checkShipping, getCartCounts, orderReceiveGift } from '@/api/order.js';
|
||||
import { getQueueStatus } from '@/api/hjfQueue.js';
|
||||
import { getAddressDefault, getAddressDetail, invoiceList, invoiceOrder, getAddressList } from '@/api/user.js';
|
||||
import { openPaySubscribe } from '@/utils/SubscribeMessage.js';
|
||||
import { storeListApi, postCartAdd, getSendGiftOrderDetail } from '@/api/store.js';
|
||||
@@ -761,11 +799,49 @@ export default {
|
||||
giftData: null,
|
||||
channel: '', // 1 是采购商 "" 正常用户
|
||||
yue_pay_status: 1,
|
||||
now_money: ''
|
||||
now_money: '',
|
||||
/**
|
||||
* 公排全局进度快照,由 getQueueStatus() 拉取后写入。
|
||||
* 结构:{ current_batch: number, trigger_multiple: number, next_refund_queue_no: number }
|
||||
* Phase 4 集成时此字段由真实 API 填充。
|
||||
* @type {object|null}
|
||||
*/
|
||||
queueStatus: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['isLogin']),
|
||||
/**
|
||||
* 当前订单是否包含报单商品(is_queue_goods=1)。
|
||||
* 遍历 cartInfo(已过滤赠品),只要有一件报单商品即为报单订单。
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isQueueOrder() {
|
||||
return this.cartInfo.some((item) => item.productInfo && item.productInfo.is_queue_goods == 1);
|
||||
},
|
||||
/**
|
||||
* 报单商品的总购买数量(用于计算公排拆单数)。
|
||||
* 每件报单商品按购买数量各自独立入队,数量即拆单数。
|
||||
* @returns {number}
|
||||
*/
|
||||
queueSplitCount() {
|
||||
return this.cartInfo.reduce((sum, item) => {
|
||||
if (item.productInfo && item.productInfo.is_queue_goods == 1) {
|
||||
return sum + (parseInt(item.cart_num) || 1);
|
||||
}
|
||||
return sum;
|
||||
}, 0);
|
||||
},
|
||||
/**
|
||||
* 预计退款批次号:基于当前公排全局进度估算。
|
||||
* 采用保守估算:当前批次余量 + 每批次4单 * 向上取整后的等待批次。
|
||||
* 实际值由后端 /hjf/queue/status 接口提供,此处仅作展示用途。
|
||||
* @returns {number}
|
||||
*/
|
||||
estimatedRefundBatch() {
|
||||
const base = this.queueStatus ? this.queueStatus.current_batch : 0;
|
||||
return base + Math.ceil(this.queueSplitCount / 4) + 1;
|
||||
},
|
||||
giftCount() {
|
||||
let count = 0;
|
||||
if (this.giveCartInfo.length) {
|
||||
@@ -838,6 +914,7 @@ export default {
|
||||
let _this = this;
|
||||
if (this.isLogin && (this.send_gift_type == 0 || this.send_gift_type == 1)) {
|
||||
this.getCheckShipping();
|
||||
this.loadQueueStatus();
|
||||
} else if (this.send_gift_type == 2 && this.isLogin) {
|
||||
this.getOrderDetail();
|
||||
} else {
|
||||
@@ -1795,6 +1872,24 @@ export default {
|
||||
tapStoreList() {
|
||||
this.textareaStatus = false;
|
||||
this.address.address = true;
|
||||
},
|
||||
/**
|
||||
* 拉取公排全局进度快照,写入 queueStatus 用于计算预计退款批次。
|
||||
* 仅在订单包含报单商品时展示相关提示,但提前加载可减少感知延迟。
|
||||
* 失败时静默忽略,不影响正常结算流程。
|
||||
*/
|
||||
loadQueueStatus() {
|
||||
getQueueStatus()
|
||||
.then((res) => {
|
||||
if (res && res.data && res.data.progress) {
|
||||
this.queueStatus = {
|
||||
current_batch: res.data.progress.current_batch_count || 0,
|
||||
trigger_multiple: res.data.progress.trigger_multiple || 4,
|
||||
next_refund_queue_no: res.data.progress.next_refund_queue_no || 0
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1999,6 +2094,53 @@ export default {
|
||||
.con-border {
|
||||
border: 1px solid var(--view-theme);
|
||||
}
|
||||
|
||||
/* 报单商品公排提示区块 */
|
||||
.hjf-queue-notice {
|
||||
background: #f0f9f0;
|
||||
border: 1rpx solid #b7e4b7;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx;
|
||||
|
||||
&__header {
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
color: #3c9c3c;
|
||||
font-size: 30rpx;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: #2d7d2d;
|
||||
}
|
||||
|
||||
&__item {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
&__dot {
|
||||
display: inline-block;
|
||||
width: 10rpx;
|
||||
height: 10rpx;
|
||||
border-radius: 50%;
|
||||
background: #3c9c3c;
|
||||
flex-shrink: 0;
|
||||
margin-top: 12rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
&__num {
|
||||
color: #3c9c3c;
|
||||
font-weight: 600;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
&__link-text {
|
||||
color: var(--view-theme);
|
||||
}
|
||||
}
|
||||
.clear-btn {
|
||||
border-radius: 0 12rpx 0 12rpx;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,27 @@
|
||||
<text class="fs-40 fw-500 text--w111-fff pl-16" v-if="order_pay_info.pay_type == 'offline' && !order_pay_info.paid">订单创建成功</text>
|
||||
<text class="fs-40 fw-500 text--w111-fff pl-16" v-else>{{ order_pay_info.paid ? '订单支付成功' : '等待支付...' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 公排入队成功提示 -->
|
||||
<view v-if="isQueueOrder && order_pay_info.paid && queueEntries.length > 0" class="queue-success-notice mt-20">
|
||||
<view class="flex-y-center">
|
||||
<text class="iconfont icon-chenggong fs-28 queue-icon"></text>
|
||||
<text class="fs-26 text--w111-fff">已加入公排队列</text>
|
||||
</view>
|
||||
<view class="fs-22 text--w111-fff mt-8 opacity-80" v-if="queueEntries.length === 1">
|
||||
排队序号 #{{ queueEntries[0].queue_no }},{{ queueEntries[0].estimated_wait }}
|
||||
</view>
|
||||
<view class="fs-22 text--w111-fff mt-8 opacity-80" v-else>
|
||||
共 {{ queueEntries.length }} 个订单已入队
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex-center mt-30">
|
||||
<view class="w-192 h-64 rd-40rpx flex-center fs-24 text--w111-fff white-border" @tap="goIndex">返回首页</view>
|
||||
<view v-if="!order_pay_info.is_send_gift || !order_pay_info.paid" class="w-192 h-64 rd-40rpx flex-center fs-24 text--w111-fff white-border ml-48" @tap="goOrderDetails">
|
||||
<view v-if="isQueueOrder && order_pay_info.paid" class="w-192 h-64 rd-40rpx flex-center fs-24 text--w111-fff white-border ml-48" @tap="goQueueStatus">
|
||||
查看公排
|
||||
</view>
|
||||
<view v-else-if="!order_pay_info.is_send_gift || !order_pay_info.paid" class="w-192 h-64 rd-40rpx flex-center fs-24 text--w111-fff white-border ml-48" @tap="goOrderDetails">
|
||||
查看订单
|
||||
</view>
|
||||
<view v-else-if="order_pay_info.paid" @click="giftModalShow = true" class="w-192 h-64 rd-40rpx flex-center fs-24 text--w111-fff white-border ml-48">送给好友</view>
|
||||
@@ -108,6 +126,7 @@ import colors from '@/mixins/color';
|
||||
import { HTTP_REQUEST_URL } from '@/config/app';
|
||||
import Cache from '@/utils/cache';
|
||||
import { userShare } from '@/api/user.js';
|
||||
import { getQueueStatus } from '@/api/hjfQueue.js';
|
||||
export default {
|
||||
components: {
|
||||
gridsLottery,
|
||||
@@ -168,9 +187,10 @@ export default {
|
||||
nickname: '',
|
||||
code: ''
|
||||
},
|
||||
userInfo: Cache.get('USER_INFO') || {},
|
||||
mpGiftImg: HTTP_REQUEST_URL + '/statics/images/gift_share.jpg',
|
||||
recommendTitle: ""
|
||||
userInfo: Cache.get('USER_INFO') || {},
|
||||
mpGiftImg: HTTP_REQUEST_URL + '/statics/images/gift_share.jpg',
|
||||
recommendTitle: "",
|
||||
queueEntries: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -181,6 +201,10 @@ export default {
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
isQueueOrder() {
|
||||
if (!this.order_pay_info || !this.order_pay_info.cartInfo) return false;
|
||||
return this.order_pay_info.cartInfo.some(item => item.productInfo && item.productInfo.is_queue_goods == 1);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -327,6 +351,12 @@ export default {
|
||||
uni.hideShareMenu()
|
||||
}
|
||||
this.loading = true;
|
||||
|
||||
// 如果是公排订单且已支付,加载公排入队信息
|
||||
if (that.isQueueOrder && res.data.paid) {
|
||||
that.loadQueueEntries();
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
that.getOrderPrize();
|
||||
}, 1000);
|
||||
@@ -473,6 +503,20 @@ export default {
|
||||
that.recommendTitle = res.data.title ? res.data.title : '猜你喜欢';
|
||||
});
|
||||
},
|
||||
loadQueueEntries() {
|
||||
getQueueStatus()
|
||||
.then((res) => {
|
||||
if (res && res.data && res.data.myOrders && res.data.myOrders.length) {
|
||||
this.queueEntries = res.data.myOrders.filter(item => item.status === 0);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
goQueueStatus() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/queue/status'
|
||||
});
|
||||
},
|
||||
goPage(type, url) {
|
||||
if (type == 1) {
|
||||
uni.navigateTo({
|
||||
@@ -502,6 +546,20 @@ export default {
|
||||
.h-484 {
|
||||
height: 484rpx;
|
||||
}
|
||||
.queue-success-notice {
|
||||
text-align: center;
|
||||
padding: 20rpx 40rpx;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 20rpx;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.queue-icon {
|
||||
color: #52c41a;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.opacity-80 {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.content {
|
||||
background: #f5f5f5;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
|
||||
@@ -156,11 +156,12 @@
|
||||
<text class="iconfont icon-ic_rightarrow fs-24"></text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 商品名称 -->
|
||||
<view class="mt-20 fs-30 lh-42rpx text--w111-333 fw-500">
|
||||
<text v-if="storeInfo.brand_name" class="brand-tag">{{ storeInfo.brand_name }}</text>
|
||||
{{ storeInfo.store_name }}
|
||||
</view>
|
||||
<!-- 商品名称 -->
|
||||
<view class="mt-20 fs-30 lh-42rpx text--w111-333 fw-500">
|
||||
<text v-if="storeInfo.brand_name" class="brand-tag">{{ storeInfo.brand_name }}</text>
|
||||
<text v-if="isQueueGoods" class="queue-goods-tag">报单商品</text>
|
||||
{{ storeInfo.store_name }}
|
||||
</view>
|
||||
<!-- 库存销量 -->
|
||||
<view class="flex-between-center mt-24 text--w111-999 fs-22 lh-30rpx">
|
||||
<text v-show="diyProduct.isOpen.includes(0)">
|
||||
@@ -419,7 +420,12 @@
|
||||
</view>
|
||||
<!-- 底部操作按钮 -->
|
||||
<view class="page_footer bg--w111-fff-s111-80 w-full z-99 fixed-lb pb-safe">
|
||||
<view class="w-full h-104 pl-32 pr-20 flex">
|
||||
<!-- 报单商品公排提示:is_queue_goods=1 时在购买按钮上方显示 -->
|
||||
<view v-if="isQueueGoods" class="queue-goods-notice flex-y-center px-32 py-16">
|
||||
<text class="iconfont icon-ic_user fs-28 queue-goods-notice__icon"></text>
|
||||
<text class="fs-24 queue-goods-notice__text pl-8">报单商品 · 购买后自动参与公排,进四退一全额返还</text>
|
||||
</view>
|
||||
<view class="w-full h-104 pl-32 pr-20 flex">
|
||||
<view class="flex">
|
||||
<view class="flex-col flex-center mr-38" @tap="goPage(2, '/pages/index/index')" v-if="diyProduct.menuList.includes(0)">
|
||||
<text class="iconfont icon-ic_mall fs-40"></text>
|
||||
@@ -856,7 +862,8 @@ export default {
|
||||
brokerage: '',
|
||||
pageHide: false,
|
||||
pageInvalid: 1,
|
||||
limitInvalid: 20
|
||||
limitInvalid: 20,
|
||||
paymentType: 'weixin'
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
@@ -915,6 +922,9 @@ export default {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
isQueueGoods() {
|
||||
return this.storeInfo && this.storeInfo.is_queue_goods == 1;
|
||||
},
|
||||
// #ifdef MP
|
||||
shareButtonStyle() {
|
||||
let res = wx.getMenuButtonBoundingClientRect();
|
||||
@@ -2404,4 +2414,57 @@ export default {
|
||||
.bounce-in {
|
||||
animation: myBounceIn 0.75s ease-out forwards;
|
||||
}
|
||||
|
||||
/**
|
||||
* 品牌标签 — 商品名称左侧品牌角标
|
||||
*/
|
||||
.brand-tag {
|
||||
display: inline-block;
|
||||
padding: 0 10rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
font-size: 20rpx;
|
||||
color: var(--view-theme);
|
||||
border: 1rpx solid var(--view-theme);
|
||||
border-radius: 6rpx;
|
||||
margin-right: 8rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 报单商品标签 — 商品名称区域的「报单商品」角标
|
||||
* 仅当 is_queue_goods=1 时通过 v-if="isQueueGoods" 渲染
|
||||
*/
|
||||
.queue-goods-tag {
|
||||
display: inline-block;
|
||||
padding: 0 12rpx;
|
||||
height: 36rpx;
|
||||
line-height: 36rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
color: #1a7e3c;
|
||||
background-color: #e8f7ed;
|
||||
border: 1rpx solid #52c41a;
|
||||
border-radius: 6rpx;
|
||||
margin-right: 8rpx;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/**
|
||||
* 购买区域公排提示条 — 报单商品时显示在底部按钮上方
|
||||
* 仅当 is_queue_goods=1 时通过 v-if="isQueueGoods" 渲染
|
||||
*/
|
||||
.queue-goods-notice {
|
||||
background-color: #f0faf3;
|
||||
border-top: 1rpx solid #b7eb8f;
|
||||
|
||||
&__icon {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
&__text {
|
||||
color: #389e0d;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
310
pro_v3.5.1/view/uniapp/pages/guide/hjf_intro.vue
Normal file
310
pro_v3.5.1/view/uniapp/pages/guide/hjf_intro.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<view class="guide-page">
|
||||
<!-- 跳过按钮 -->
|
||||
<view v-if="currentIndex < slides.length - 1" class="skip-btn" @tap="handleSkip">
|
||||
<text class="skip-text">跳过</text>
|
||||
</view>
|
||||
|
||||
<!-- 轮播主体 -->
|
||||
<swiper
|
||||
class="guide-swiper"
|
||||
:current="currentIndex"
|
||||
:indicator-dots="false"
|
||||
@change="onSwiperChange"
|
||||
>
|
||||
<swiper-item v-for="(slide, index) in slides" :key="index" class="swiper-item">
|
||||
<view class="slide-content">
|
||||
<image
|
||||
class="slide-image"
|
||||
:src="slide.image"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<view class="slide-text-wrap">
|
||||
<text class="slide-title">{{ slide.title }}</text>
|
||||
<text class="slide-desc">{{ slide.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 底部区域:指示器 + 按钮 -->
|
||||
<view class="bottom-area">
|
||||
<!-- 圆点指示器 -->
|
||||
<view class="dot-indicators">
|
||||
<view
|
||||
v-for="(slide, index) in slides"
|
||||
:key="index"
|
||||
class="dot"
|
||||
:class="{ active: index === currentIndex }"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-wrap">
|
||||
<button
|
||||
v-if="currentIndex < slides.length - 1"
|
||||
class="btn-next"
|
||||
@tap="handleNext"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn-start"
|
||||
@tap="handleStart"
|
||||
>
|
||||
立即体验
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* @file hjf_intro.vue
|
||||
* @description P23 新用户引导页(轮播引导)
|
||||
*
|
||||
* 功能:
|
||||
* - 3 屏轮播引导(平台介绍 / 公排规则 / 会员积分体系)
|
||||
* - 底部圆点指示器实时同步当前页
|
||||
* - 右上角"跳过"按钮(最后一屏隐藏)
|
||||
* - 最后一屏显示"立即体验"按钮,点击跳转首页
|
||||
* - 跳过或完成后写入本地存储,不再重复展示
|
||||
*
|
||||
* Mock 数据:MOCK_GUIDE_DATA(来自 @/utils/hjfMockData.js)
|
||||
* 路由注册:pages/guide/hjf_intro(navigationStyle: custom)
|
||||
*
|
||||
* @module pages/guide/hjf_intro
|
||||
* @author OpenClaw Agent
|
||||
* @version 1.0.0
|
||||
* @see docs/frontend-new-pages-spec.md 第 4.2.1 节
|
||||
*/
|
||||
|
||||
import { MOCK_GUIDE_DATA } from '@/utils/hjfMockData.js';
|
||||
|
||||
/** 本地存储 key,用于记录引导已读状态 */
|
||||
const GUIDE_READ_KEY = 'hjf_guide_read';
|
||||
|
||||
export default {
|
||||
name: 'HjfIntro',
|
||||
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* 引导轮播幻灯片列表
|
||||
* @type {Array<{title: string, desc: string, image: string}>}
|
||||
*/
|
||||
slides: [],
|
||||
|
||||
/**
|
||||
* 当前激活的幻灯片索引(0-based)
|
||||
* @type {number}
|
||||
*/
|
||||
currentIndex: 0
|
||||
};
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.initSlides();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 初始化轮播数据,从 Mock 数据加载幻灯片列表
|
||||
* Phase 4 集成后可替换为真实 API 调用
|
||||
*/
|
||||
initSlides() {
|
||||
this.slides = MOCK_GUIDE_DATA.slides || [];
|
||||
},
|
||||
|
||||
/**
|
||||
* swiper change 事件回调,同步当前索引到圆点指示器
|
||||
* @param {UniApp.SwiperChangeEvent} e - swiper change 事件对象
|
||||
*/
|
||||
onSwiperChange(e) {
|
||||
this.currentIndex = e.detail.current;
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击"下一步"按钮,切换到下一张幻灯片
|
||||
*/
|
||||
handleNext() {
|
||||
if (this.currentIndex < this.slides.length - 1) {
|
||||
this.currentIndex += 1;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击"跳过"按钮,记录已读状态并直接跳转首页
|
||||
*/
|
||||
handleSkip() {
|
||||
this.markGuideRead();
|
||||
this.goHome();
|
||||
},
|
||||
|
||||
/**
|
||||
* 点击"立即体验"按钮(最后一屏),记录已读状态并跳转首页
|
||||
*/
|
||||
handleStart() {
|
||||
this.markGuideRead();
|
||||
this.goHome();
|
||||
},
|
||||
|
||||
/**
|
||||
* 将引导已读状态写入本地存储
|
||||
* 后续在 App.vue 或首页 onShow 中读取此标记,决定是否跳转引导页
|
||||
*/
|
||||
markGuideRead() {
|
||||
uni.setStorageSync(GUIDE_READ_KEY, '1');
|
||||
},
|
||||
|
||||
/**
|
||||
* 跳转至应用首页,使用 reLaunch 清空页面栈
|
||||
*/
|
||||
goHome() {
|
||||
uni.reLaunch({ url: '/pages/index/index' });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
$theme-gradient: linear-gradient(135deg, var(--view-theme, #e93323) 0%, #ff7043 100%);
|
||||
$slide-height: calc(100vh - 280rpx);
|
||||
|
||||
.guide-page {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 跳过按钮
|
||||
.skip-btn {
|
||||
position: fixed;
|
||||
top: calc(var(--status-bar-height) + 20rpx);
|
||||
right: 40rpx;
|
||||
z-index: 10;
|
||||
padding: 12rpx 30rpx;
|
||||
background: rgba(0, 0, 0, 0.12);
|
||||
border-radius: 40rpx;
|
||||
|
||||
.skip-text {
|
||||
font-size: 26rpx;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
// 轮播主体
|
||||
.guide-swiper {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: $slide-height;
|
||||
}
|
||||
|
||||
.swiper-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.slide-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 60rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.slide-image {
|
||||
width: 560rpx;
|
||||
height: 420rpx;
|
||||
margin-bottom: 80rpx;
|
||||
}
|
||||
|
||||
.slide-text-wrap {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.slide-title {
|
||||
display: block;
|
||||
font-size: 44rpx;
|
||||
font-weight: bold;
|
||||
color: #222;
|
||||
margin-bottom: 28rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.slide-desc {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
color: #888;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
// 底部区域
|
||||
.bottom-area {
|
||||
padding: 0 60rpx 80rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 圆点指示器
|
||||
.dot-indicators {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #ddd;
|
||||
margin: 0 10rpx;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.active {
|
||||
width: 40rpx;
|
||||
border-radius: 8rpx;
|
||||
background: var(--view-theme, #e93323);
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮公共
|
||||
.btn-next,
|
||||
.btn-start {
|
||||
width: 560rpx;
|
||||
height: 96rpx;
|
||||
border-radius: 48rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-next {
|
||||
background: #f5f5f5;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.btn-start {
|
||||
background: $theme-gradient;
|
||||
color: #fff;
|
||||
box-shadow: 0 8rpx 30rpx rgba(233, 51, 35, 0.35);
|
||||
}
|
||||
</style>
|
||||
@@ -122,6 +122,8 @@
|
||||
<view class="btn" @click="reconnect">重新连接</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- HJF 演示控制面板 -->
|
||||
<HjfDemoPanel />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -590,7 +592,9 @@
|
||||
getDiyVersion(0).then(res => {
|
||||
let diyVersion = uni.getStorageSync('diyVersion');
|
||||
if ((res.data.version + '0') === diyVersion) {
|
||||
this.setDiyData(JSON.parse(diyData));
|
||||
// Storage may return string or already-parsed object (e.g. on H5)
|
||||
const data = typeof diyData === 'string' ? JSON.parse(diyData) : diyData;
|
||||
this.setDiyData(data);
|
||||
} else {
|
||||
uni.setStorageSync('diyVersion', (res.data.version + '0'));
|
||||
this.getDiyData();
|
||||
|
||||
@@ -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>
|
||||
@@ -43,10 +43,6 @@ export default {
|
||||
<view class="name display-add" v-if="!userInfo.uid" @tap="openAuto">请点击授权</view>
|
||||
<view class="acea-row row-middle" v-if="userInfo.uid">
|
||||
<view class="name">{{ userInfo.nickname }}</view><strong></strong>
|
||||
<view class="vip flex-center" v-if="userInfo.level">
|
||||
<text class="iconfont icon-huiyuandengji"></text>
|
||||
V{{userInfo.level}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex-y-center mt-10">
|
||||
<view class="h-42 flex-center fs-22 text--w111-fff pl-16 pr-14 rd-30rpx b-f mr-12"
|
||||
@@ -60,7 +56,13 @@ export default {
|
||||
</view>
|
||||
<template v-if="!userInfo.is_channel && !userInfo.is_service">
|
||||
<view class="bind-phone" v-if="!userInfo.phone && userInfo.uid" @tap="bindPhone">绑定手机号</view>
|
||||
<view class="phone" v-else>{{ perShowType ? 'ID:' + userInfo.uid : userInfo.phone }}</view>
|
||||
<view class="acea-row row-middle" v-else>
|
||||
<view class="phone">{{ perShowType ? 'ID:' + userInfo.uid : userInfo.phone }}</view>
|
||||
<view class="vip flex-center" v-if="userInfo.level">
|
||||
<text class="iconfont icon-huiyuandengji"></text>
|
||||
{{ userInfo.vip_name || ('V' + userInfo.level) }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
</view>
|
||||
@@ -220,18 +222,17 @@ export default {
|
||||
}
|
||||
}
|
||||
.vip{
|
||||
// width: 64rpx;
|
||||
height: 26rpx;
|
||||
height: 40rpx;
|
||||
background: #FEF0D9;
|
||||
border: 1px solid #FACC7D;
|
||||
border-radius: 50rpx;
|
||||
font-size: 18rpx;
|
||||
font-weight: 500;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #DFA541;
|
||||
margin-left: 10rpx;
|
||||
padding: 0 6rpx;
|
||||
padding: 0 12rpx;
|
||||
.iconfont {
|
||||
font-size: 20rpx !important;
|
||||
font-size: 28rpx !important;
|
||||
margin-right: 4rpx;
|
||||
color: #DFA541 !important;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,29 @@
|
||||
<template v-if="isObjectData(diyData)">
|
||||
<user-member :userInfo="userInfo" :memberData="diyData.member" :orderAdminData="orderAdminData" :balanceStatus="balanceStatus" :isScrolling="isScrolling"></user-member>
|
||||
<user-order :orderMenu="orderMenu" :orderAdminData="orderAdminData" :userInfo="userInfo" :memberData="diyData.member" :orderData="diyData.order"></user-order>
|
||||
<!-- 黄精粉快捷入口:我的资产 & 公排记录(与 member-points 保持一致风格) -->
|
||||
<view class="acea-row member-points hjf-nav-row">
|
||||
<view class="acea-row row-middle row-center item" @tap="intoPage('/pages/assets/index')">
|
||||
<view>
|
||||
<view>我的资产</view>
|
||||
<view class="arrow">
|
||||
查看余额、积分
|
||||
<text class="iconfont icon-ic_rightarrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<image src="@/static/images/user-member.png" class="image"></image>
|
||||
</view>
|
||||
<view class="acea-row row-middle row-center item" @tap="intoPage('/pages/queue/status')">
|
||||
<view>
|
||||
<view>公排查询</view>
|
||||
<view class="arrow">
|
||||
查看排队进度
|
||||
<text class="iconfont icon-ic_rightarrow"></text>
|
||||
</view>
|
||||
</view>
|
||||
<image src="@/static/images/user-points.png" class="image"></image>
|
||||
</view>
|
||||
</view>
|
||||
<user-order-static
|
||||
v-if="isObjectData(orderAdminData) && orderAdminData.order.user_order"
|
||||
:orderAdminData="orderAdminData.order"
|
||||
@@ -13,7 +36,7 @@
|
||||
<user-menu :menuData="diyData.menu" :routineContact="routineContact"></user-menu>
|
||||
<user-menu v-if="storeMenuShow" :menuData="diyData.merMenu"></user-menu>
|
||||
<view class="copy_right pb-20">
|
||||
<template v-if="configData.copyrightContext">
|
||||
<template v-if="configData && configData.copyrightContext">
|
||||
<image :src="configData.copyrightImage" mode="aspectFill" class="copyRightImg"></image>
|
||||
<view class="of0b21">
|
||||
{{ configData.copyrightContext }}
|
||||
@@ -29,6 +52,8 @@
|
||||
<!-- #ifdef MP -->
|
||||
<editUserModal :isShow="editModal" @closeEdit="closeEdit" @editSuccess="editSuccess"></editUserModal>
|
||||
<!-- #endif -->
|
||||
<!-- HJF 演示控制面板 -->
|
||||
<HjfDemoPanel />
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
@@ -187,7 +212,7 @@ export default {
|
||||
color: ['#333', '#333'] //边框颜色支持渐变色
|
||||
},
|
||||
imgHost: HTTP_REQUEST_URL,
|
||||
configData: Cache.get('BASIC_CONFIG'),
|
||||
configData: Cache.get('BASIC_CONFIG') || {},
|
||||
copyrightImage: HTTP_REQUEST_URL + '/statics/images/product/support.png',
|
||||
giftPic: '',
|
||||
vip_type: 1,
|
||||
@@ -632,13 +657,15 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style scoped lang="scss">
|
||||
.footer-placeholder {
|
||||
height: calc(98rpx + constant(safe-area-inset-bottom));
|
||||
height: calc(98rpx + env(safe-area-inset-bottom));
|
||||
height: 98rpx;
|
||||
}
|
||||
|
||||
.user-page {
|
||||
position: relative;
|
||||
padding-bottom: calc(100rpx+ constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
|
||||
padding-bottom: 100rpx;
|
||||
@@ -667,4 +694,62 @@ export default {
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 黄精粉导航行:复用 member-points 样式(与会员中心/积分商城完全一致)
|
||||
.hjf-nav-row {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.member-points {
|
||||
border-radius: 20rpx;
|
||||
margin: 20rpx;
|
||||
background-color: #ffffff;
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 134rpx;
|
||||
padding-left: 40rpx;
|
||||
font-weight: 500;
|
||||
font-size: 28rpx;
|
||||
line-height: 34rpx;
|
||||
color: #333333;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
height: 48rpx;
|
||||
border-left: 1rpx solid #eeeeee;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
&:first-child::before {
|
||||
display: none;
|
||||
}
|
||||
.iconfont {
|
||||
position: relative;
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
margin-top: 12rpx;
|
||||
font-weight: 400;
|
||||
font-size: 22rpx;
|
||||
line-height: 24rpx;
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
margin-left: 40rpx;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
margin-left: 2rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,30 +1,68 @@
|
||||
<template>
|
||||
<view :style="colorStyle">
|
||||
<view class='bill-details'>
|
||||
<!-- 类型筛选导航 -->
|
||||
<view class='nav acea-row'>
|
||||
<view class='item' :class='type==0 ? "on":""' @click='changeType(0)'>全部</view>
|
||||
<view class='item' :class='type==1 ? "on":""' @click='changeType(1)'>消费</view>
|
||||
<view class='item' :class='type==2 ? "on":""' @click='changeType(2)'>储值</view>
|
||||
<view
|
||||
class='item'
|
||||
:class='type == 0 ? "on" : ""'
|
||||
@click='changeType(0)'
|
||||
>全部</view>
|
||||
<view
|
||||
class='item'
|
||||
:class='type == 1 ? "on" : ""'
|
||||
@click='changeType(1)'
|
||||
>消费</view>
|
||||
<view
|
||||
class='item'
|
||||
:class='type == 2 ? "on" : ""'
|
||||
@click='changeType(2)'
|
||||
>储值</view>
|
||||
<view
|
||||
class='item'
|
||||
:class='type == "queue_refund" ? "on" : ""'
|
||||
@click='changeType("queue_refund")'
|
||||
>公排退款</view>
|
||||
</view>
|
||||
|
||||
<!-- 账单列表 -->
|
||||
<view class='sign-record'>
|
||||
<view class='list' v-for="(item,index) in userBillList" :key="index">
|
||||
<view class='list' v-for="(item, index) in userBillList" :key="index">
|
||||
<view class='item'>
|
||||
<view class='data'>{{item.time}}</view>
|
||||
<view class='data'>{{ item.time }}</view>
|
||||
<view class='listn'>
|
||||
<view class='itemn acea-row row-between-wrapper' v-for="(vo,indexn) in item.child" :key="indexn">
|
||||
<view
|
||||
class='itemn acea-row row-between-wrapper'
|
||||
v-for="(vo, indexn) in item.child"
|
||||
:key="indexn"
|
||||
>
|
||||
<view>
|
||||
<view class='name line1'>{{vo.title}}</view>
|
||||
<view>{{vo.add_time}}</view>
|
||||
<view class='name line1'>
|
||||
{{ vo.title }}
|
||||
<!-- 公排退款标记 -->
|
||||
<text
|
||||
v-if="vo.type === 'queue_refund'"
|
||||
class='queue-refund-tag'
|
||||
>公排退款</text>
|
||||
</view>
|
||||
<view class='time-text'>{{ vo.add_time }}</view>
|
||||
</view>
|
||||
<view class='num' :class="vo.pm ? 'num-add' : 'num-sub'">
|
||||
<text v-if="vo.pm">+{{ vo.number }}</text>
|
||||
<text v-else>-{{ vo.number }}</text>
|
||||
</view>
|
||||
<view class='num' v-if="vo.pm">+{{vo.number}}</view>
|
||||
<view class='num' v-else>-{{vo.number}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class='loadingicon acea-row row-center-wrapper' v-if="userBillList.length>0">
|
||||
<text class='loading iconfont icon-jiazai' :hidden='loading==false'></text>{{loadTitle}}
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view class='loadingicon acea-row row-center-wrapper' v-if="userBillList.length > 0">
|
||||
<text class='loading iconfont icon-jiazai' :hidden='loading == false'></text>
|
||||
{{ loadTitle }}
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="px-20 mt-20" v-if="userBillList.length == 0">
|
||||
<emptyPage title="暂无记录~" src="/statics/images/noOrder.gif"></emptyPage>
|
||||
</view>
|
||||
@@ -35,18 +73,25 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getCommissionInfo,
|
||||
moneyList
|
||||
} from '@/api/user.js';
|
||||
import {
|
||||
toLogin
|
||||
} from '@/libs/login.js';
|
||||
import {
|
||||
mapGetters
|
||||
} from "vuex";
|
||||
import { moneyList } from '@/api/user.js';
|
||||
import { toLogin } from '@/libs/login.js';
|
||||
import { mapGetters } from 'vuex';
|
||||
import emptyPage from '@/components/emptyPage.vue';
|
||||
import colors from "@/mixins/color";
|
||||
import colors from '@/mixins/color';
|
||||
|
||||
/**
|
||||
* 账单明细页
|
||||
*
|
||||
* 展示用户的账单流水,支持按类型筛选:
|
||||
* - 0: 全部
|
||||
* - 1: 消费
|
||||
* - 2: 储值
|
||||
* - "queue_refund": 公排退款(type=queue_refund 时显示专属标记)
|
||||
*
|
||||
* 列表按日期分组,支持上拉分页加载。
|
||||
*
|
||||
* @module pages/users/user_bill/index
|
||||
*/
|
||||
export default {
|
||||
components: {
|
||||
emptyPage,
|
||||
@@ -54,125 +99,116 @@
|
||||
mixins: [colors],
|
||||
data() {
|
||||
return {
|
||||
/** @type {string} 底部加载提示文字 */
|
||||
loadTitle: '加载更多',
|
||||
/** @type {boolean} 是否正在加载中(防止重复请求) */
|
||||
loading: false,
|
||||
/** @type {boolean} 是否已加载全部数据 */
|
||||
loadend: false,
|
||||
/** @type {number} 当前页码 */
|
||||
page: 1,
|
||||
/** @type {number} 每页条数 */
|
||||
limit: 15,
|
||||
/**
|
||||
* 当前筛选类型
|
||||
* 0=全部 1=消费 2=储值 "queue_refund"=公排退款
|
||||
* @type {number|string}
|
||||
*/
|
||||
type: 0,
|
||||
/** @type {Array<Object>} 按日期分组的账单列表 */
|
||||
userBillList: [],
|
||||
times:[],
|
||||
isAuto: false, //没有授权的不会自动授权
|
||||
isShowAuth: false //是否隐藏授权
|
||||
/** @type {Array<string>} 已加载的日期键列表(用于去重分组) */
|
||||
times: [],
|
||||
};
|
||||
},
|
||||
computed: mapGetters(['isLogin']),
|
||||
computed: {
|
||||
...mapGetters(['isLogin']),
|
||||
},
|
||||
onShow() {
|
||||
uni.removeStorageSync('form_type_cart');
|
||||
if (this.isLogin) {
|
||||
this.getUserBillList();
|
||||
} else {
|
||||
toLogin()
|
||||
toLogin();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
* 生命周期函数 — 监听页面加载
|
||||
* @param {Object} options - 页面跳转参数
|
||||
* @param {number|string} [options.type=0] - 初始筛选类型
|
||||
*/
|
||||
onLoad: function(options) {
|
||||
onLoad(options) {
|
||||
this.type = options.type || 0;
|
||||
},
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
* 页面上拉触底 — 加载下一页
|
||||
*/
|
||||
onReachBottom: function() {
|
||||
onReachBottom() {
|
||||
this.getUserBillList();
|
||||
},
|
||||
onPageScroll(object) {
|
||||
/**
|
||||
* 页面滚动事件 — 广播 scroll 事件供子组件使用
|
||||
*/
|
||||
onPageScroll() {
|
||||
uni.$emit('scroll');
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 授权回调
|
||||
* 获取账单明细列表(分页追加)
|
||||
*
|
||||
* 请求 moneyList 接口,将返回数据按日期分组追加到 userBillList。
|
||||
* 若 loading 或 loadend 为 true 则直接返回,防止重复/越界请求。
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
onLoadFun: function() {
|
||||
this.getUserBillList();
|
||||
this.isShowAuth = false;
|
||||
},
|
||||
// 授权关闭
|
||||
authColse: function(e) {
|
||||
this.isShowAuth = e
|
||||
},
|
||||
/**
|
||||
* 获取账户明细
|
||||
*/
|
||||
getUserBillList: function() {
|
||||
let that = this;
|
||||
let page = that.page;
|
||||
let limit = that.limit;
|
||||
if (that.loading) return;
|
||||
if (that.loadend) return;
|
||||
that.loading = true;
|
||||
that.loadTitle = '';
|
||||
moneyList({
|
||||
page: page,
|
||||
limit: limit
|
||||
},that.type).then(res => {
|
||||
for (let i = 0; i < res.data.time.length; i++) {
|
||||
|
||||
if (!this.times.includes(res.data.time[i])) {
|
||||
this.times.push(res.data.time[i])
|
||||
this.userBillList.push({
|
||||
time: res.data.time[i],
|
||||
child: []
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < this.times.length; x++) {
|
||||
for (let j = 0; j < res.data.list.length; j++) {
|
||||
if (this.times[x] === res.data.list[j].time_key) {
|
||||
this.userBillList[x].child.push(res.data.list[j])
|
||||
getUserBillList() {
|
||||
if (this.loading) return;
|
||||
if (this.loadend) return;
|
||||
|
||||
this.loading = true;
|
||||
this.loadTitle = '';
|
||||
|
||||
moneyList({ page: this.page, limit: this.limit }, this.type)
|
||||
.then(res => {
|
||||
const { time: timeKeys, list } = res.data;
|
||||
|
||||
// 按日期键建立分组(跨页去重)
|
||||
timeKeys.forEach(key => {
|
||||
if (!this.times.includes(key)) {
|
||||
this.times.push(key);
|
||||
this.userBillList.push({ time: key, child: [] });
|
||||
}
|
||||
}
|
||||
}
|
||||
let loadend = res.data.list.length < that.limit;
|
||||
that.loadend = loadend;
|
||||
that.loadTitle = loadend ? '没有更多内容啦~' : '加载更多';
|
||||
that.page += 1;
|
||||
that.loading = false;
|
||||
}).catch(err=>{
|
||||
that.loading = false;
|
||||
that.loadTitle = '加载更多';
|
||||
})
|
||||
});
|
||||
|
||||
// 将明细条目归入对应日期分组
|
||||
this.times.forEach((key, idx) => {
|
||||
list.forEach(item => {
|
||||
if (item.time_key === key) {
|
||||
this.userBillList[idx].child.push(item);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const loadend = list.length < this.limit;
|
||||
this.loadend = loadend;
|
||||
this.loadTitle = loadend ? '没有更多内容啦~' : '加载更多';
|
||||
this.page += 1;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
this.loadTitle = '加载更多';
|
||||
});
|
||||
},
|
||||
// getUserBillList: function() {
|
||||
// let that = this;
|
||||
// if (that.loadend) return;
|
||||
// if (that.loading) return;
|
||||
// that.loading = true;
|
||||
// that.loadTitle = "";
|
||||
// let data = {
|
||||
// page: that.page,
|
||||
// limit: that.limit
|
||||
// }
|
||||
// getCommissionInfo(data, that.type).then(function(res) {
|
||||
// let list = res.data,
|
||||
// loadend = list.length < that.limit;
|
||||
// that.userBillList = that.$util.SplitArray(list, that.userBillList);
|
||||
// that.$set(that, 'userBillList', that.userBillList);
|
||||
// that.loadend = loadend;
|
||||
// that.loading = false;
|
||||
// that.loadTitle = loadend ? "没有更多内容啦~" : "加载更多";
|
||||
// that.page = that.page + 1;
|
||||
// }, function(res) {
|
||||
// that.loading = false;
|
||||
// that.loadTitle = '加载更多';
|
||||
// });
|
||||
// },
|
||||
|
||||
/**
|
||||
* 切换导航
|
||||
* 切换账单筛选类型并重置列表
|
||||
*
|
||||
* @param {number|string} type - 目标类型(0全部 1消费 2储值 "queue_refund"公排退款)
|
||||
* @returns {void}
|
||||
*/
|
||||
changeType: function(type) {
|
||||
changeType(type) {
|
||||
if (this.type === type) return;
|
||||
this.type = type;
|
||||
this.loadend = false;
|
||||
this.page = 1;
|
||||
@@ -180,35 +216,71 @@
|
||||
this.$set(this, 'userBillList', []);
|
||||
this.getUserBillList();
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang='scss'>
|
||||
.sign-record .list .item .data{
|
||||
.sign-record .list .item .data {
|
||||
color: #999;
|
||||
padding: 20rpx 30rpx 10rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
.sign-record .list .item .listn{
|
||||
|
||||
.sign-record .list .item .listn {
|
||||
width: 710rpx;
|
||||
margin: 0 auto;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
.sign-record .list .item .listn .itemn{
|
||||
|
||||
.sign-record .list .item .listn .itemn {
|
||||
padding: 0;
|
||||
margin: 0 30rpx;
|
||||
height: 150rpx;
|
||||
|
||||
}
|
||||
.sign-record .list .item .listn .itemn:nth-last-child(1){
|
||||
|
||||
.sign-record .list .item .listn .itemn:nth-last-child(1) {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.sign-record .list .item .listn .itemn .name{
|
||||
|
||||
.sign-record .list .item .listn .itemn .name {
|
||||
color: #333;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.sign-record .list .item .listn .itemn .num{
|
||||
color: #333333;
|
||||
|
||||
.sign-record .list .item .listn .itemn .time-text {
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.sign-record .list .item .listn .itemn .num {
|
||||
font-family: 'Regular';
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.num-add {
|
||||
color: var(--view-theme);
|
||||
}
|
||||
|
||||
.num-sub {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
/* 公排退款标记 */
|
||||
.queue-refund-tag {
|
||||
display: inline-block;
|
||||
margin-left: 10rpx;
|
||||
padding: 2rpx 12rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 20rpx;
|
||||
color: #fff;
|
||||
background: var(--view-theme);
|
||||
vertical-align: middle;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* 顶部类型筛选导航 */
|
||||
.bill-details .nav {
|
||||
background-color: #fff;
|
||||
height: 80rpx;
|
||||
@@ -225,11 +297,11 @@
|
||||
|
||||
.bill-details .nav .item.on {
|
||||
color: var(--view-theme);
|
||||
/* border-bottom: 3rpx solid var(--view-theme); */
|
||||
font-size: 30rpx;
|
||||
position: relative;
|
||||
}
|
||||
.bill-details .nav .item.on::after{
|
||||
|
||||
.bill-details .nav .item.on::after {
|
||||
position: absolute;
|
||||
width: 64rpx;
|
||||
height: 6rpx;
|
||||
@@ -237,7 +309,7 @@
|
||||
content: ' ';
|
||||
background: var(--view-theme);
|
||||
bottom: 0;
|
||||
left:50%;
|
||||
left: 50%;
|
||||
margin-left: -32rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -45,78 +45,89 @@
|
||||
</view>
|
||||
</view>
|
||||
<view :hidden="currentTab != 0">
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>持卡人</view>
|
||||
<view class='input'><input placeholder='请输入持卡人姓名' placeholder-class='placeholder'
|
||||
name="name" onKeypress="javascript:if(event.keyCode == 32)event.returnValue = false;"></input></view>
|
||||
</view>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>卡号</view>
|
||||
<view class='input'><input type='number' placeholder='请输入卡号' placeholder-class='placeholder'
|
||||
name="cardnum"></input></view>
|
||||
</view>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>银行</view>
|
||||
<view class='input'>
|
||||
<picker @change="bindPickerChange" :value="index" :range="array">
|
||||
<view class="acea-row row-between-wrapper">
|
||||
<text class='Bank'>{{array[index]}}</text>
|
||||
<text class='iconfont icon-ic_rightarrow'></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>提现</view>
|
||||
<view class='input acea-row row-between-wrapper'>
|
||||
<input @input='inputNum' :value='cashVal' :maxlength="moneyMaxLeng" :placeholder='"最低提现金额:¥"+minPrice' placeholder-class='placeholder'
|
||||
name="money" type='digit'></input>
|
||||
<view class="all" @click="allCash">全部提现</view>
|
||||
</view>
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>持卡人</view>
|
||||
<view class='input'><input placeholder='请输入持卡人姓名' placeholder-class='placeholder'
|
||||
name="name" onKeypress="javascript:if(event.keyCode == 32)event.returnValue = false;"></input></view>
|
||||
</view>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>卡号</view>
|
||||
<view class='input'><input type='number' placeholder='请输入卡号' placeholder-class='placeholder'
|
||||
name="cardnum"></input></view>
|
||||
</view>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>银行</view>
|
||||
<view class='input'>
|
||||
<picker @change="bindPickerChange" :value="index" :range="array">
|
||||
<view class="acea-row row-between-wrapper">
|
||||
<text class='Bank'>{{array[index]}}</text>
|
||||
<text class='iconfont icon-ic_rightarrow'></text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip'>
|
||||
提现手续费: <text class="price">{{withdraw_fee}}%</text>,实际到账:<text class="price">¥{{true_money}}</text>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>提现</view>
|
||||
<view class='input acea-row row-between-wrapper'>
|
||||
<input @input='inputNum' :value='cashVal' :maxlength="moneyMaxLeng" :placeholder='"最低提现金额:¥"+minPrice' placeholder-class='placeholder'
|
||||
name="money" type='digit'></input>
|
||||
<view class="all" @click="allCash">全部提现</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
<view class='tip'>
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip fee-breakdown'>
|
||||
手续费(<text class="fee-rate">{{withdraw_fee}}%</text>):<text class="price">¥{{feeAmount}}</text>
|
||||
<text class="fee-sep"> | </text>实际到账:<text class="price">¥{{actualAmount}}</text>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip tip-warning'>
|
||||
温馨提示: <text class="num">公排退款提现需收取 <text class="fee">{{withdraw_fee}}%</text> 手续费,实际到账金额以扣除手续费后为准</text>
|
||||
</view>
|
||||
</view>
|
||||
<view :hidden="currentTab != 1">
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>提现</view>
|
||||
<view class='input acea-row row-between-wrapper'>
|
||||
<input @input='inputNum' :value='cashVal' :maxlength="moneyMaxLeng" :placeholder='"最低提现金额:¥"+minPrice' placeholder-class='placeholder'
|
||||
name="money" type='digit'></input>
|
||||
<view class="all" @click="allCash">全部提现</view>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
<view :hidden="currentTab != 1">
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
<view class='item acea-row row-between-wrapper'>
|
||||
<view class='name'>提现</view>
|
||||
<view class='input acea-row row-between-wrapper'>
|
||||
<input @input='inputNum' :value='cashVal' :maxlength="moneyMaxLeng" :placeholder='"最低提现金额:¥"+minPrice' placeholder-class='placeholder'
|
||||
name="money" type='digit'></input>
|
||||
<view class="all" @click="allCash">全部提现</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
<view class='tip'>
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip fee-breakdown'>
|
||||
手续费(<text class="fee-rate">{{withdraw_fee}}%</text>):<text class="price">¥{{feeAmount}}</text>
|
||||
<text class="fee-sep"> | </text>实际到账:<text class="price">¥{{actualAmount}}</text>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip tip-warning'>
|
||||
温馨提示: <text class="num">公排退款提现需收取 <text class="fee">{{withdraw_fee}}%</text> 手续费,实际到账金额以扣除手续费后为准</text>
|
||||
</view>
|
||||
</view>
|
||||
<view :hidden="currentTab != 2">
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
<view :hidden="currentTab != 2">
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
@@ -152,17 +163,21 @@
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip'>
|
||||
提现手续费: <text class="price">{{withdraw_fee}}%</text>,实际到账:<text class="price">¥{{true_money}}</text>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip fee-breakdown'>
|
||||
手续费(<text class="fee-rate">{{withdraw_fee}}%</text>):<text class="price">¥{{feeAmount}}</text>
|
||||
<text class="fee-sep"> | </text>实际到账:<text class="price">¥{{actualAmount}}</text>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
<view :hidden='currentTab != 3'>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip tip-warning'>
|
||||
温馨提示: <text class="num">公排退款提现需收取 <text class="fee">{{withdraw_fee}}%</text> 手续费,实际到账金额以扣除手续费后为准</text>
|
||||
</view>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
<view :hidden='currentTab != 3'>
|
||||
<form @submit="subCash">
|
||||
<view class='list'>
|
||||
<view class="itemCon">
|
||||
@@ -204,18 +219,22 @@
|
||||
当前可提现金额: <text
|
||||
class="price">¥{{userInfo.commissionCount}}</text>,冻结佣金:¥{{userInfo.broken_commission}}
|
||||
</view>
|
||||
<view class='tip'>
|
||||
提现手续费: <text class="price">{{withdraw_fee}}%</text>,实际到账:<text class="price">¥{{true_money}}</text>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip fee-breakdown'>
|
||||
手续费(<text class="fee-rate">{{withdraw_fee}}%</text>):<text class="price">¥{{feeAmount}}</text>
|
||||
<text class="fee-sep"> | </text>实际到账:<text class="price">¥{{actualAmount}}</text>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
<view class='tip'>
|
||||
说明: <text class="num">每笔佣金的冻结期为{{userInfo.broken_day}}天,到期后可提现</text>
|
||||
</view>
|
||||
<view class='tip tip-warning'>
|
||||
温馨提示: <text class="num">公排退款提现需收取 <text class="fee">{{withdraw_fee}}%</text> 手续费,实际到账金额以扣除手续费后为准</text>
|
||||
</view>
|
||||
</view>
|
||||
<button formType="submit" class='bnt bg-color'>立即提现</button>
|
||||
</form>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<home></home>
|
||||
</view>
|
||||
</template>
|
||||
@@ -226,6 +245,7 @@
|
||||
extractBank,
|
||||
getUserInfo
|
||||
} from '@/api/user.js';
|
||||
import { getWithdrawInfo } from '@/api/hjfAssets.js';
|
||||
import { toLogin } from '@/libs/login.js';
|
||||
import { mapGetters } from "vuex";
|
||||
import colors from '@/mixins/color.js';
|
||||
@@ -257,7 +277,12 @@
|
||||
extract_wechat_type:0,
|
||||
cashVal: '',
|
||||
copyIndex:null,
|
||||
platform: ''
|
||||
platform: '',
|
||||
/**
|
||||
* 提现配置信息(来自 hjfAssets.getWithdrawInfo)
|
||||
* @type {{ now_money: string, min_extract: number, fee_rate: number, extract_bank: string[] }}
|
||||
*/
|
||||
withdrawInfo: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -269,6 +294,24 @@
|
||||
// }else{
|
||||
// return false
|
||||
// }
|
||||
},
|
||||
/**
|
||||
* 当前输入金额对应的手续费金额(7%,保留两位小数)
|
||||
* @returns {string}
|
||||
*/
|
||||
feeAmount() {
|
||||
const val = parseFloat(this.cashVal) || 0;
|
||||
const rate = parseFloat(this.withdraw_fee) || 7;
|
||||
return (Math.floor(val * rate) / 100).toFixed(2);
|
||||
},
|
||||
/**
|
||||
* 扣除手续费后实际到账金额(保留两位小数)
|
||||
* @returns {string}
|
||||
*/
|
||||
actualAmount() {
|
||||
const val = parseFloat(this.cashVal) || 0;
|
||||
const fee = parseFloat(this.feeAmount) || 0;
|
||||
return Math.max(0, val - fee).toFixed(2);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -298,6 +341,7 @@
|
||||
})
|
||||
this.getUserInfo();
|
||||
this.getUserExtractBank();
|
||||
this.loadWithdrawInfo();
|
||||
} else {
|
||||
toLogin()
|
||||
}
|
||||
@@ -365,8 +409,22 @@
|
||||
onLoadFun: function() {
|
||||
this.getUserInfo();
|
||||
this.getUserExtractBank();
|
||||
this.loadWithdrawInfo();
|
||||
this.isShowAuth = false;
|
||||
},
|
||||
/**
|
||||
* 加载提现配置信息(手续费率、最低提现额、可提现余额)
|
||||
* 结果存入 withdrawInfo,并将 withdraw_fee 同步为接口返回的 fee_rate(默认 7%)
|
||||
* @returns {void}
|
||||
*/
|
||||
loadWithdrawInfo: function() {
|
||||
getWithdrawInfo().then(res => {
|
||||
this.withdrawInfo = res.data || {};
|
||||
if (this.withdrawInfo.fee_rate !== undefined) {
|
||||
this.withdraw_fee = String(this.withdrawInfo.fee_rate);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 授权关闭
|
||||
authColse: function(e) {
|
||||
this.isShowAuth = e
|
||||
@@ -856,6 +914,47 @@
|
||||
}
|
||||
}
|
||||
|
||||
.cash-withdrawal .wrapper .list .tip-warning {
|
||||
color: #E6A23C;
|
||||
background-color: #FDF6EC;
|
||||
border-radius: 8rpx;
|
||||
padding: 12rpx 16rpx;
|
||||
margin-top: 12rpx;
|
||||
line-height: 1.6;
|
||||
|
||||
.num{
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.fee{
|
||||
color: #E64340;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.cash-withdrawal .wrapper .list .fee-breakdown {
|
||||
background-color: #F0F9EB;
|
||||
border-radius: 8rpx;
|
||||
padding: 10rpx 16rpx;
|
||||
margin-top: 12rpx;
|
||||
color: #606266;
|
||||
|
||||
.fee-rate {
|
||||
color: #E64340;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fee-sep {
|
||||
color: #CCCCCC;
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
|
||||
.price {
|
||||
color: var(--view-theme);
|
||||
margin: 0 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.cash-withdrawal .wrapper .list .tip2 {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
|
||||
@@ -32,24 +32,28 @@
|
||||
class='recharge'>储值</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class='cumulative acea-row row-middle'>
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<view class='item'>
|
||||
<view>累计储值(元)</view>
|
||||
<view class='money'>{{userInfo.recharge || 0}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP -->
|
||||
<view class='item' v-if="recharge_switch">
|
||||
<view>累计储值(元)</view>
|
||||
<view class='money'>{{userInfo.recharge || 0}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class='item'>
|
||||
<view>累计消费(元)</view>
|
||||
<view class='money'>{{userInfo.orderStatusSum || 0}}</view>
|
||||
</view>
|
||||
<view class='cumulative acea-row row-middle'>
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
<view class='item'>
|
||||
<view>累计储值(元)</view>
|
||||
<view class='money'>{{userInfo.recharge || 0}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP -->
|
||||
<view class='item' v-if="recharge_switch">
|
||||
<view>累计储值(元)</view>
|
||||
<view class='money'>{{userInfo.recharge || 0}}</view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<view class='item'>
|
||||
<view>累计消费(元)</view>
|
||||
<view class='money'>{{userInfo.orderStatusSum || 0}}</view>
|
||||
</view>
|
||||
<view class='item'>
|
||||
<view>公排退款(元)</view>
|
||||
<view class='money'>{{queueRefundedTotal}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="pictrue">
|
||||
<image :src="imgHost+'/statics/images/users/pig.png'"></image>
|
||||
</view>
|
||||
@@ -129,6 +133,7 @@
|
||||
import {
|
||||
mapGetters
|
||||
} from "vuex";
|
||||
import { getQueueStatus } from '@/api/hjfQueue.js';
|
||||
import recommend from '@/components/recommend/index';
|
||||
import colors from "@/mixins/color";
|
||||
import {
|
||||
@@ -148,6 +153,12 @@
|
||||
userInfo: {
|
||||
now_money: 0,
|
||||
},
|
||||
/**
|
||||
* 公排累计退款金额(元)
|
||||
* 由 getQueueStatus() 返回的 myOrders 中 status===1 的订单金额累加而来
|
||||
* @type {number}
|
||||
*/
|
||||
queueRefundedTotal: 0,
|
||||
hostProduct: [],
|
||||
isClose: false,
|
||||
recharge_switch: 0,
|
||||
@@ -167,6 +178,7 @@
|
||||
if (newV) {
|
||||
this.getUserInfo();
|
||||
this.get_activity();
|
||||
this.getQueueRefundedTotal();
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
@@ -179,6 +191,7 @@
|
||||
if (this.isLogin) {
|
||||
this.getUserInfo();
|
||||
this.get_activity();
|
||||
this.getQueueRefundedTotal();
|
||||
} else {
|
||||
toLogin()
|
||||
}
|
||||
@@ -222,6 +235,23 @@
|
||||
that.$set(that, "activity", res.data);
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取公排累计退款金额
|
||||
* 调用 getQueueStatus(),将 myOrders 中 status===1(已退款)的订单金额累加,
|
||||
* 结果保存到 queueRefundedTotal(保留两位小数,单位:元)
|
||||
* @see docs/frontend-new-pages-spec.md 6.1.4
|
||||
* @returns {void}
|
||||
*/
|
||||
getQueueRefundedTotal: function() {
|
||||
let that = this;
|
||||
getQueueStatus().then(res => {
|
||||
const orders = (res.data && res.data.myOrders) || [];
|
||||
const total = orders
|
||||
.filter(order => order.status === 1)
|
||||
.reduce((sum, order) => sum + Number(order.amount || 0), 0);
|
||||
that.queueRefundedTotal = total.toFixed(2);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 获取我的推荐
|
||||
*/
|
||||
|
||||
@@ -1,62 +1,91 @@
|
||||
<template>
|
||||
<view :style="colorStyle">
|
||||
<view class='commission-details'>
|
||||
<!-- 团队收益统计卡片(5.1.7) -->
|
||||
<view class="team-stats-card">
|
||||
<view class="team-stats-title">团队业绩统计</view>
|
||||
<view class="team-stats-row">
|
||||
<view class="team-stats-item">
|
||||
<view class="team-stats-num">{{ teamData.direct_count }}</view>
|
||||
<view class="team-stats-label">直推人数</view>
|
||||
</view>
|
||||
<view class="team-stats-divider"></view>
|
||||
<view class="team-stats-item">
|
||||
<view class="team-stats-num">{{ teamData.umbrella_count }}</view>
|
||||
<view class="team-stats-label">伞下人数</view>
|
||||
</view>
|
||||
<view class="team-stats-divider"></view>
|
||||
<view class="team-stats-item">
|
||||
<view class="team-stats-num text-primary">{{ teamData.umbrella_orders }}</view>
|
||||
<view class="team-stats-label">团队订单数</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class='search acea-row row-between-wrapper' v-if="recordType != 1 && recordType != 4">
|
||||
<view class='input'>
|
||||
<text class="iconfont icon-ic_search"></text>
|
||||
<input placeholder='搜索用户名称' placeholder-class='placeholder' v-model="keyword" @confirm="submitForm"
|
||||
confirm-type='search' name="search"></input>
|
||||
<input
|
||||
placeholder='搜索用户名称'
|
||||
placeholder-class='placeholder'
|
||||
v-model="keyword"
|
||||
@confirm="submitForm"
|
||||
confirm-type='search'
|
||||
name="search"
|
||||
></input>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<timeSlot @changeTime="changeTime"></timeSlot>
|
||||
|
||||
<view class='sign-record'>
|
||||
<view class="top_num" v-if="recordType != 4 && recordList.length">
|
||||
支出:¥{{expend || 0}} 收入:¥{{income || 0}}
|
||||
</view>
|
||||
<view class="box">
|
||||
<block v-for="(item,index) in recordList" :key="index" v-if="recordList.length>0">
|
||||
<block v-for="(item, index) in recordList" :key="index" v-if="recordList.length > 0">
|
||||
<view class='list'>
|
||||
<view class='item'>
|
||||
<!-- <view class='data'>{{item.time}}</view> -->
|
||||
<view class='listn'>
|
||||
<!-- <block v-for="(child,indexn) in item.child" :key="indexn"> -->
|
||||
<view class='itemn1 flex justify-between'>
|
||||
<view>
|
||||
<view class='name line1'>
|
||||
{{item.title}}
|
||||
<!-- <text class="status_badge success" v-if="recordType == 4 && item.status == 1">审核通过</text> -->
|
||||
<text class="status_badge default" v-if="recordType == 4 && item.status == 0">待审核</text>
|
||||
<text class="status_badge error" v-if="recordType == 4 && item.status == 2">审核未通过</text>
|
||||
<!-- 提现记录: 0 待审核 1 通过 2 未通过 -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- 积分来源标签(直推 / 伞下) -->
|
||||
<view class="income-type-tag" v-if="item.type">
|
||||
<text class="tag-direct" v-if="item.type === 'direct'">直推奖励</text>
|
||||
<text class="tag-umbrella" v-else-if="item.type === 'umbrella'">伞下奖励</text>
|
||||
</view>
|
||||
<view class="mark" v-if="item.extract_status == -1">原因:{{item.extract_msg}}</view>
|
||||
<view>{{item.add_time}}</view>
|
||||
<view v-if="item.is_frozen && item.is_frozen == 1">佣金冻结中,解冻时间:{{item.frozen_time}}</view>
|
||||
</view>
|
||||
<view>
|
||||
<view class='num' :class="recordType == 4 && item.status == 0?'on':''"
|
||||
v-if="item.pm == 1">+{{item.number}}</view>
|
||||
<view class='num' v-else>-{{item.number}}</view>
|
||||
<!-- 积分数量展示(不带 ¥,见 6.1.5 §2) -->
|
||||
<view class='num' :class="recordType == 4 && item.status == 0 ? 'on' : ''"
|
||||
v-if="item.pm == 1">+{{item.points !== undefined ? item.points : item.number}}</view>
|
||||
<view class='num' v-else>-{{item.points !== undefined ? item.points : item.number}}</view>
|
||||
<view class="fail" v-if="item.extract_status == -1 && item.type == 'extract'">审核未通过</view>
|
||||
<view class="wait" v-if="item.extract_status == 0 && item.type == 'extract'">待审核</view>
|
||||
<view class="wait" v-if="item.is_frozen == 1">冻结中</view>
|
||||
<view class="w-154 h-56 rd-30rpx flex-center mt-16 bg-color fs-24 text--w111-fff"
|
||||
v-if="item.extract_status == 0 && item.type == 'extract'"
|
||||
@tap="extractCancel(item.link_id)"
|
||||
>取消提现</view>
|
||||
v-if="item.extract_status == 0 && item.type == 'extract'"
|
||||
@tap="extractCancel(item.link_id)">取消提现</view>
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view class="w-154 h-56 rd-30rpx flex-center mt-16 ml-12 bg-color fs-24 text--w111-fff"
|
||||
v-if="item.wechat_state == 1 && item.type == 'extract'"
|
||||
v-if="item.wechat_state == 1 && item.type == 'extract'"
|
||||
@tap="jumpPath('/pages/users/user_spread_money/receiving?type=1&id=' + item.extract_order_id)">立即收款</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view class="w-154 h-56 rd-30rpx flex-center mt-16 ml-12 bg-color fs-24 text--w111-fff"
|
||||
v-if="item.wechat_state == 1 && item.type == 'extract' && isWeixin"
|
||||
v-if="item.wechat_state == 1 && item.type == 'extract' && isWeixin"
|
||||
@tap="jumpPath('/pages/users/user_spread_money/receiving?type=1&id=' + item.extract_order_id)">立即收款</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- </block> -->
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -64,7 +93,7 @@
|
||||
</view>
|
||||
|
||||
<view class='loadingicon acea-row row-center-wrapper' v-if="recordList.length">
|
||||
<text class='loading iconfont icon-jiazai' :hidden='loading==false'></text>{{loadTitle}}
|
||||
<text class='loading iconfont icon-jiazai' :hidden='loading == false'></text>{{loadTitle}}
|
||||
</view>
|
||||
<view class="empty" v-if="!recordList.length">
|
||||
<emptyPage title='暂无数据~' src="/statics/images/noOrder.gif"></emptyPage>
|
||||
@@ -76,21 +105,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { moneyList, getSpreadInfo, extractCancelApi } from '@/api/user.js';
|
||||
import { moneyList, getSpreadInfo, spreadCount, extractCancelApi } from '@/api/user.js';
|
||||
import { getTeamData, getTeamIncome } from '@/api/hjfMember.js';
|
||||
import { toLogin } from '@/libs/login.js';
|
||||
import { mapGetters } from "vuex";
|
||||
import emptyPage from '@/components/emptyPage.vue'
|
||||
import { mapGetters } from 'vuex';
|
||||
import emptyPage from '@/components/emptyPage.vue';
|
||||
import colors from '@/mixins/color.js';
|
||||
import timeSlot from '../components/timeSlot/index.vue';
|
||||
// #ifdef H5
|
||||
import Auth from '@/libs/wechat';
|
||||
// #endif
|
||||
|
||||
export default {
|
||||
components: {
|
||||
emptyPage,
|
||||
timeSlot
|
||||
},
|
||||
components: { emptyPage, timeSlot },
|
||||
mixins: [colors],
|
||||
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
@@ -111,12 +140,33 @@
|
||||
income: '',
|
||||
expend: '',
|
||||
disabled: false,
|
||||
|
||||
/**
|
||||
* 团队概况数据
|
||||
* @type {{ direct_count: number, umbrella_count: number, umbrella_orders: number }}
|
||||
*/
|
||||
teamData: {
|
||||
direct_count: 0,
|
||||
umbrella_count: 0,
|
||||
umbrella_orders: 0,
|
||||
},
|
||||
|
||||
/**
|
||||
* 团队收益明细列表(来自 getTeamIncome)
|
||||
* @type {Array<{ id: number, title: string, type: string, points: number, from_nickname: string, add_time: string }>}
|
||||
*/
|
||||
teamIncome: [],
|
||||
|
||||
// #ifdef H5
|
||||
isWeixin: Auth.isWeixin(),
|
||||
//#endif
|
||||
// #endif
|
||||
};
|
||||
},
|
||||
computed: mapGetters(['isLogin']),
|
||||
|
||||
computed: {
|
||||
...mapGetters(['isLogin']),
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
if (this.isLogin) {
|
||||
this.type = options.type;
|
||||
@@ -124,7 +174,8 @@
|
||||
toLogin();
|
||||
}
|
||||
},
|
||||
onShow: function() {
|
||||
|
||||
onShow() {
|
||||
uni.removeStorageSync('form_type_cart');
|
||||
this.page = 1;
|
||||
this.limit = 20;
|
||||
@@ -133,25 +184,22 @@
|
||||
this.status = false;
|
||||
this.$set(this, 'recordList', []);
|
||||
this.$set(this, 'times', []);
|
||||
let type = this.type;
|
||||
|
||||
this.loadTeamData();
|
||||
|
||||
const type = this.type;
|
||||
if (type == 1) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: "佣金记录"
|
||||
});
|
||||
uni.setNavigationBarTitle({ title: '推荐收益' });
|
||||
this.name = '提现总额';
|
||||
this.recordType = 3;
|
||||
this.getRecordList();
|
||||
} else if (type == 2) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: "佣金记录"
|
||||
});
|
||||
this.name = '佣金明细';
|
||||
uni.setNavigationBarTitle({ title: '推荐收益' });
|
||||
this.name = '推荐收益明细';
|
||||
this.recordType = 3;
|
||||
this.getRecordList();
|
||||
} else if (type == 4) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: "提现记录"
|
||||
});
|
||||
uni.setNavigationBarTitle({ title: '提现记录' });
|
||||
this.name = '提现明细';
|
||||
this.recordType = 4;
|
||||
this.getRecordList();
|
||||
@@ -161,23 +209,44 @@
|
||||
icon: 'none',
|
||||
duration: 1000,
|
||||
mask: true,
|
||||
success: function(res) {
|
||||
setTimeout(function() {
|
||||
success() {
|
||||
setTimeout(() => {
|
||||
// #ifndef H5
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
});
|
||||
uni.navigateBack({ delta: 1 });
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
history.back();
|
||||
// #endif
|
||||
|
||||
}, 1200)
|
||||
}, 1200);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 加载团队概况和收益明细,统一在 onShow 调用
|
||||
* @returns {void}
|
||||
*/
|
||||
loadTeamData() {
|
||||
getTeamData().then(res => {
|
||||
const d = (res && res.data) || {};
|
||||
this.teamData = {
|
||||
direct_count: d.direct_count || 0,
|
||||
umbrella_count: d.umbrella_count || 0,
|
||||
umbrella_orders: d.umbrella_orders || 0,
|
||||
};
|
||||
}).catch(() => {});
|
||||
|
||||
getTeamIncome({ page: 1, limit: this.limit }).then(res => {
|
||||
this.teamIncome = (res && res.data && res.data.list) || [];
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜索框确认时重置并重新加载列表
|
||||
* @returns {void}
|
||||
*/
|
||||
submitForm() {
|
||||
this.page = 1;
|
||||
this.limit = 20;
|
||||
@@ -188,98 +257,166 @@
|
||||
this.$set(this, 'times', []);
|
||||
this.getRecordList();
|
||||
},
|
||||
|
||||
/**
|
||||
* 时间筛选回调
|
||||
* @param {{ start: number, stop: number }} time - 筛选时间段
|
||||
* @returns {void}
|
||||
*/
|
||||
changeTime(time) {
|
||||
this.start = time.start
|
||||
this.stop = time.stop
|
||||
this.start = time.start;
|
||||
this.stop = time.stop;
|
||||
this.page = 1;
|
||||
this.loadend = false;
|
||||
this.$set(this, 'recordList', []);
|
||||
this.getRecordList();
|
||||
},
|
||||
getRecordList: function() {
|
||||
let that = this;
|
||||
let page = that.page;
|
||||
let limit = that.limit;
|
||||
let recordType = that.recordType;
|
||||
if (that.loading) return;
|
||||
if (that.loadend) return;
|
||||
|
||||
/**
|
||||
* 加载收益流水列表(分页追加)
|
||||
* @returns {void}
|
||||
*/
|
||||
getRecordList() {
|
||||
const that = this;
|
||||
const { page, limit, recordType } = that;
|
||||
if (that.loading || that.loadend) return;
|
||||
that.loading = true;
|
||||
that.loadTitle = '';
|
||||
moneyList({
|
||||
keyword: this.keyword,
|
||||
start: this.start,
|
||||
stop: this.stop,
|
||||
page: page,
|
||||
limit: limit
|
||||
page,
|
||||
limit,
|
||||
}, recordType).then(res => {
|
||||
this.expend = res.data.expend;
|
||||
this.income = res.data.income;
|
||||
// for (let i = 0; i < res.data.time.length; i++) {
|
||||
// // if (!this.times.includes(res.data.time[i])) {
|
||||
// this.times.push(res.data.time[i])
|
||||
// this.recordList.push({
|
||||
// time: res.data.time[i],
|
||||
// child: []
|
||||
// })
|
||||
// // }
|
||||
// }
|
||||
// // for (let x = 0; x < this.times.length; x++) {
|
||||
// for (let j = 0; j < res.data.list.length; j++) {
|
||||
// // if (this.times[x] === res.data.list[j].time_key) {
|
||||
|
||||
// // }
|
||||
// this.recordList[j].child.push(res.data.list[j])
|
||||
// }
|
||||
// // }
|
||||
this.recordList = this.recordList.concat(res.data.list)
|
||||
let loadend = res.data.list.length < that.limit;
|
||||
this.recordList = this.recordList.concat(res.data.list);
|
||||
const loadend = res.data.list.length < that.limit;
|
||||
that.loadend = loadend;
|
||||
that.loadTitle = loadend ? '没有更多内容啦~' : '加载更多';
|
||||
that.page += 1;
|
||||
that.loading = false;
|
||||
}).catch(err => {
|
||||
}).catch(() => {
|
||||
that.loading = false;
|
||||
that.loadTitle = '加载更多';
|
||||
})
|
||||
},
|
||||
getRecordListCount: function() {
|
||||
let that = this;
|
||||
getSpreadInfo().then(res => {
|
||||
that.recordCount = res.data.commissionCount;
|
||||
that.extractCount = res.data.extractCount;
|
||||
});
|
||||
},
|
||||
jumpPath(url){
|
||||
uni.navigateTo({
|
||||
url
|
||||
})
|
||||
|
||||
/**
|
||||
* 页面内跳转
|
||||
* @param {string} url - 目标路由
|
||||
* @returns {void}
|
||||
*/
|
||||
jumpPath(url) {
|
||||
uni.navigateTo({ url });
|
||||
},
|
||||
extractCancel(id){
|
||||
if(this.disabled) return
|
||||
|
||||
/**
|
||||
* 取消提现申请
|
||||
* @param {number|string} id - 提现订单 ID
|
||||
* @returns {void}
|
||||
*/
|
||||
extractCancel(id) {
|
||||
if (this.disabled) return;
|
||||
this.disabled = true;
|
||||
extractCancelApi(id).then(res=>{
|
||||
extractCancelApi(id).then(res => {
|
||||
this.disabled = false;
|
||||
this.changeTime({start:0,stop: 0});
|
||||
return this.$util.Tips({
|
||||
title: res.msg
|
||||
});
|
||||
}).catch(err=>{
|
||||
return this.$util.Tips({
|
||||
title: err
|
||||
});
|
||||
})
|
||||
}
|
||||
this.changeTime({ start: 0, stop: 0 });
|
||||
return this.$util.Tips({ title: res.msg });
|
||||
}).catch(err => {
|
||||
this.disabled = false;
|
||||
return this.$util.Tips({ title: err });
|
||||
});
|
||||
},
|
||||
},
|
||||
onReachBottom: function() {
|
||||
|
||||
onReachBottom() {
|
||||
this.getRecordList();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.empty{
|
||||
.empty {
|
||||
margin: 0 20rpx 20rpx 20rpx;
|
||||
}
|
||||
|
||||
/* ===== 团队统计卡片 ===== */
|
||||
.team-stats-card {
|
||||
background: linear-gradient(135deg, #4e9f3d 0%, #1e5128 100%);
|
||||
border-radius: 20rpx;
|
||||
margin: 20rpx 20rpx 0;
|
||||
padding: 30rpx 20rpx 24rpx;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.team-stats-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-bottom: 24rpx;
|
||||
opacity: 0.92;
|
||||
}
|
||||
|
||||
.team-stats-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.team-stats-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.team-stats-num {
|
||||
font-size: 40rpx;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.team-stats-num.text-primary {
|
||||
color: #ffe082;
|
||||
}
|
||||
|
||||
.team-stats-label {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.8;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.team-stats-divider {
|
||||
width: 1rpx;
|
||||
height: 56rpx;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* ===== 积分来源标签 ===== */
|
||||
.income-type-tag {
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.tag-direct,
|
||||
.tag-umbrella {
|
||||
display: inline-block;
|
||||
height: 36rpx;
|
||||
border-radius: 6rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 0 10rpx;
|
||||
}
|
||||
|
||||
.tag-direct {
|
||||
background: rgba(78, 159, 61, 0.12);
|
||||
color: #4e9f3d;
|
||||
}
|
||||
|
||||
.tag-umbrella {
|
||||
background: rgba(24, 144, 255, 0.1);
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* ===== 搜索框 ===== */
|
||||
.commission-details .search {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
@@ -301,12 +438,6 @@
|
||||
padding-left: 70rpx;
|
||||
}
|
||||
|
||||
.box {
|
||||
border-radius: 24rpx;
|
||||
margin: 0 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.commission-details .search .input .placeholder {
|
||||
color: #bbb;
|
||||
}
|
||||
@@ -320,12 +451,15 @@
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
/* ===== 列表区 ===== */
|
||||
.sign-record {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.commission-details .promoterHeader .headerCon .money {
|
||||
font-size: 36rpx;
|
||||
.box {
|
||||
border-radius: 24rpx;
|
||||
margin: 0 20rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.top_num {
|
||||
@@ -337,24 +471,47 @@
|
||||
.radius15 {
|
||||
border-radius: 14rpx 14rpx 0 0;
|
||||
}
|
||||
.sign-record .list .item .listn .itemn1{border-bottom:1rpx solid #eee;padding:22rpx 24rpx;}
|
||||
.sign-record .list .item .listn .itemn1 .name{width:390rpx;font-size:28rpx;color:#333;margin-bottom:12rpx;}
|
||||
.sign-record .list .item .listn .itemn1 .num{font-size:36rpx;color:#333333;font-family:'Regular';text-align: right;}
|
||||
.sign-record .list .item .listn .itemn1 .num.font-color{color:#e93323!important;}
|
||||
.sign-record .list .item .listn .itemn1 .fail{
|
||||
|
||||
.sign-record .list .item .listn .itemn1 {
|
||||
border-bottom: 1rpx solid #eee;
|
||||
padding: 22rpx 24rpx;
|
||||
}
|
||||
|
||||
.sign-record .list .item .listn .itemn1 .name {
|
||||
width: 390rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.sign-record .list .item .listn .itemn1 .num {
|
||||
font-size: 36rpx;
|
||||
color: #333333;
|
||||
font-family: 'Regular';
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.sign-record .list .item .listn .itemn1 .num.font-color {
|
||||
color: #e93323 !important;
|
||||
}
|
||||
|
||||
.sign-record .list .item .listn .itemn1 .fail {
|
||||
color: #E93323;
|
||||
margin-top: 14rpx;
|
||||
text-align: right;
|
||||
}
|
||||
.sign-record .list .item .listn .itemn1 .wait{
|
||||
|
||||
.sign-record .list .item .listn .itemn1 .wait {
|
||||
color: #FFB200;
|
||||
margin-top: 14rpx;
|
||||
text-align: right;
|
||||
}
|
||||
.mark{
|
||||
|
||||
.mark {
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
.status_badge{
|
||||
|
||||
.status_badge {
|
||||
display: inline-block;
|
||||
height: 40rpx;
|
||||
border-radius: 8rpx;
|
||||
@@ -362,18 +519,21 @@
|
||||
line-height: 40rpx;
|
||||
font-family: PingFangSC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
margin-left:16rpx;
|
||||
padding:0 12rpx 0;
|
||||
margin-left: 16rpx;
|
||||
padding: 0 12rpx 0;
|
||||
}
|
||||
.success{
|
||||
|
||||
.success {
|
||||
background: rgba(24, 144, 255, .1);
|
||||
color: #1890FF;
|
||||
}
|
||||
.default{
|
||||
|
||||
.default {
|
||||
background: #FFF1E5;
|
||||
color: #FF7D00;
|
||||
}
|
||||
.error{
|
||||
|
||||
.error {
|
||||
background: #FDEBEB;
|
||||
color: #F53F3F;
|
||||
}
|
||||
|
||||
BIN
pro_v3.5.1/view/uniapp/static/images/home-logo.png
Normal file
BIN
pro_v3.5.1/view/uniapp/static/images/home-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -183,7 +183,9 @@ const actions = {
|
||||
let diyVersion = uni.getStorageSync('diyVersionNav');
|
||||
if (res.data.version === diyVersion) {
|
||||
let pageFooter = uni.getStorageSync('pageFooterData');
|
||||
commit("SET_PAGE_FOOTER", pageFooter);
|
||||
if (pageFooter && pageFooter.menuList && pageFooter.menuList.length) {
|
||||
commit("SET_PAGE_FOOTER", pageFooter);
|
||||
}
|
||||
} else {
|
||||
uni.setStorageSync('diyVersionNav', res.data.version);
|
||||
let result = await getNavigation();
|
||||
|
||||
809
pro_v3.5.1/view/uniapp/utils/hjfMockData.js
Normal file
809
pro_v3.5.1/view/uniapp/utils/hjfMockData.js
Normal file
@@ -0,0 +1,809 @@
|
||||
/**
|
||||
* 黄精粉健康商城 - UniApp Mock 数据集中管理
|
||||
* Phase 1 前端开发使用,Phase 4 集成后可移除
|
||||
*/
|
||||
|
||||
// ========== 场景切换系统 ==========
|
||||
|
||||
/**
|
||||
* 当前演示场景
|
||||
* 'A' - 新用户(首次体验)
|
||||
* 'B' - 活跃用户(等待退款中)- 默认
|
||||
* 'C' - VIP用户(退款刚触发)
|
||||
*/
|
||||
let MOCK_SCENARIO = 'B';
|
||||
|
||||
/**
|
||||
* 切换场景并触发页面刷新
|
||||
* @param {string} scenario - 'A' | 'B' | 'C'
|
||||
*/
|
||||
export function setMockScenario(scenario) {
|
||||
if (['A', 'B', 'C'].includes(scenario)) {
|
||||
MOCK_SCENARIO = scenario;
|
||||
console.log(`[HJF Mock] 已切换到场景 ${scenario}`);
|
||||
// 触发全局事件,通知页面刷新
|
||||
uni.$emit('hjf-scenario-changed', scenario);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前场景
|
||||
*/
|
||||
export function getCurrentScenario() {
|
||||
return MOCK_SCENARIO;
|
||||
}
|
||||
|
||||
// ========== 公排模块 ==========
|
||||
|
||||
export const MOCK_QUEUE_STATUS = {
|
||||
totalOrders: 156,
|
||||
myOrders: [
|
||||
{
|
||||
id: 1,
|
||||
order_id: 'HJF202603100001',
|
||||
amount: 3600.00,
|
||||
queue_no: 142,
|
||||
status: 0,
|
||||
refund_time: 0,
|
||||
trigger_batch: 0,
|
||||
add_time: 1741593600,
|
||||
position: 14,
|
||||
estimated_wait: '约3天'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
order_id: 'HJF202603080002',
|
||||
amount: 3600.00,
|
||||
queue_no: 98,
|
||||
status: 1,
|
||||
refund_time: 1741507200,
|
||||
trigger_batch: 24,
|
||||
add_time: 1741420800,
|
||||
position: 0,
|
||||
estimated_wait: '已退款'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
order_id: 'HJF202603070003',
|
||||
amount: 3600.00,
|
||||
queue_no: 85,
|
||||
status: 0,
|
||||
refund_time: 0,
|
||||
trigger_batch: 0,
|
||||
add_time: 1741334400,
|
||||
position: 46,
|
||||
estimated_wait: '约12天'
|
||||
}
|
||||
],
|
||||
progress: {
|
||||
current_batch_count: 2,
|
||||
trigger_multiple: 4,
|
||||
next_refund_queue_no: 39
|
||||
}
|
||||
};
|
||||
|
||||
export const MOCK_QUEUE_HISTORY = {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
order_id: 'HJF202603050001',
|
||||
amount: 3600.00,
|
||||
queue_no: 45,
|
||||
status: 1,
|
||||
refund_time: 1741334400,
|
||||
trigger_batch: 11,
|
||||
add_time: 1741161600,
|
||||
time_key: '2026-03-07'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
order_id: 'HJF202603060002',
|
||||
amount: 3600.00,
|
||||
queue_no: 67,
|
||||
status: 0,
|
||||
refund_time: 0,
|
||||
trigger_batch: 0,
|
||||
add_time: 1741248000,
|
||||
time_key: '2026-03-06'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
order_id: 'HJF202603040003',
|
||||
amount: 3600.00,
|
||||
queue_no: 33,
|
||||
status: 1,
|
||||
refund_time: 1741248000,
|
||||
trigger_batch: 8,
|
||||
add_time: 1741075200,
|
||||
time_key: '2026-03-04'
|
||||
}
|
||||
],
|
||||
count: 25,
|
||||
page: 1,
|
||||
limit: 15
|
||||
};
|
||||
|
||||
// ========== 资产模块 ==========
|
||||
|
||||
export const MOCK_ASSETS_OVERVIEW = {
|
||||
now_money: '7200.00',
|
||||
frozen_points: 15000,
|
||||
available_points: 3200,
|
||||
today_release: 6,
|
||||
total_queue_refund: '14400.00',
|
||||
total_points_earned: 18200,
|
||||
member_level: 2,
|
||||
member_level_name: '云店'
|
||||
};
|
||||
|
||||
export const MOCK_POINTS_DETAIL = {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '直推奖励 - 用户张三购买报单商品',
|
||||
type: 'reward_direct',
|
||||
points: 800,
|
||||
pm: 1,
|
||||
status: 'frozen',
|
||||
add_time: '2026-03-10 14:30',
|
||||
order_id: 'HJF202603100005'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '每日释放 - 待释放积分自动解冻',
|
||||
type: 'release',
|
||||
points: 6,
|
||||
pm: 1,
|
||||
status: 'released',
|
||||
add_time: '2026-03-10 00:00',
|
||||
release_date: '2026-03-10'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '积分消费 - 购买普通商品',
|
||||
type: 'consume',
|
||||
points: 200,
|
||||
pm: 0,
|
||||
status: 'released',
|
||||
add_time: '2026-03-09 16:22',
|
||||
order_id: 'HJF202603090012'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '伞下奖励 - 用户李四购买报单商品',
|
||||
type: 'reward_umbrella',
|
||||
points: 300,
|
||||
pm: 1,
|
||||
status: 'frozen',
|
||||
add_time: '2026-03-09 10:15',
|
||||
order_id: 'HJF202603090003'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '每日释放 - 待释放积分自动解冻',
|
||||
type: 'release',
|
||||
points: 6,
|
||||
pm: 1,
|
||||
status: 'released',
|
||||
add_time: '2026-03-09 00:00',
|
||||
release_date: '2026-03-09'
|
||||
}
|
||||
],
|
||||
count: 45,
|
||||
page: 1,
|
||||
limit: 15
|
||||
};
|
||||
|
||||
export const MOCK_CASH_DETAIL = {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '公排退款 - 订单HJF202603050001',
|
||||
amount: '3600.00',
|
||||
pm: 1,
|
||||
add_time: '2026-03-07 12:00',
|
||||
order_id: 'HJF202603050001'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '提现 - 微信零钱',
|
||||
amount: '930.00',
|
||||
pm: 0,
|
||||
add_time: '2026-03-06 15:30',
|
||||
remark: '手续费¥70.00'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '购物消费',
|
||||
amount: '299.00',
|
||||
pm: 0,
|
||||
add_time: '2026-03-05 09:20',
|
||||
order_id: 'HJF202603050010'
|
||||
}
|
||||
],
|
||||
count: 12,
|
||||
page: 1,
|
||||
limit: 15
|
||||
};
|
||||
|
||||
export const MOCK_WITHDRAW_INFO = {
|
||||
now_money: '7200.00',
|
||||
min_extract: 100,
|
||||
fee_rate: 7,
|
||||
extract_bank: ['微信零钱', '支付宝', '银行卡'],
|
||||
bank_list: [
|
||||
{ bank_name: '中国工商银行', bank_code: '1234****5678' }
|
||||
]
|
||||
};
|
||||
|
||||
// ========== 会员模块 ==========
|
||||
|
||||
export const MOCK_MEMBER_INFO = {
|
||||
member_level: 2,
|
||||
member_level_name: '云店',
|
||||
direct_count: 8,
|
||||
umbrella_count: 35,
|
||||
umbrella_orders: 42,
|
||||
next_level_name: '服务商',
|
||||
next_level_require: 100,
|
||||
progress_percent: 42
|
||||
};
|
||||
|
||||
export const MOCK_TEAM_DATA = {
|
||||
direct_count: 8,
|
||||
umbrella_count: 35,
|
||||
umbrella_orders: 42,
|
||||
members: [
|
||||
{
|
||||
uid: 10087,
|
||||
nickname: '张三',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 1,
|
||||
member_level_name: '创客',
|
||||
join_time: '2026-02-15',
|
||||
direct_orders: 5,
|
||||
is_direct: true
|
||||
},
|
||||
{
|
||||
uid: 10088,
|
||||
nickname: '李四',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 0,
|
||||
member_level_name: '普通会员',
|
||||
join_time: '2026-03-01',
|
||||
direct_orders: 1,
|
||||
is_direct: false,
|
||||
parent_nickname: '张三'
|
||||
},
|
||||
{
|
||||
uid: 10089,
|
||||
nickname: '王五',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 2,
|
||||
member_level_name: '云店',
|
||||
join_time: '2026-01-20',
|
||||
direct_orders: 12,
|
||||
is_direct: true
|
||||
}
|
||||
],
|
||||
page: 1,
|
||||
count: 35
|
||||
};
|
||||
|
||||
export const MOCK_TEAM_INCOME = {
|
||||
list: [
|
||||
{
|
||||
id: 1,
|
||||
title: '直推奖励',
|
||||
from_uid: 10087,
|
||||
from_nickname: '张三',
|
||||
order_id: 'HJF202603100005',
|
||||
points: 800,
|
||||
type: 'direct',
|
||||
add_time: '2026-03-10 14:30'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '伞下奖励(级差)',
|
||||
from_uid: 10088,
|
||||
from_nickname: '李四',
|
||||
order_id: 'HJF202603090003',
|
||||
points: 300,
|
||||
type: 'umbrella',
|
||||
add_time: '2026-03-09 10:15'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '直推奖励',
|
||||
from_uid: 10089,
|
||||
from_nickname: '王五',
|
||||
order_id: 'HJF202603080010',
|
||||
points: 800,
|
||||
type: 'direct',
|
||||
add_time: '2026-03-08 16:45'
|
||||
}
|
||||
],
|
||||
count: 22,
|
||||
page: 1,
|
||||
limit: 15
|
||||
};
|
||||
|
||||
// ========== 引导模块 ==========
|
||||
|
||||
export const MOCK_GUIDE_DATA = {
|
||||
slides: [
|
||||
{
|
||||
title: '欢迎来到黄精粉健康商城',
|
||||
desc: '健康好物,品质生活',
|
||||
image: '/static/images/guide/slide1.png'
|
||||
},
|
||||
{
|
||||
title: '公排返利机制',
|
||||
desc: '购买报单商品自动进入公排,每进4单退1单全额返还',
|
||||
image: '/static/images/guide/slide2.png'
|
||||
},
|
||||
{
|
||||
title: '会员积分体系',
|
||||
desc: '推荐好友即获积分奖励,积分每日自动释放',
|
||||
image: '/static/images/guide/slide3.png'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// ========== 场景数据集合 ==========
|
||||
|
||||
/**
|
||||
* 场景 A - 新用户(首次体验)
|
||||
*/
|
||||
const SCENARIO_A_DATA = {
|
||||
queueStatus: {
|
||||
totalOrders: 12,
|
||||
myOrders: [],
|
||||
progress: {
|
||||
current_batch_count: 0,
|
||||
trigger_multiple: 4,
|
||||
next_refund_queue_no: 4
|
||||
}
|
||||
},
|
||||
queueHistory: {
|
||||
list: [],
|
||||
count: 0,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
assetsOverview: {
|
||||
now_money: '0.00',
|
||||
frozen_points: 0,
|
||||
available_points: 0,
|
||||
today_release: 0,
|
||||
total_queue_refund: '0.00',
|
||||
total_points_earned: 0,
|
||||
member_level: 0,
|
||||
member_level_name: '普通会员'
|
||||
},
|
||||
pointsDetail: {
|
||||
list: [],
|
||||
count: 0,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
cashDetail: {
|
||||
list: [],
|
||||
count: 0,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
withdrawInfo: {
|
||||
now_money: '0.00',
|
||||
min_extract: 100,
|
||||
fee_rate: 7,
|
||||
extract_bank: ['微信零钱', '支付宝', '银行卡'],
|
||||
bank_list: []
|
||||
},
|
||||
memberInfo: {
|
||||
member_level: 0,
|
||||
member_level_name: '普通会员',
|
||||
direct_count: 0,
|
||||
umbrella_count: 0,
|
||||
umbrella_orders: 0,
|
||||
next_level_name: '创客',
|
||||
next_level_require: 3,
|
||||
progress_percent: 0
|
||||
},
|
||||
teamData: {
|
||||
direct_count: 0,
|
||||
umbrella_count: 0,
|
||||
umbrella_orders: 0,
|
||||
members: [],
|
||||
page: 1,
|
||||
count: 0
|
||||
},
|
||||
teamIncome: {
|
||||
list: [],
|
||||
count: 0,
|
||||
page: 1,
|
||||
limit: 15
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 场景 B - 活跃用户(等待退款中)- 使用原有数据
|
||||
*/
|
||||
const SCENARIO_B_DATA = {
|
||||
queueStatus: MOCK_QUEUE_STATUS,
|
||||
queueHistory: MOCK_QUEUE_HISTORY,
|
||||
assetsOverview: MOCK_ASSETS_OVERVIEW,
|
||||
pointsDetail: MOCK_POINTS_DETAIL,
|
||||
cashDetail: MOCK_CASH_DETAIL,
|
||||
withdrawInfo: MOCK_WITHDRAW_INFO,
|
||||
memberInfo: MOCK_MEMBER_INFO,
|
||||
teamData: MOCK_TEAM_DATA,
|
||||
teamIncome: MOCK_TEAM_INCOME
|
||||
};
|
||||
|
||||
/**
|
||||
* 场景 C - VIP 用户(退款刚触发)
|
||||
*/
|
||||
const SCENARIO_C_DATA = {
|
||||
queueStatus: {
|
||||
totalOrders: 289,
|
||||
myOrders: [
|
||||
{
|
||||
id: 10,
|
||||
order_id: 'HJF202603110001',
|
||||
amount: 3600.00,
|
||||
queue_no: 285,
|
||||
status: 1,
|
||||
refund_time: Date.now() / 1000 - 120,
|
||||
trigger_batch: 71,
|
||||
add_time: Date.now() / 1000 - 86400 * 5,
|
||||
position: 0,
|
||||
estimated_wait: '已退款'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
order_id: 'HJF202603090010',
|
||||
amount: 3600.00,
|
||||
queue_no: 268,
|
||||
status: 0,
|
||||
refund_time: 0,
|
||||
trigger_batch: 0,
|
||||
add_time: Date.now() / 1000 - 86400 * 2,
|
||||
position: 8,
|
||||
estimated_wait: '约2天'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
order_id: 'HJF202603080008',
|
||||
amount: 3600.00,
|
||||
queue_no: 244,
|
||||
status: 1,
|
||||
refund_time: Date.now() / 1000 - 86400 * 3,
|
||||
trigger_batch: 61,
|
||||
add_time: Date.now() / 1000 - 86400 * 8,
|
||||
position: 0,
|
||||
estimated_wait: '已退款'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
order_id: 'HJF202603050007',
|
||||
amount: 3600.00,
|
||||
queue_no: 196,
|
||||
status: 1,
|
||||
refund_time: Date.now() / 1000 - 86400 * 10,
|
||||
trigger_batch: 49,
|
||||
add_time: Date.now() / 1000 - 86400 * 15,
|
||||
position: 0,
|
||||
estimated_wait: '已退款'
|
||||
}
|
||||
],
|
||||
progress: {
|
||||
current_batch_count: 1,
|
||||
trigger_multiple: 4,
|
||||
next_refund_queue_no: 72
|
||||
}
|
||||
},
|
||||
queueHistory: {
|
||||
list: [
|
||||
{
|
||||
id: 10,
|
||||
order_id: 'HJF202603110001',
|
||||
amount: 3600.00,
|
||||
queue_no: 285,
|
||||
status: 1,
|
||||
refund_time: Date.now() / 1000 - 120,
|
||||
trigger_batch: 71,
|
||||
add_time: Date.now() / 1000 - 86400 * 5,
|
||||
time_key: '2026-03-11'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
order_id: 'HJF202603090010',
|
||||
amount: 3600.00,
|
||||
queue_no: 268,
|
||||
status: 0,
|
||||
refund_time: 0,
|
||||
trigger_batch: 0,
|
||||
add_time: Date.now() / 1000 - 86400 * 2,
|
||||
time_key: '2026-03-09'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
order_id: 'HJF202603080008',
|
||||
amount: 3600.00,
|
||||
queue_no: 244,
|
||||
status: 1,
|
||||
refund_time: Date.now() / 1000 - 86400 * 3,
|
||||
trigger_batch: 61,
|
||||
add_time: Date.now() / 1000 - 86400 * 8,
|
||||
time_key: '2026-03-08'
|
||||
}
|
||||
],
|
||||
count: 4,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
assetsOverview: {
|
||||
now_money: '25200.00',
|
||||
frozen_points: 38500,
|
||||
available_points: 12600,
|
||||
today_release: 15,
|
||||
total_queue_refund: '50400.00',
|
||||
total_points_earned: 51100,
|
||||
member_level: 3,
|
||||
member_level_name: '服务商'
|
||||
},
|
||||
pointsDetail: {
|
||||
list: [
|
||||
{
|
||||
id: 50,
|
||||
title: '直推奖励 - 用户刘五购买报单商品',
|
||||
type: 'reward_direct',
|
||||
points: 1000,
|
||||
pm: 1,
|
||||
status: 'frozen',
|
||||
add_time: '2026-03-11 10:20',
|
||||
order_id: 'HJF202603110025'
|
||||
},
|
||||
{
|
||||
id: 49,
|
||||
title: '伞下奖励 - 用户赵六购买报单商品',
|
||||
type: 'reward_umbrella',
|
||||
points: 200,
|
||||
pm: 1,
|
||||
status: 'frozen',
|
||||
add_time: '2026-03-11 08:15',
|
||||
order_id: 'HJF202603110018'
|
||||
},
|
||||
{
|
||||
id: 48,
|
||||
title: '每日释放 - 待释放积分自动解冻',
|
||||
type: 'release',
|
||||
points: 15,
|
||||
pm: 1,
|
||||
status: 'released',
|
||||
add_time: '2026-03-11 00:00',
|
||||
release_date: '2026-03-11'
|
||||
},
|
||||
{
|
||||
id: 47,
|
||||
title: '直推奖励 - 用户孙七购买报单商品',
|
||||
type: 'reward_direct',
|
||||
points: 1000,
|
||||
pm: 1,
|
||||
status: 'frozen',
|
||||
add_time: '2026-03-10 16:30',
|
||||
order_id: 'HJF202603100045'
|
||||
}
|
||||
],
|
||||
count: 156,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
cashDetail: {
|
||||
list: [
|
||||
{
|
||||
id: 15,
|
||||
title: '公排退款 - 订单HJF202603110001',
|
||||
amount: '3600.00',
|
||||
pm: 1,
|
||||
add_time: '2026-03-11 10:00',
|
||||
order_id: 'HJF202603110001'
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
title: '公排退款 - 订单HJF202603080008',
|
||||
amount: '3600.00',
|
||||
pm: 1,
|
||||
add_time: '2026-03-08 14:00',
|
||||
order_id: 'HJF202603080008'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
title: '提现 - 微信零钱',
|
||||
amount: '9300.00',
|
||||
pm: 0,
|
||||
add_time: '2026-03-07 15:30',
|
||||
remark: '手续费¥700.00'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: '公排退款 - 订单HJF202603050007',
|
||||
amount: '3600.00',
|
||||
pm: 1,
|
||||
add_time: '2026-03-05 12:00',
|
||||
order_id: 'HJF202603050007'
|
||||
}
|
||||
],
|
||||
count: 28,
|
||||
page: 1,
|
||||
limit: 15
|
||||
},
|
||||
withdrawInfo: {
|
||||
now_money: '25200.00',
|
||||
min_extract: 100,
|
||||
fee_rate: 7,
|
||||
extract_bank: ['微信零钱', '支付宝', '银行卡'],
|
||||
bank_list: [
|
||||
{ bank_name: '中国工商银行', bank_code: '1234****5678' }
|
||||
]
|
||||
},
|
||||
memberInfo: {
|
||||
member_level: 3,
|
||||
member_level_name: '服务商',
|
||||
direct_count: 15,
|
||||
umbrella_count: 80,
|
||||
umbrella_orders: 125,
|
||||
next_level_name: '分公司',
|
||||
next_level_require: 1000,
|
||||
progress_percent: 12.5
|
||||
},
|
||||
teamData: {
|
||||
direct_count: 15,
|
||||
umbrella_count: 80,
|
||||
umbrella_orders: 125,
|
||||
members: [
|
||||
{
|
||||
uid: 10091,
|
||||
nickname: '刘五',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 2,
|
||||
member_level_name: '云店',
|
||||
join_time: '2026-02-01',
|
||||
direct_orders: 35,
|
||||
is_direct: true
|
||||
},
|
||||
{
|
||||
uid: 10092,
|
||||
nickname: '赵六',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 2,
|
||||
member_level_name: '云店',
|
||||
join_time: '2026-02-10',
|
||||
direct_orders: 28,
|
||||
is_direct: true
|
||||
},
|
||||
{
|
||||
uid: 10093,
|
||||
nickname: '孙七',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 1,
|
||||
member_level_name: '创客',
|
||||
join_time: '2026-02-20',
|
||||
direct_orders: 12,
|
||||
is_direct: true
|
||||
},
|
||||
{
|
||||
uid: 10094,
|
||||
nickname: '周八',
|
||||
avatar: '/static/images/default_avatar.png',
|
||||
member_level: 0,
|
||||
member_level_name: '普通会员',
|
||||
join_time: '2026-03-05',
|
||||
direct_orders: 2,
|
||||
is_direct: false,
|
||||
parent_nickname: '刘五'
|
||||
}
|
||||
],
|
||||
page: 1,
|
||||
count: 80
|
||||
},
|
||||
teamIncome: {
|
||||
list: [
|
||||
{
|
||||
id: 50,
|
||||
title: '直推奖励',
|
||||
from_uid: 10091,
|
||||
from_nickname: '刘五',
|
||||
order_id: 'HJF202603110025',
|
||||
points: 1000,
|
||||
type: 'direct',
|
||||
add_time: '2026-03-11 10:20'
|
||||
},
|
||||
{
|
||||
id: 49,
|
||||
title: '伞下奖励(级差)',
|
||||
from_uid: 10094,
|
||||
from_nickname: '周八',
|
||||
order_id: 'HJF202603110018',
|
||||
points: 200,
|
||||
type: 'umbrella',
|
||||
add_time: '2026-03-11 08:15'
|
||||
},
|
||||
{
|
||||
id: 48,
|
||||
title: '直推奖励',
|
||||
from_uid: 10092,
|
||||
from_nickname: '赵六',
|
||||
order_id: 'HJF202603100045',
|
||||
points: 1000,
|
||||
type: 'direct',
|
||||
add_time: '2026-03-10 16:30'
|
||||
},
|
||||
{
|
||||
id: 47,
|
||||
title: '伞下奖励(级差)',
|
||||
from_uid: 10095,
|
||||
from_nickname: '吴九',
|
||||
order_id: 'HJF202603100032',
|
||||
points: 200,
|
||||
type: 'umbrella',
|
||||
add_time: '2026-03-10 11:45'
|
||||
}
|
||||
],
|
||||
count: 85,
|
||||
page: 1,
|
||||
limit: 15
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 场景数据映射
|
||||
*/
|
||||
const MOCK_SCENARIO_DATA = {
|
||||
A: SCENARIO_A_DATA,
|
||||
B: SCENARIO_B_DATA,
|
||||
C: SCENARIO_C_DATA
|
||||
};
|
||||
|
||||
/**
|
||||
* 场景感知的 Mock 数据获取函数
|
||||
*/
|
||||
export function getMockQueueStatus() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].queueStatus));
|
||||
}
|
||||
|
||||
export function getMockQueueHistory() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].queueHistory));
|
||||
}
|
||||
|
||||
export function getMockAssetsOverview() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].assetsOverview));
|
||||
}
|
||||
|
||||
export function getMockPointsDetail() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].pointsDetail));
|
||||
}
|
||||
|
||||
export function getMockCashDetail() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].cashDetail));
|
||||
}
|
||||
|
||||
export function getMockWithdrawInfo() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].withdrawInfo));
|
||||
}
|
||||
|
||||
export function getMockMemberInfo() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].memberInfo));
|
||||
}
|
||||
|
||||
export function getMockTeamData() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].teamData));
|
||||
}
|
||||
|
||||
export function getMockTeamIncome() {
|
||||
return JSON.parse(JSON.stringify(MOCK_SCENARIO_DATA[MOCK_SCENARIO].teamIncome));
|
||||
}
|
||||
|
||||
@@ -18,21 +18,32 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
chainWebpack: config => {
|
||||
// 优先使用 HBuilderX 插件内的 babel 插件,若不存在则使用项目 node_modules(便于命令行运行)
|
||||
const path = require('path')
|
||||
const HX_BABEL = '/Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules'
|
||||
const optionalChaining = require.resolve('@babel/plugin-proposal-optional-chaining', { paths: [HX_BABEL] })
|
||||
const nullishCoalescing = require.resolve('@babel/plugin-proposal-nullish-coalescing-operator', { paths: [HX_BABEL] })
|
||||
config.module.rule('js').use('babel-loader').tap(options => {
|
||||
options = options || {}
|
||||
options.plugins = options.plugins || []
|
||||
const pluginPaths = options.plugins.map(p => (Array.isArray(p) ? p[0] : p))
|
||||
if (!pluginPaths.includes(optionalChaining)) {
|
||||
options.plugins.push(optionalChaining)
|
||||
}
|
||||
if (!pluginPaths.includes(nullishCoalescing)) {
|
||||
options.plugins.push(nullishCoalescing)
|
||||
}
|
||||
return options
|
||||
})
|
||||
const projectRoot = path.resolve(__dirname)
|
||||
let optionalChaining, nullishCoalescing
|
||||
try {
|
||||
optionalChaining = require.resolve('@babel/plugin-proposal-optional-chaining', { paths: [HX_BABEL] })
|
||||
nullishCoalescing = require.resolve('@babel/plugin-proposal-nullish-coalescing-operator', { paths: [HX_BABEL] })
|
||||
} catch (e) {
|
||||
optionalChaining = require.resolve('@babel/plugin-proposal-optional-chaining', { paths: [projectRoot] })
|
||||
nullishCoalescing = require.resolve('@babel/plugin-proposal-nullish-coalescing-operator', { paths: [projectRoot] })
|
||||
}
|
||||
if (config.module.rules.get('js')) {
|
||||
config.module.rule('js').use('babel-loader').tap(options => {
|
||||
options = options || {}
|
||||
options.plugins = options.plugins || []
|
||||
const pluginPaths = options.plugins.map(p => (Array.isArray(p) ? p[0] : p))
|
||||
if (!pluginPaths.includes(optionalChaining)) {
|
||||
options.plugins.push(optionalChaining)
|
||||
}
|
||||
if (!pluginPaths.includes(nullishCoalescing)) {
|
||||
options.plugins.push(nullishCoalescing)
|
||||
}
|
||||
return options
|
||||
})
|
||||
}
|
||||
},
|
||||
configureWebpack: config => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
|
||||
Reference in New Issue
Block a user