Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
291be28219 | ||
|
|
d531a3a8eb | ||
|
|
124e77c049 | ||
|
|
f2c8364d87 | ||
|
|
06e8223c27 |
@@ -1,3 +1,4 @@
|
|||||||
unpackage/
|
unpackage/
|
||||||
node_modules/
|
node_modules/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.playwright-mcp/
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
## 项目概览
|
||||||
|
|
||||||
|
怀化人社 H5(hhrs)—— 基于 uni-app 构建的移动端 H5 前端,入口 `pages/`,UI 依赖 `uview-ui`。属于 12301-dscp 项目群中的「怀化人社」H5 端。
|
||||||
|
|
||||||
|
## Git 规范
|
||||||
|
|
||||||
|
- 独立 git 仓库,远程:`https://git.hn12301.net/xuchao/hhrs.git`,主分支 `master`
|
||||||
|
- 提交信息使用中文
|
||||||
|
- `.gitignore` 已忽略 `unpackage/`、`node_modules/`、`.DS_Store`;**不要**提交构建产物(unpackage 目录)
|
||||||
|
|
||||||
|
## 部署信息
|
||||||
|
|
||||||
|
线上为纯静态站点,位于 121.43.239.220(阿里云 ECS / CentOS 7,宿主机 Nginx 直接伺服):
|
||||||
|
|
||||||
|
| 项目 | 内容 |
|
||||||
|
| --- | --- |
|
||||||
|
| 站点 | https://hh.hn12301.net/ |
|
||||||
|
| 网站目录 | `/home/hh/html`(uni-app 构建产物,入口 index.html) |
|
||||||
|
| Nginx 配置 | `/etc/nginx/conf.d/h5-site-https.conf`(80 catch-all → 301 跳 HTTPS) |
|
||||||
|
| 证书 | Let's Encrypt ec-256,acme.sh 每月 1/7/13/19 日 01:01 自动续期,手动续期见运维手册 |
|
||||||
|
|
||||||
|
**发布流程**:本地 HBuilderX 构建 → 覆盖上传产物到 `/home/hh/html/` → 静态文件即时生效,**无需重启 Nginx**。改过 Nginx 配置才需要 `nginx -t && nginx -s reload`。
|
||||||
|
|
||||||
|
**关键注意**:
|
||||||
|
- 前端 API 基地址指向 `https://dscp-dev.hn12301.net`(= 120.26.254.65,数字化测试环境),**后端不在部署机上**。出现"页面能打开但数据不通"时优先排查 120.26.254.65 的接口
|
||||||
|
- 页面图片走 OSS:`aliyunoss.hn12301.net/hhrs/`
|
||||||
|
- SSH 凭据及全部运维细节见 Obsidian 工作资料《121.43.239.220(H5站点服务器)运维手册_2026-08-04.md》
|
||||||
|
- 数据录入(商铺/资源/中奖等)操作指南见仓库根 docs 的 `hhrs-ops-guide.md`(运营向)
|
||||||
@@ -20,12 +20,15 @@ let StoreDetailUrl = 'mp-api/hhrsStore/detail';
|
|||||||
let WinnerDatesUrl = 'mp-api/hhrsWinner/dates';
|
let WinnerDatesUrl = 'mp-api/hhrsWinner/dates';
|
||||||
let WinnerListUrl = 'mp-api/hhrsWinner/list';
|
let WinnerListUrl = 'mp-api/hhrsWinner/list';
|
||||||
let AppointUrl = 'zhy-api/cardBag/appoint';
|
let AppointUrl = 'zhy-api/cardBag/appoint';
|
||||||
|
let TokenDecodeUrl = 'open-api/hhService/tokenDecode';
|
||||||
|
|
||||||
|
|
||||||
// 此处第二个参数vm,就是我们在页面使用的this
|
// 此处第二个参数vm,就是我们在页面使用的this
|
||||||
const install = (Vue, vm) => {
|
const install = (Vue, vm) => {
|
||||||
let checkerLogin = (params = {}) => vm.$u.post(LoginUrl, params);
|
let checkerLogin = (params = {}) => vm.$u.post(LoginUrl, params);
|
||||||
let sendLoginVerifyCode = (params = {}) => vm.$u.post(SendVerifyCodeUrl, params);
|
let sendLoginVerifyCode = (params = {}) => vm.$u.post(SendVerifyCodeUrl, params);
|
||||||
|
// xuchao: 云闪付入口 token 解码
|
||||||
|
let tokenDecode = (params = {}) => vm.$u.post(TokenDecodeUrl, params);
|
||||||
|
|
||||||
// 文旅场馆
|
// 文旅场馆
|
||||||
let getVenueList = (params = {}) => vm.$u.get(VenueListUrl, params);
|
let getVenueList = (params = {}) => vm.$u.get(VenueListUrl, params);
|
||||||
@@ -57,6 +60,7 @@ const install = (Vue, vm) => {
|
|||||||
vm.$u.api = {
|
vm.$u.api = {
|
||||||
checkerLogin,
|
checkerLogin,
|
||||||
sendLoginVerifyCode,
|
sendLoginVerifyCode,
|
||||||
|
tokenDecode,
|
||||||
getVenueList,
|
getVenueList,
|
||||||
getVenueDetail,
|
getVenueDetail,
|
||||||
getMerchantList,
|
getMerchantList,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// 同时,我们也可以在此使用getApp().globalData,如果你把token放在getApp().globalData的话,也是可以使用的
|
// 同时,我们也可以在此使用getApp().globalData,如果你把token放在getApp().globalData的话,也是可以使用的
|
||||||
const install = (Vue, vm) => {
|
const install = (Vue, vm) => {
|
||||||
Vue.prototype.$u.http.setConfig({
|
Vue.prototype.$u.http.setConfig({
|
||||||
baseUrl: 'https://dscp-dev.hn12301.net',
|
baseUrl: 'https://dscp-uat.hn12301.net',
|
||||||
// loadingText: '努力加载中~',
|
// loadingText: '努力加载中~',
|
||||||
// loadingTime: 18800,
|
// loadingTime: 18800,
|
||||||
// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
|
// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
|
||||||
|
|||||||
@@ -104,7 +104,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
|
// xuchao: 直接通过链接进入(无历史栈)时回到首页
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (pages.length > 1) {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
} else {
|
||||||
|
uni.reLaunch({ url: '/pages/index/index' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
goDetail(item) {
|
goDetail(item) {
|
||||||
uni.navigateTo({ url: '/pages/venue/detail?id=' + item.shopId })
|
uni.navigateTo({ url: '/pages/venue/detail?id=' + item.shopId })
|
||||||
|
|||||||
+50
-2
@@ -59,10 +59,56 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad(options) {
|
||||||
|
if (options.token) {
|
||||||
|
this.handleTokenLogin(options.token)
|
||||||
|
}
|
||||||
this.getLocation()
|
this.getLocation()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// xuchao: 云闪付入口 token 自动登录
|
||||||
|
handleTokenLogin(token) {
|
||||||
|
this.$u.api.tokenDecode({ token }).then(res => {
|
||||||
|
if (res.errcode === 0 && res.data && res.data.mobile) {
|
||||||
|
uni.setStorageSync('phone', res.data.mobile)
|
||||||
|
this.$u.api.getEligibility({ phone: res.data.mobile, identityNo: res.data.idNo || '', name: res.data.name || '' }).then(eRes => {
|
||||||
|
if (eRes.errcode === 0) {
|
||||||
|
uni.setStorageSync('isAuth', eRes.data.isAuth)
|
||||||
|
uni.setStorageSync('hasWin', eRes.data.hasWin)
|
||||||
|
uni.setStorageSync('cardNumber', eRes.data.cardNumber)
|
||||||
|
uni.setStorageSync('expiresTime', eRes.data.expiresTime)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// xuchao: 清除地址栏 token,防止分享泄露
|
||||||
|
this.clearUrlToken()
|
||||||
|
} else {
|
||||||
|
this.degradeToPhoneLogin()
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.degradeToPhoneLogin()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// xuchao: 解码失败兜底,提示并跳手机号登录
|
||||||
|
degradeToPhoneLogin() {
|
||||||
|
uni.showToast({ title: '登录凭证已失效,请使用手机号登录', icon: 'none' })
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({ url: '/pages/index/login' })
|
||||||
|
}, 800)
|
||||||
|
},
|
||||||
|
// xuchao: 从 URL hash 中移除 token 参数
|
||||||
|
clearUrlToken() {
|
||||||
|
try {
|
||||||
|
const hash = window.location.hash || ''
|
||||||
|
const qIndex = hash.indexOf('?')
|
||||||
|
if (qIndex === -1) return
|
||||||
|
const params = new URLSearchParams(hash.substring(qIndex + 1))
|
||||||
|
if (!params.has('token')) return
|
||||||
|
params.delete('token')
|
||||||
|
const rest = params.toString()
|
||||||
|
const base = hash.substring(0, qIndex)
|
||||||
|
window.history.replaceState(null, '', rest ? `${base}?${rest}` : base)
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
getLocation() {
|
getLocation() {
|
||||||
import('@/common/location.js').then(module => {
|
import('@/common/location.js').then(module => {
|
||||||
module.default.getLocation().then(({ lng, lat }) => {
|
module.default.getLocation().then(({ lng, lat }) => {
|
||||||
@@ -83,8 +129,10 @@
|
|||||||
}
|
}
|
||||||
const isAuth = uni.getStorageSync('isAuth')
|
const isAuth = uni.getStorageSync('isAuth')
|
||||||
const hasWin = uni.getStorageSync('hasWin')
|
const hasWin = uni.getStorageSync('hasWin')
|
||||||
|
// xuchao: 最近发起过认证流程(含取消)时忽略旧认证缓存,一律回认证页
|
||||||
|
const authFlowStarted = uni.getStorageSync('authFlowStarted')
|
||||||
let status = 'auth'
|
let status = 'auth'
|
||||||
if (isAuth) {
|
if (isAuth && !authFlowStarted) {
|
||||||
status = hasWin ? 'info' : 'noWin'
|
status = hasWin ? 'info' : 'noWin'
|
||||||
}
|
}
|
||||||
uni.navigateTo({ url: `/pages/rights/rights?status=${status}` })
|
uni.navigateTo({ url: `/pages/rights/rights?status=${status}` })
|
||||||
|
|||||||
+56
-2
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page" :class="{ 'page-auth': status === 'auth' }">
|
<view class="page" :class="{ 'page-auth': status === 'auth' }">
|
||||||
<!-- 返回按钮(顶部,非auth状态) -->
|
<!-- 返回按钮(顶部,非auth状态) -->
|
||||||
<view class="back-btn" @click="goBack" v-if="status !== 'auth'">
|
<view class="back-btn" :class="{ 'back-btn--black': status === 'noWin' }" @click="goBack" v-if="status !== 'auth'">
|
||||||
<u-icon name="arrow-left" color="#ffffff" size="40"></u-icon>
|
<u-icon name="arrow-left" :color="status === 'noWin' ? '#000000' : '#ffffff'" size="40"></u-icon>
|
||||||
<text class="back-text">返回</text>
|
<text class="back-text">返回</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 返回按钮(底部,auth状态) -->
|
<!-- 返回按钮(底部,auth状态) -->
|
||||||
@@ -212,11 +212,46 @@
|
|||||||
this.lat = loc.lat
|
this.lat = loc.lat
|
||||||
}
|
}
|
||||||
this.checkAuthResult()
|
this.checkAuthResult()
|
||||||
|
// xuchao: 本地无认证缓存时自动查询后端恢复已认证状态
|
||||||
|
this.autoRecoverStatus()
|
||||||
if (this.status === 'info') {
|
if (this.status === 'info') {
|
||||||
this.loadRightsList()
|
this.loadRightsList()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// xuchao: 已登录但本地无认证缓存时,仅传手机号查询资格以恢复状态(不带证件号,避免触发被动认证)
|
||||||
|
autoRecoverStatus() {
|
||||||
|
if (this.status !== 'auth') return
|
||||||
|
// xuchao: 用户主动发起过认证流程时,取消后应停留在认证页,不自动恢复
|
||||||
|
if (uni.getStorageSync('authFlowStarted')) return
|
||||||
|
const phone = uni.getStorageSync('phone')
|
||||||
|
if (!phone) return
|
||||||
|
this.$u.api.getEligibility({
|
||||||
|
phone: phone
|
||||||
|
}).then(res => {
|
||||||
|
if (res.errcode !== 0 || !res.data) return
|
||||||
|
uni.setStorageSync('isAuth', res.data.isAuth)
|
||||||
|
uni.setStorageSync('hasWin', res.data.hasWin)
|
||||||
|
uni.setStorageSync('cardNumber', res.data.cardNumber)
|
||||||
|
uni.setStorageSync('expiresTime', res.data.expiresTime)
|
||||||
|
// xuchao: 自动恢复成功,清除流程标记
|
||||||
|
uni.removeStorageSync('authFlowStarted')
|
||||||
|
if (res.data.isAuth) {
|
||||||
|
this.status = res.data.hasWin ? 'info' : 'noWin'
|
||||||
|
if (this.status === 'info') {
|
||||||
|
this.loadRightsList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// xuchao: 认证取消/未通过:清理旧认证缓存并标记流程已发起,取消后应停留认证页
|
||||||
|
markAuthCanceled() {
|
||||||
|
uni.removeStorageSync('isAuth')
|
||||||
|
uni.removeStorageSync('hasWin')
|
||||||
|
uni.removeStorageSync('cardNumber')
|
||||||
|
uni.removeStorageSync('expiresTime')
|
||||||
|
uni.setStorageSync('authFlowStarted', true)
|
||||||
|
},
|
||||||
checkAuthResult() {
|
checkAuthResult() {
|
||||||
const url = new URL(window.location.href)
|
const url = new URL(window.location.href)
|
||||||
let responseStr = url.searchParams.get('response')
|
let responseStr = url.searchParams.get('response')
|
||||||
@@ -261,6 +296,8 @@
|
|||||||
uni.setStorageSync('hasWin', eRes.data.hasWin)
|
uni.setStorageSync('hasWin', eRes.data.hasWin)
|
||||||
uni.setStorageSync('cardNumber', eRes.data.cardNumber)
|
uni.setStorageSync('cardNumber', eRes.data.cardNumber)
|
||||||
uni.setStorageSync('expiresTime', eRes.data.expiresTime)
|
uni.setStorageSync('expiresTime', eRes.data.expiresTime)
|
||||||
|
// xuchao: 认证成功,清除流程标记
|
||||||
|
uni.removeStorageSync('authFlowStarted')
|
||||||
if (eRes.data.isAuth) {
|
if (eRes.data.isAuth) {
|
||||||
this.status = eRes.data.hasWin ? 'info' : 'noWin'
|
this.status = eRes.data.hasWin ? 'info' : 'noWin'
|
||||||
if (this.status === 'info') {
|
if (this.status === 'info') {
|
||||||
@@ -270,6 +307,8 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
// xuchao: 实名未通过视为取消认证,清理缓存并标记
|
||||||
|
this.markAuthCanceled()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '认证未通过',
|
title: '认证未通过',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
@@ -288,6 +327,8 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
// xuchao: 未返回认证回调视为取消认证,清理缓存并标记
|
||||||
|
this.markAuthCanceled()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '认证未通过',
|
title: '认证未通过',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
@@ -324,6 +365,8 @@
|
|||||||
}
|
}
|
||||||
uni.setStorageSync('authName', this.form.realName)
|
uni.setStorageSync('authName', this.form.realName)
|
||||||
uni.setStorageSync('authIdNo', this.form.idNumber)
|
uni.setStorageSync('authIdNo', this.form.idNumber)
|
||||||
|
// xuchao: 标记已发起认证流程,取消后停留在认证页,不自动恢复
|
||||||
|
uni.setStorageSync('authFlowStarted', true)
|
||||||
this.loadAliyunSDK().then(() => {
|
this.loadAliyunSDK().then(() => {
|
||||||
const metaInfo = JSON.stringify(window.getMetaInfo())
|
const metaInfo = JSON.stringify(window.getMetaInfo())
|
||||||
const returnUrl = window.location.origin + window.location.pathname +
|
const returnUrl = window.location.origin + window.location.pathname +
|
||||||
@@ -467,7 +510,13 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
|
// xuchao: 直接通过链接进入(无历史栈)时回到首页
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (pages.length > 1) {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
} else {
|
||||||
|
uni.reLaunch({ url: '/pages/index/index' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -901,6 +950,11 @@
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* xuchao: noWin 状态(无中奖空态页)返回按钮改黑色 */
|
||||||
|
.back-btn--black .back-text {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
/* 底部返回按钮 */
|
/* 底部返回按钮 */
|
||||||
.back-btn-bottom {
|
.back-btn-bottom {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@@ -104,7 +104,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
|
// xuchao: 直接通过链接进入(无历史栈)时回到首页
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (pages.length > 1) {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
} else {
|
||||||
|
uni.reLaunch({ url: '/pages/index/index' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
goDetail(item) {
|
goDetail(item) {
|
||||||
uni.navigateTo({ url: '/pages/venue/detail?id=' + item.shopId })
|
uni.navigateTo({ url: '/pages/venue/detail?id=' + item.shopId })
|
||||||
|
|||||||
+68
-24
@@ -15,7 +15,7 @@
|
|||||||
<view class="stats-card">
|
<view class="stats-card">
|
||||||
<image class="stats-gift" src="https://zhihuiyou.oss-cn-shenzhen.aliyuncs.com/hhrs/hhrs_xyckr.png" mode="aspectFit"></image>
|
<image class="stats-gift" src="https://zhihuiyou.oss-cn-shenzhen.aliyuncs.com/hhrs/hhrs_xyckr.png" mode="aspectFit"></image>
|
||||||
<text class="stats-label">已产生</text>
|
<text class="stats-label">已产生</text>
|
||||||
<text class="stats-number">7,000</text>
|
<text class="stats-number">{{ totalWinners.toLocaleString() }}</text>
|
||||||
<text class="stats-label">名幸运持卡人</text>
|
<text class="stats-label">名幸运持卡人</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
|
|
||||||
<!-- 中奖者表格 -->
|
<!-- 中奖者表格 -->
|
||||||
<view class="table-area">
|
<view class="table-area">
|
||||||
|
<template v-if="currentList.length > 0">
|
||||||
<view class="table-header">
|
<view class="table-header">
|
||||||
<text class="table-col col-name">姓名</text>
|
<text class="table-col col-name">姓名</text>
|
||||||
<text class="table-col col-idcard">身份证</text>
|
<text class="table-col col-idcard">身份证</text>
|
||||||
@@ -56,9 +57,16 @@
|
|||||||
<text class="table-col col-idcard">{{ item.memberIdentityNo }}</text>
|
<text class="table-col col-idcard">{{ item.memberIdentityNo }}</text>
|
||||||
<text class="table-col col-phone">{{ item.memberPhone }}</text>
|
<text class="table-col col-phone">{{ item.memberPhone }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="loadmore-area">
|
<!-- xuchao: 名单不足一页时隐藏加载更多 -->
|
||||||
|
<view class="loadmore-area" v-if="(totals[currentTab] || 0) > pageSize">
|
||||||
<u-loadmore :status="loadStatus" :loading-text="'加载中...'" :loadmore-text="'上拉加载更多'" :nomore-text="'没有更多了'" />
|
<u-loadmore :status="loadStatus" :loading-text="'加载中...'" :loadmore-text="'上拉加载更多'" :nomore-text="'没有更多了'" />
|
||||||
</view>
|
</view>
|
||||||
|
</template>
|
||||||
|
<!-- xuchao: 无名单时展示空态 -->
|
||||||
|
<view class="empty-area" v-else>
|
||||||
|
<u-icon name="empty" size="60" color="#c8c8c8"></u-icon>
|
||||||
|
<text class="empty-text">暂无数据</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -72,9 +80,10 @@ export default {
|
|||||||
winnerData: {},
|
winnerData: {},
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
total: 0,
|
totals: {},
|
||||||
loading: false,
|
totalWinners: 0,
|
||||||
loadStatus: 'loadmore',
|
inFlight: 0,
|
||||||
|
loadStatusMap: {},
|
||||||
keyword: ''
|
keyword: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -88,6 +97,10 @@ export default {
|
|||||||
(item.memberName && item.memberName.toLowerCase().includes(kw)) ||
|
(item.memberName && item.memberName.toLowerCase().includes(kw)) ||
|
||||||
(item.memberPhone && item.memberPhone.includes(kw))
|
(item.memberPhone && item.memberPhone.includes(kw))
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
// xuchao: 各期独立的加载状态,避免并行请求互相覆盖
|
||||||
|
loadStatus() {
|
||||||
|
return this.loadStatusMap[this.currentTab] || 'nomore'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
@@ -110,45 +123,55 @@ export default {
|
|||||||
data[index] = []
|
data[index] = []
|
||||||
})
|
})
|
||||||
this.winnerData = data
|
this.winnerData = data
|
||||||
|
// xuchao: 预置各期 key,保证 totals/loadStatusMap 的响应式
|
||||||
|
this.totals = {}
|
||||||
|
this.loadStatusMap = {}
|
||||||
|
dates.forEach((date, index) => {
|
||||||
|
this.$set(this.totals, index, 0)
|
||||||
|
this.$set(this.loadStatusMap, index, 'loading')
|
||||||
|
})
|
||||||
|
// xuchao: 预取各期第一页,汇总已产生总人数
|
||||||
if (this.tabs.length > 0) {
|
if (this.tabs.length > 0) {
|
||||||
this.loadWinnerList(0)
|
this.tabs.forEach((tab, index) => {
|
||||||
|
this.loadWinnerList(index, 1, true)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadWinnerList(tabIndex) {
|
loadWinnerList(tabIndex, pageNo, allowConcurrent) {
|
||||||
if (!this.tabs[tabIndex]) return
|
if (!this.tabs[tabIndex]) return
|
||||||
if (this.loading) return
|
if (this.inFlight > 0 && !allowConcurrent) return
|
||||||
this.loading = true
|
this.inFlight++
|
||||||
this.loadStatus = 'loading'
|
this.$set(this.loadStatusMap, tabIndex, 'loading')
|
||||||
const drawDate = this.tabs[tabIndex].date
|
const drawDate = this.tabs[tabIndex].date
|
||||||
this.$u.api.getWinnerList({
|
this.$u.api.getWinnerList({
|
||||||
pageNo: this.pageNo,
|
pageNo: pageNo || this.pageNo,
|
||||||
pageSize: this.pageSize,
|
pageSize: this.pageSize,
|
||||||
drawDate: drawDate
|
drawDate: drawDate
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res.errcode === 0 && res.data) {
|
if (res.errcode === 0 && res.data) {
|
||||||
const list = res.data.list || []
|
const list = res.data.list || []
|
||||||
if (this.pageNo === 1) {
|
const page = pageNo || this.pageNo
|
||||||
|
if (page === 1) {
|
||||||
this.$set(this.winnerData, tabIndex, list)
|
this.$set(this.winnerData, tabIndex, list)
|
||||||
} else {
|
} else {
|
||||||
this.winnerData[tabIndex] = this.winnerData[tabIndex].concat(list)
|
this.winnerData[tabIndex] = this.winnerData[tabIndex].concat(list)
|
||||||
}
|
}
|
||||||
this.total = res.data.total || 0
|
this.$set(this.totals, tabIndex, res.data.total || 0)
|
||||||
|
// xuchao: 各期总人数求和,用于顶部幸运持卡人数
|
||||||
|
this.totalWinners = Object.values(this.totals).reduce((sum, n) => sum + n, 0)
|
||||||
}
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false
|
this.inFlight--
|
||||||
if (this.winnerData[tabIndex].length === 0) {
|
const len = this.winnerData[tabIndex].length
|
||||||
this.loadStatus = 'nomore'
|
const total = this.totals[tabIndex] || 0
|
||||||
} else if (this.winnerData[tabIndex].length >= this.total) {
|
this.$set(this.loadStatusMap, tabIndex, len === 0 || len >= total ? 'nomore' : 'loadmore')
|
||||||
this.loadStatus = 'nomore'
|
|
||||||
} else {
|
|
||||||
this.loadStatus = 'loadmore'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadMore() {
|
loadMore() {
|
||||||
if (this.winnerData[this.currentTab].length < this.total && !this.loading) {
|
const total = this.totals[this.currentTab] || 0
|
||||||
|
if (this.winnerData[this.currentTab].length < total && this.inFlight === 0) {
|
||||||
this.pageNo++
|
this.pageNo++
|
||||||
this.loadWinnerList(this.currentTab)
|
this.loadWinnerList(this.currentTab)
|
||||||
}
|
}
|
||||||
@@ -157,13 +180,20 @@ export default {
|
|||||||
this.currentTab = index
|
this.currentTab = index
|
||||||
this.pageNo = 1
|
this.pageNo = 1
|
||||||
if (this.winnerData[index].length === 0) {
|
if (this.winnerData[index].length === 0) {
|
||||||
this.loadWinnerList(index)
|
this.loadWinnerList(index, 1)
|
||||||
} else {
|
} else {
|
||||||
this.loadStatus = this.winnerData[index].length >= this.total ? 'nomore' : 'loadmore'
|
const total = this.totals[index] || 0
|
||||||
|
this.$set(this.loadStatusMap, index, this.winnerData[index].length >= total ? 'nomore' : 'loadmore')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
goBack() {
|
goBack() {
|
||||||
|
// xuchao: 直接通过链接进入(无历史栈)时回到首页
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (pages.length > 1) {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
|
} else {
|
||||||
|
uni.reLaunch({ url: '/pages/index/index' })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onSearch() {
|
onSearch() {
|
||||||
// 前端过滤,无需重新请求
|
// 前端过滤,无需重新请求
|
||||||
@@ -408,4 +438,18 @@ export default {
|
|||||||
padding: 24px 0;
|
padding: 24px 0;
|
||||||
color: #999999;
|
color: #999999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 空态 */
|
||||||
|
.empty-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 80px 0;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user