Explorar o código

Merge branch 'master' of http://39.101.143.165:8090/hxl13994548489/LocalLivingServicesUniapp

caiyuqin hai 1 mes
pai
achega
7bd244f56b

+ 184 - 0
api/memberPrepaid.js

@@ -0,0 +1,184 @@
+import { http } from '@/common/service/service.js'
+import { USER_INFO } from '@/common/util/constants.js'
+
+const BASE = '/memberPrepaidMerchant'
+
+/** 当前登录门店 ID */
+export function getMerchantId() {
+	const info = uni.getStorageSync(USER_INFO) || {}
+	return info.merchantId || ''
+}
+
+/** 获取本商户经纬度(共享门店列表按门店坐标算距离) */
+export function getMerchantLocation(merchantId = getMerchantId()) {
+	if (!merchantId) {
+		return Promise.reject(new Error('未获取到门店信息'))
+	}
+	return http
+		.get('/merchant/localMerchantInfo/queryById', {
+			params: { id: merchantId }
+		})
+		.then((res) => {
+			if (res.data.code !== 200 || !res.data.result) {
+				return Promise.reject(new Error(res.data.message || '获取门店信息失败'))
+			}
+			const { longitude, latitude } = res.data.result
+			if (
+				longitude === undefined ||
+				longitude === null ||
+				longitude === '' ||
+				latitude === undefined ||
+				latitude === null ||
+				latitude === ''
+			) {
+				return Promise.reject(new Error('门店未配置经纬度'))
+			}
+			return {
+				longitude: Number(longitude),
+				latitude: Number(latitude)
+			}
+		})
+}
+
+/** 格式化门店距离展示,如 746.00m / 3.24km */
+export function formatStoreDistance(item = {}) {
+	if (item.distance == null || item.distance === '') {
+		return item.distanceText || ''
+	}
+	const num = Number(item.distance)
+	if (Number.isNaN(num)) {
+		return String(item.distanceText || item.distance)
+	}
+	const text = Number.isInteger(num) ? String(num) : num.toFixed(2)
+	const unit = item.distanceUnit || ''
+	return unit ? `${text}${unit}` : text
+}
+
+/**
+ * auditStatus: 0未申请 / 1待审核 / 2已通过 / 3已驳回
+ * operationStatus: 0关闭 / 1开启
+ * reapplyStatus: 0不适用 / 1禁止重新申请 / 2允许重新申请
+ */
+const api = {
+	/** 查询商户行业是否支持储值(true 时显示储值管理入口) */
+	checkIndustryEligibility(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/industry/eligibility`, { params: { merchantId } })
+	},
+
+	/** 查询储值功能状态 */
+	getFeatureStatus(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/feature/status`, { params: { merchantId } })
+	},
+
+	/** 当前生效协议 */
+	getCurrentAgreement() {
+		return http.get(`${BASE}/agreement/current`)
+	},
+
+	/** 当前或最近申请详情 */
+	getApplication(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/feature/application`, { params: { merchantId } })
+	},
+
+	/** 提交开通申请 */
+	applyFeature(data, merchantId = getMerchantId()) {
+		return http.post(`${BASE}/feature/apply`, data, { params: { merchantId } })
+	},
+
+	/** 开启储值功能 */
+	openFeature(merchantId = getMerchantId()) {
+		return http.post(`${BASE}/feature/open`, {}, { params: { merchantId } })
+	},
+
+	/** 关闭储值功能 */
+	closeFeature(data, merchantId = getMerchantId()) {
+		return http.post(`${BASE}/feature/close`, data || {}, { params: { merchantId } })
+	},
+
+	/** 当前门店详情 */
+	getCurrentStore(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/store/current`, { params: { merchantId } })
+	},
+
+	/**
+	 * 可添加共享门店分页
+	 * 必传 longitude / latitude,按距离由近到远
+	 */
+	getShareableStores(params = {}) {
+		return http.get(`${BASE}/store/shareable/page`, {
+			params: {
+				merchantId: getMerchantId(),
+				pageNo: 1,
+				pageSize: 10,
+				...params
+			}
+		})
+	},
+
+	/**
+	 * 已共享门店分页
+	 * 必传 longitude / latitude,按距离由近到远
+	 */
+	getSharedStores(params = {}) {
+		return http.get(`${BASE}/store/shared/page`, {
+			params: {
+				merchantId: getMerchantId(),
+				pageNo: 1,
+				pageSize: 10,
+				...params
+			}
+		})
+	},
+
+	/** 批量添加共享门店 */
+	addSharedStores(sharedStoreIds, merchantId = getMerchantId()) {
+		return http.post(
+			`${BASE}/store/shared`,
+			{ sharedStoreIds },
+			{ params: { merchantId } }
+		)
+	},
+
+	/** 移除共享门店 */
+	removeSharedStore(sharedStoreId, merchantId = getMerchantId()) {
+		return http.delete(`${BASE}/store/shared/${sharedStoreId}`, {}, { params: { merchantId } })
+	},
+
+	/** 首页概览 */
+	getOverview(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/overview`, { params: { merchantId } })
+	},
+
+	/** 账户明细分页 */
+	getLedgerPage(params = {}) {
+		return http.get(`${BASE}/account/ledger/page`, {
+			params: { merchantId: getMerchantId(), pageNo: 1, pageSize: 10, ...params }
+		})
+	},
+
+	/** 账户明细详情 */
+	getLedgerDetail(ledgerId, merchantId = getMerchantId()) {
+		return http.get(`${BASE}/account/ledger/${ledgerId}/detail`, {
+			params: { merchantId }
+		})
+	},
+
+	/** 查询储值设置 */
+	getRechargeConfig(merchantId = getMerchantId()) {
+		return http.get(`${BASE}/recharge/config`, { params: { merchantId } })
+	},
+
+	/** 保存储值设置 */
+	saveRechargeConfig(data, merchantId = getMerchantId()) {
+		return http.put(`${BASE}/recharge/config`, data, { params: { merchantId } })
+	},
+
+	/** 可赠送优惠券分页 */
+	getCouponPage(params = {}) {
+		return http.get(`${BASE}/recharge/coupon/page`, {
+			params: { merchantId: getMerchantId(), pageNo: 1, pageSize: 50, ...params }
+		})
+	}
+}
+
+export default api

+ 64 - 0
common/router/modules/routes.js

@@ -797,5 +797,69 @@ const routes = [{
 			title: '提现详情',
 		},
 	},
+	// 储值管理
+	{
+		path: '/pages/topUp/index',
+		name: 'topUpIndex',
+		meta: {
+			title: '储值管理',
+		},
+	},
+	{
+		path: '/pages/topUp/agreement',
+		name: 'topUpAgreement',
+		meta: {
+			title: '储值管理',
+		},
+	},
+	{
+		path: '/pages/topUp/status',
+		name: 'topUpStatus',
+		meta: {
+			title: '储值管理',
+		},
+	},
+	{
+		path: '/pages/topUp/manage',
+		name: 'topUpManage',
+		meta: {
+			title: '储值管理',
+		},
+	},
+	{
+		path: '/pages/topUp/settings',
+		name: 'topUpSettings',
+		meta: {
+			title: '储值设置',
+		},
+	},
+	{
+		path: '/pages/topUp/shareStore',
+		name: 'topUpShareStore',
+		meta: {
+			title: '共享门店',
+		},
+	},
+	{
+		path: '/pages/topUp/shareStoreAdd',
+		name: 'topUpShareStoreAdd',
+		meta: {
+			title: '添加共享门店',
+		},
+	},
+	{
+		path: '/pages/topUp/records',
+		name: 'topUpRecords',
+		meta: {
+			title: '账户明细',
+		},
+	},
+	{
+		path: '/pages/topUp/recordDetail',
+		name: 'topUpRecordDetail',
+		meta: {
+			title: '明细详情',
+		},
+	},
 ]
 export default routes

+ 28 - 15
components/index_nav.vue

@@ -24,10 +24,17 @@
 		components: {
 			Modal,
 		},
+		props: {
+			/** 是否展示储值管理入口(由行业准入接口控制) */
+			showTopUp: {
+				type: Boolean,
+				default: false
+			}
+		},
 		data() {
 			return {
 				dotStyle: true,
-				swiperList: [{
+				baseList: [{
 						name: '店铺设置',
 						url: '/static/index/shop.png',
 						path: '/pages/store/settings'
@@ -52,11 +59,6 @@
 						path:'/pages/order/index',
 						type:'tabbar'
 					},
-					// {
-					// 	name: '审核查询',
-					// 	url: '/static/index/audit.png',
-					// 	path: '/pages/store/examine'
-					// },
 					{
 						name: '提现管理',
 						url: '/static/index/withdraw.png',
@@ -78,11 +80,18 @@
 						url: '/static/index/ad.png',
 						path: '/pages/adManagement/adlist'
 					},
+					{
+						name: '储值管理',
+						url: '/static/index/toup.png',
+						path: '/pages/topUp/index',
+						needTopUp: true
+					},
 					// {
-					// 	name: '秒杀活动',
-					// 	url: '/static/index/seckill.png'
+					// 	name: '门店管理',
+					// 	url: '/static/index/all.png',
+					// 	path: '/pages/storeManage/index',
+					// 	needTopUp: true
 					// },
-					
 					{
 						name: '全部服务',
 						url: '/static/index/all.png',
@@ -93,11 +102,15 @@
 				modalText: {
 					title: '提示',
 					btntext: '好的',
-					// 很抱歉,您没有通过平台审核,如有疑问请联系平台客服。
 					content: '报名成功,请等待平台审核'
 				},
 			}
 		},
+		computed: {
+			swiperList() {
+				return this.baseList.filter((item) => !item.needTopUp || this.showTopUp);
+			}
+		},
 		methods: {
 			async jump(item) {
 				// 需要先报名
@@ -176,7 +189,7 @@
 		height: 100%;
 		display: flex;
 		flex-wrap: wrap;
-		justify-content: space-between;
+		// justify-content: space-between;
 		column-gap: 30rpx;
 		
 	}
@@ -185,7 +198,7 @@
 		width: 100rpx;
 		display: flex;
 		flex-direction: column;
-		
+		margin-bottom: 20rpx;
 		justify-content: space-between;
 		align-items: center;
 		font-size: 24rpx;
@@ -196,9 +209,9 @@
 		}
 	}
 
-	.card:nth-child(-n+5) {
-		margin-bottom: 40rpx;
-	}
+	// .card:nth-child(-n+5) {
+	// 	margin-bottom: 40rpx;
+	// }
 
 	::v-deep .uni-swiper-wrapper {
 		-webkit-transform: translate3d(0px, 0, 0);

+ 56 - 0
pages.json

@@ -561,6 +561,62 @@
 			"style": {
 				"navigationBarTitleText": "广告详情"
 			}
+		},
+
+		// 储值管理---------------
+		{
+			"path": "pages/topUp/index",
+			"style": {
+				"navigationBarTitleText": "储值管理"
+			}
+		},
+		{
+			"path": "pages/topUp/agreement",
+			"style": {
+				"navigationBarTitleText": "储值管理"
+			}
+		},
+		{
+			"path": "pages/topUp/status",
+			"style": {
+				"navigationBarTitleText": "储值管理"
+			}
+		},
+		{
+			"path": "pages/topUp/manage",
+			"style": {
+				"navigationBarTitleText": "储值管理"
+			}
+		},
+		{
+			"path": "pages/topUp/settings",
+			"style": {
+				"navigationBarTitleText": "储值设置"
+			}
+		},
+		{
+			"path": "pages/topUp/shareStore",
+			"style": {
+				"navigationBarTitleText": "共享门店"
+			}
+		},
+		{
+			"path": "pages/topUp/shareStoreAdd",
+			"style": {
+				"navigationBarTitleText": "添加共享门店"
+			}
+		},
+		{
+			"path": "pages/topUp/records",
+			"style": {
+				"navigationBarTitleText": "账户明细"
+			}
+		},
+		{
+			"path": "pages/topUp/recordDetail",
+			"style": {
+				"navigationBarTitleText": "明细详情"
+			}
 		}
 	],
 	"globalStyle": {

+ 21 - 4
pages/index/index.vue

@@ -67,7 +67,7 @@
 				</view>
 			</view>
 			<view class="other">
-				<indexNav />
+				<indexNav :showTopUp="showTopUp" />
 			</view>
 		</view>
 		<!-- 完善信息弹窗,storeStatus == 3 (店铺已经审核通过 ) -->
@@ -106,6 +106,7 @@
 <script>
 	import indexNav from "@/components/index_nav.vue";
 	import checkPopup from "@/components/check-popup.vue";
+	import prepaidApi from "@/api/memberPrepaid.js";
 	export default {
 		components: {
 			indexNav,
@@ -125,7 +126,8 @@
 				describe: "", //描述
 				isPopup: false, //弹窗是否出现
 				referenceId: "", //邀请码
-				logo: ''
+				logo: '',
+				showTopUp: false // 行业是否支持储值
 			};
 		},
 		onLoad: function() {
@@ -214,6 +216,21 @@
 						}
 					});
 			},
+			/** 查询行业是否支持储值,控制首页储值管理入口 */
+			checkTopUpEligibility() {
+				if (!this.merchantId) {
+					this.showTopUp = false;
+					return;
+				}
+				prepaidApi
+					.checkIndustryEligibility(this.merchantId)
+					.then((res) => {
+						this.showTopUp = res.data.code === 200 && !!res.data.result;
+					})
+					.catch(() => {
+						this.showTopUp = false;
+					});
+			},
 		},
 		mounted() {
 			// let info =
@@ -233,7 +250,7 @@
 					that.getMessage();
 					that.getNum();
 					that.getStroeStatus();
-
+					that.checkTopUpEligibility();
 				},
 			});
 		},
@@ -475,7 +492,7 @@
 			}
 			.other{
 				width: 100%;
-				height: 304rpx;
+				// height: 304rpx;
 				padding: 32rpx ;
 				background: #FFFFFF;
 				border-radius: 16rpx;

+ 283 - 0
pages/topUp/agreement.vue

@@ -0,0 +1,283 @@
+<template>
+	<view class="page">
+		<u-navbar title="储值管理" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<scroll-view scroll-y class="content" :style="{ height: scrollHeight }">
+			<view class="card">
+				<!-- 接口正文 -->
+				<template v-if="contentText">
+					<rich-text :nodes="contentText"></rich-text>
+				</template>
+				<!-- 无接口正文时展示默认规则 -->
+				<template v-else>
+					<view class="doc-title">{{ agreementTitle }}</view>
+					<view class="section">
+						<view class="h">一、协议主体</view>
+						<view class="p">本协议由平台运营方(以下简称 “平台”)与入驻商户(以下简称 “商户”)共同订立。商户点击勾选 “我已阅读并同意《储值功能开通业务规则》、点击【申请开通】按钮,即代表商户已完整阅读、充分理解并自愿接受本协议全部条款,正式与平台达成有效合约。</view>
+					</view>
+					<view class="section">
+						<view class="h">二、功能说明</view>
+						<view class="p">储值功能,指平台为商户提供面向 C 端消费者的预付储值充值、余额消费核销、充值赠送权益配置、跨门店共享储值余额等相关技术服务;</view>
+						<view class="p">商户可自主配置储值充值档位、自定义充值门槛、充值赠送余额、赠送优惠券、共享适用门店等营销规则;所有配置内容由商户独立承担对应的履约责任。</view>
+					</view>
+					<view class="section">
+						<view class="h">三、商户权利与义务</view>
+						<view class="p">商户承诺具备合法经营资质,拥有开展预付储值经营活动所需全部资质,符合当地市场监管部门关于单用途预付卡、预付费经营相关法律法规要求,独立承担因开展储值业务产生全部法律责任。</view>
+						<view class="p">商户设置的充值金额、赠送余额、优惠券、使用期限、使用范围、退改规则等全部活动内容,应当清晰、真实、无虚假宣传,不得设置侵害消费者合法权益的条款。</view>
+						<view class="p">储值本金履约责任:消费者充值资金对应的履约义务由商户自行承担。商户需保障消费者储值余额可按照约定正常到店消费核销;如出现闭店、停业、转让、经营异常等情形,商户负责妥善处理消费者储值余额退款、履约事宜,平台仅提供技术展示与核销工具,不承担商户履约担保责任。</view>
+						<view class="p">商户配置【共享门店】功能时,确认知晓:储值本金支持跨已添加共享门店消费;充值赠送优惠券仅限原充值门店使用,无法跨店通用。商户应当提前向消费者清晰告知该规则。</view>
+					</view>
+					<view class="section">
+						<view class="h">四、平台权利与义务</view>
+						<view class="p">平台仅提供软件技术服务,搭建线上储值展示、充值、核销、订单记录查询系统,不代收、不截留商户储值资金,不介入商户与消费者之间的实体商品 / 服务履约关系。</view>
+						<view class="p">平台有权对商户储值活动内容进行合规巡检;若商户存在违规宣传、侵害消费者权益、违反监管政策行为,平台有权限制、暂停、关闭商户储值功能。</view>
+						<view class="p">平台负责保障系统正常运行,因系统维护、网络、不可抗力导致临时服务中断,将尽可能提前通知商户。</view>
+					</view>
+					<view class="section">
+						<view class="h">五、资金、结算相关约定</view>
+						<view class="p">商户自行负责储值资金收取与资金管理;平台仅提供交易流水记录,不对商户储值资金安全承担保管、赔付责任。</view>
+						<view class="p">储值账户流水、充值记录、消费明细、赠送记录以平台系统后台记录作为双方对账有效凭证。</view>
+					</view>
+					<view class="section">
+						<view class="h">六、功能启停规则</view>
+						<view class="p">商户提交开通申请后,由平台进行审核;审核通过后方可启用储值功能;审核不通过,平台可无需说明理由驳回申请。</view>
+						<view class="p">商户可在后台自主关闭储值功能;功能关闭后,新用户无法发起储值充值;已拥有储值余额的老用户仍然可以正常消费核销余额。</view>
+						<view class="p">商户存在违规经营、大量客诉、违反法律法规情形,平台可单方面暂停或永久关闭储值功能,并保留追究商户相关责任的权利。</view>
+					</view>
+					<view class="section">
+						<view class="h">七、风险告知(重点提示)</view>
+						<view class="p warn">预付费储值业务受到市场监督管理部门严格监管,商户务必熟知并遵守本地预付费消费相关管理条例,违规经营将面临行政处罚。</view>
+						<view class="p warn">若商户出现停业、倒闭、失联等情况,消费者储值余额无法正常消费产生的全部纠纷、赔偿责任由商户独立承担;消费者有权向监管部门投诉、提起诉讼。</view>
+						<view class="p warn">商户不得利用储值功能开展非法集资、变相融资等违法活动,一经发现平台立即关停功能,并移交相关部门处理。</view>
+					</view>
+					<view class="section">
+						<view class="h">八、其他约定</view>
+						<view class="p">本协议自商户提交开通申请之日生效;若商户关闭储值功能,不影响功能存续期间已产生交易对应的责任条款持续有效。</view>
+						<view class="p">平台有权根据业务及监管要求修订本协议,修订后将在商户端进行公示,商户持续使用储值功能即视为接受更新后的协议。</view>
+						<view class="p">因本协议产生争议,双方优先友好协商;协商不成,提交平台主体所在地人民法院诉讼解决。</view>
+					</view>
+				</template>
+			</view>
+		</scroll-view>
+
+		<view class="footer">
+			<view class="agree" @click="agreed = !agreed">
+				<view class="checkbox" :class="{ checked: agreed }">
+					<view class="checkbox-false" v-if="!agreed">
+						<!-- 111 -->
+					</view>
+					<!-- <u-icon v-if="!agreed" name="checkmark-circle" color="#368DFA" size="35"></u-icon> -->
+					<u-icon v-if="agreed" name="checkmark-circle-fill" color="#368DFA" size="35"></u-icon>
+				</view>
+				<text>我已阅读并同意《{{ agreementTitle }}》</text>
+			</view>
+			<view class="btn" :class="{ disabled: !agreed || submitting }" @click="onApply">
+				{{ submitting ? '提交中...' : '申请开通' }}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantId } from '@/api/memberPrepaid.js'
+
+	export default {
+		data() {
+			return {
+				agreed: false,
+				scrollHeight: '70vh',
+				submitting: false,
+				agreementVersion: '',
+				agreementTitle: '储值功能开通业务规则',
+				contentText: ''
+			};
+		},
+		onLoad() {
+			this.loadAgreement();
+			this.checkStatus();
+		},
+		onReady() {
+			const sys = uni.getSystemInfoSync();
+			this.scrollHeight = `${sys.windowHeight - uni.upx2px(280) - (sys.statusBarHeight || 0) - 44}px`;
+		},
+		methods: {
+			checkStatus() {
+				const merchantId = getMerchantId();
+				if (!merchantId) return;
+				prepaidApi.getFeatureStatus(merchantId).then((res) => {
+					if (res.data.code !== 200) return;
+					const auditStatus = Number((res.data.result || {}).auditStatus);
+					if (auditStatus === 1 || auditStatus === 3) {
+						uni.redirectTo({ url: '/pages/topUp/status' });
+					} else if (auditStatus === 2) {
+						uni.redirectTo({ url: '/pages/topUp/manage' });
+					}
+				});
+			},
+			loadAgreement() {
+				prepaidApi.getCurrentAgreement().then((res) => {
+					console.log('11111',res);
+					if (res.data.code !== 200 || !res.data.result) return;
+					const data = res.data.result;
+					this.agreementVersion = data.agreementVersion || '';
+					this.agreementTitle = data.agreementTitle || '储值功能开通业务规则';
+					this.contentText = data.contentText || '';
+				});
+			},
+			buildIdempotencyKey() {
+				const merchantId = getMerchantId() || 'unknown';
+				return `apply-${merchantId}-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
+			},
+			onApply() {
+				if (!this.agreed || this.submitting) {
+					if (!this.agreed) {
+						uni.showToast({ title: '请先阅读并同意协议', icon: 'none' });
+					}
+					return;
+				}
+				// if (!this.agreementVersion) {
+				// 	uni.showToast({ title: '协议版本加载中,请稍后', icon: 'none' });
+				// 	return;
+				// }
+				const merchantId = getMerchantId();
+				if (!merchantId) {
+					uni.showToast({ title: '未获取到门店信息', icon: 'none' });
+					return;
+				}
+				this.submitting = true;
+				prepaidApi
+					.applyFeature(
+						{
+							agreementVersion: this.agreementVersion,
+							agreed: true,
+							idempotencyKey: this.buildIdempotencyKey()
+						},
+						merchantId
+					)
+					.then((res) => {
+						if (res.data.code === 200) {
+							uni.redirectTo({ url: '/pages/topUp/status' });
+						} else {
+							uni.showToast({ title: res.data.message || '申请失败', icon: 'none' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.submitting = false;
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+		display: flex;
+		flex-direction: column;
+	}
+
+	.content {
+		flex: 1;
+		padding: 24rpx 24rpx 0;
+		box-sizing: border-box;
+	}
+
+	.card {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 32rpx 28rpx 40rpx;
+		margin-bottom: 24rpx;
+	}
+
+	.doc-title {
+		text-align: center;
+		font-size: 32rpx;
+		font-weight: 600;
+		color: #333;
+		margin-bottom: 28rpx;
+	}
+
+	.section {
+		margin-bottom: 28rpx;
+
+		.h {
+			font-size: 28rpx;
+			font-weight: 600;
+			color: #333;
+			margin-bottom: 12rpx;
+		}
+
+		.p {
+			font-size: 26rpx;
+			color: #666;
+			line-height: 1.7;
+			text-align: justify;
+			margin-bottom: 12rpx;
+		}
+
+		.warn {
+			color: #e54d42;
+		}
+	}
+
+	.footer {
+		width: 100%;
+		position: fixed;
+		bottom: 0;
+		background: #fff;
+		padding: 20rpx 32rpx calc(20rpx + env(safe-area-inset-bottom));
+		box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
+
+		.agree {
+			display: flex;
+			align-items: center;
+			font-size: 24rpx;
+			color: #666;
+			margin-bottom: 20rpx;
+
+			.checkbox {
+				width: 32rpx;
+				height: 32rpx;
+				// border: 2rpx solid #c8c9cc;
+				// border-radius: 6rpx;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				flex-shrink: 0;
+
+				// &.checked {
+				// 	background: #2979ff;
+				// 	border-color: #2979ff;
+				// }
+				.checkbox-false{
+					width: 34rpx;
+					height: 34rpx;
+					border-radius: 50%;
+					border: 1rpx solid #ccc;
+				}
+			}
+			text{
+				margin-left: 12rpx;
+			}
+		}
+
+		.btn {
+			height: 88rpx;
+			line-height: 88rpx;
+			text-align: center;
+			background: linear-gradient( 90deg, #51A2FF 0%, #4EC2FF 100%);
+			border-radius: 140rpx;
+			color: #fff;
+			font-size: 32rpx;
+			// font-weight: 500;
+
+			&.disabled {
+				opacity: 0.45;
+			}
+		}
+	}
+</style>

+ 69 - 0
pages/topUp/index.vue

@@ -0,0 +1,69 @@
+<template>
+	<view class="page">
+		<u-navbar title="储值管理" :autoBack="true" :placeholder="true"></u-navbar>
+		<view class="loading">
+			<u-loading mode="circle" size="48"></u-loading>
+			<text>加载中...</text>
+		</view>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantId } from '@/api/memberPrepaid.js'
+
+	/**
+	 * auditStatus: 0未申请 / 1待审核 / 2已通过 / 3已驳回
+	 */
+	export default {
+		onShow() {
+			this.routeByStatus();
+		},
+		methods: {
+			routeByStatus() {
+				const merchantId = getMerchantId();
+				if (!merchantId) {
+					uni.showToast({ title: '未获取到门店信息', icon: 'none' });
+					return;
+				}
+				prepaidApi
+					.getFeatureStatus(merchantId)
+					.then((res) => {
+						if (res.data.code !== 200) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const data = res.data.result || {};
+						const auditStatus = Number(data.auditStatus);
+						let url = '/pages/topUp/agreement';
+						if (auditStatus === 1 || auditStatus === 3) {
+							url = '/pages/topUp/status';
+						} else if (auditStatus === 2) {
+							url = '/pages/topUp/manage';
+						}
+						uni.redirectTo({ url });
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+	}
+
+	.loading {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		padding-top: 200rpx;
+		color: #999;
+		font-size: 26rpx;
+		gap: 20rpx;
+	}
+</style>

+ 560 - 0
pages/topUp/manage.vue

@@ -0,0 +1,560 @@
+<template>
+	<view class="page">
+		<u-navbar title="储值管理" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<!-- 总开关 -->
+		<view class="card switch-card">
+			<text class="label">储值功能</text>
+			<u-switch v-model="enabled" activeColor="#2979ff" :disabled="toggling" @change="onToggle"></u-switch>
+		</view>
+
+		<!-- 数据指标 -->
+		<view class="stats-card" :style="{ backgroundImage: 'url(' + statsBg + ')' }">
+			<view class="balance-block">
+				<text class="balance-label">储值余额(元)</text>
+				<text class="balance-value">¥ {{ balanceText }}</text>
+			</view>
+			<view class="stats-panel">
+				<view class="stat" v-for="(item, index) in stats" :key="index">
+					<view class="value">{{ item.value }}</view>
+					<view class="name">{{ item.name }}</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 操作入口 -->
+		<view class="actions card">
+			<view class="action-btn" @click="goSettings">
+				<image class="action-icon" :src="iconSettings" mode="aspectFit"></image>
+				<text class="action-text">储值设置</text>
+				<image class="action-more" :src="iconMore" mode="aspectFit"></image>
+			</view>
+			<view class="action-divider"></view>
+			<view class="action-btn" @click="goShareStore">
+				<image class="action-icon" :src="iconShareStore" mode="aspectFit"></image>
+				<text class="action-text">共享门店</text>
+				<image class="action-more" :src="iconMore" mode="aspectFit"></image>
+			</view>
+		</view>
+
+		<!-- 账户明细 -->
+		<view class="card detail-card">
+			<view class="detail-head">
+				<text class="title">账户明细</text>
+				<view class="more" @click="goAll">
+					全部
+					<u-icon name="arrow-right" color="#999" size="12"></u-icon>
+				</view>
+			</view>
+
+			<view class="list" v-if="records.length">
+				<view class="item" v-for="(item, index) in records" :key="item.id || index" @click="goDetail(item)">
+					<image class="left-icon" :src="getTypeIcon(item.type)" mode="aspectFit"></image>
+					<view class="info">
+						<view class="row1">
+							<text class="name">{{ item.title }}</text>
+							<text class="amount" :class="{ plus: item.amount > 0 }">
+								{{ item.amount > 0 ? '+' : '' }}{{ formatMoney(item.amount) }}
+							</text>
+						</view>
+						<view class="row2">
+							<text class="store">{{ item.storeName }}</text>
+							<text class="time">{{ item.time }}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="empty" v-else>暂无明细</view>
+		</view>
+
+		<!-- 开启/关闭确认弹窗 -->
+		<u-popup v-model="confirmVisible" mode="center" border-radius="24" :mask-close-able="false">
+			<view class="confirm-dialog">
+				<view class="confirm-title">提示</view>
+				<view class="confirm-body">
+					<text class="confirm-text">{{ confirmContent }}</text>
+				</view>
+				<view class="confirm-footer">
+					<view class="btn cancel" @click="onConfirmCancel">取消</view>
+					<view class="btn ok" @click="onConfirmOk">确定</view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantId } from '@/api/memberPrepaid.js'
+
+	export default {
+		data() {
+			return {
+				enabled: false,
+				toggling: false,
+				confirmVisible: false,
+				confirmContent: '',
+				pendingEnabled: null,
+				balance: 0,
+				balanceText: '0.00',
+				statsBg: '/static/topUp/indexBg.png',
+				iconSettings: '/static/topUp/prepaidsettings.png',
+				iconShareStore: '/static/topUp/sharedstores.png',
+				iconMore: '/static/topUp/more.png',
+				iconRecharge: '/static/topUp/recharge.png',
+				iconSpend: '/static/topUp/expenditure.png',
+				iconGive: '/static/topUp/give.png',
+				stats: [
+					{ name: '储值总额(元)', value: '0.00' },
+					{ name: '储值总用户', value: '0' },
+					{ name: '储值余额用户', value: '0' }
+				],
+				records: []
+			};
+		},
+		onShow() {
+			this.bootstrap();
+		},
+		methods: {
+			formatMoney(val) {
+				const n = Number(val);
+				if (Number.isNaN(n)) return '0.00';
+				return n.toFixed(2);
+			},
+			formatCount(val) {
+				return String(Number(val) || 0);
+			},
+			getTypeIcon(type) {
+				if (type === 'spend') return this.iconSpend;
+				if (type === 'gift') return this.iconGive;
+				return this.iconRecharge;
+			},
+			mapLedgerItem(item) {
+				const txType = String(item.transactionType || '').toUpperCase();
+				const isRecharge = txType === 'RECHARGE';
+				const isGift = txType === 'GIFT' || txType === 'GIVE';
+				const userName = item.userName || '用户';
+				const changeAmount = Number(item.changeAmount);
+				let type = 'spend';
+				let titlePrefix = '支出';
+				if (isGift) {
+					type = 'gift';
+					titlePrefix = '赠送';
+				} else if (isRecharge) {
+					type = 'recharge';
+					titlePrefix = '充值';
+				}
+				return {
+					id: item.ledgerId,
+					type,
+					title: `${titlePrefix}-${userName}`,
+					storeName: item.storeName || '',
+					amount: Number.isNaN(changeAmount)
+						? isRecharge || isGift
+							? Number(item.payAmount || 0) + Number(item.giftAmount || 0)
+							: -Math.abs(Number(item.payAmount || 0))
+						: changeAmount,
+					time: item.transactionTime || ''
+				};
+			},
+			bootstrap() {
+				const merchantId = getMerchantId();
+				if (!merchantId) {
+					uni.showToast({ title: '未获取到门店信息', icon: 'none' });
+					return;
+				}
+				prepaidApi.getFeatureStatus(merchantId).then((res) => {
+					if (res.data.code !== 200) {
+						uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+						return;
+					}
+					const data = res.data.result || {};
+					const auditStatus = Number(data.auditStatus);
+					if (auditStatus !== 2) {
+						uni.redirectTo({ url: '/pages/topUp/index' });
+						return;
+					}
+					this.enabled = Number(data.operationStatus) === 1;
+					this.loadOverview(merchantId);
+				});
+			},
+			loadOverview(merchantId) {
+				prepaidApi.getOverview(merchantId).then((res) => {
+					if (res.data.code !== 200 || !res.data.result) return;
+					const data = res.data.result;
+					console.log('11111data',data)
+					this.balance = Number(data.balance) || 0;
+					this.balanceText = this.formatMoney(data.balance);
+					this.stats = [
+						{ name: '储值总额(元)', value: this.formatMoney(data.totalRechargeAmount) },
+						{ name: '储值总用户', value: this.formatCount(data.totalUserCount) },
+						{ name: '储值余额用户', value: this.formatCount(data.balanceUserCount) }
+					];
+					const list = Array.isArray(data.latestLedgers) ? data.latestLedgers : [];
+					this.records = list.map((item) => this.mapLedgerItem(item));
+				});
+			},
+			onToggle(val) {
+				this.enabled = !val;
+				this.pendingEnabled = val;
+				if (val) {
+					this.confirmContent = '是否确认开启门店储值功能?';
+				} else if (this.balance > 0) {
+					this.confirmContent = '用户账户余额未使用完是否确认关闭门店储值功能?';
+				} else {
+					this.confirmContent = '是否确认关闭门店储值功能?';
+				}
+				this.confirmVisible = true;
+			},
+			onConfirmCancel() {
+				this.confirmVisible = false;
+				this.pendingEnabled = null;
+			},
+			onConfirmOk() {
+				const val = this.pendingEnabled;
+				this.confirmVisible = false;
+				this.pendingEnabled = null;
+				if (val === null || val === undefined || this.toggling) return;
+				const merchantId = getMerchantId();
+				this.toggling = true;
+				const req = val
+					? prepaidApi.openFeature(merchantId)
+					: prepaidApi.closeFeature({}, merchantId);
+				req
+					.then((res) => {
+						if (res.data.code === 200) {
+							const data = res.data.result || {};
+							this.enabled = Number(data.operationStatus) === 1;
+							uni.showToast({
+								title: this.enabled ? '已开启储值功能' : '已关闭储值功能',
+								icon: 'none'
+							});
+						} else {
+							uni.showToast({ title: res.data.message || '操作失败', icon: 'none' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.toggling = false;
+					});
+			},
+			goSettings() {
+				uni.navigateTo({ url: '/pages/topUp/settings' });
+			},
+			goShareStore() {
+				uni.navigateTo({ url: '/pages/topUp/shareStore' });
+			},
+			goAll() {
+				uni.navigateTo({ url: '/pages/topUp/records' });
+			},
+			goDetail(item) {
+				const type = item.type === 'spend' ? 'spend' : 'recharge';
+				uni.navigateTo({
+					url: `/pages/topUp/recordDetail?id=${item.id || ''}&type=${type}`
+				});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f5f6f8;
+		padding: 24rpx;
+		padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
+	}
+
+	.card {
+		background: #fff;
+		border-radius: 16rpx;
+		margin-bottom: 24rpx;
+	}
+
+	.switch-card {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 28rpx 32rpx;
+
+		.label {
+			font-size: 30rpx;
+			color: #333;
+			font-weight: 500;
+		}
+	}
+
+	.stats-card {
+		border-radius: 20rpx;
+		margin-bottom: 24rpx;
+		padding: 36rpx 28rpx 24rpx;
+		background-color: #3a9bff;
+		background-repeat: no-repeat;
+		background-size: 100% 100%;
+		background-position: center;
+		overflow: hidden;
+		box-sizing: border-box;
+
+		.balance-block {
+			padding: 8rpx 8rpx 32rpx;
+
+			.balance-label {
+				display: block;
+				font-size: 26rpx;
+				color: rgba(255, 255, 255, 0.9);
+				margin-bottom: 16rpx;
+			}
+
+			.balance-value {
+				display: block;
+				font-size: 56rpx;
+				font-weight: 600;
+				color: #fff;
+				line-height: 1.2;
+				text-align: center;
+			}
+		}
+
+		.stats-panel {
+			display: flex;
+			align-items: stretch;
+			background: #fff;
+			border-radius: 16rpx;
+			padding: 28rpx 8rpx;
+
+			.stat {
+				flex: 1;
+				text-align: center;
+				position: relative;
+
+				&:not(:last-child)::after {
+					content: '';
+					position: absolute;
+					right: 0;
+					top: 50%;
+					transform: translateY(-50%);
+					width: 1px;
+					height: 56rpx;
+					background: #f0f0f0;
+				}
+
+				.value {
+					font-size: 32rpx;
+					font-weight: 600;
+					color: #333;
+					line-height: 1.3;
+				}
+
+				.name {
+					font-size: 22rpx;
+					color: #999;
+					margin-top: 10rpx;
+				}
+			}
+		}
+	}
+
+	.actions {
+		display: flex;
+		align-items: center;
+		padding: 8rpx 0;
+		margin-bottom: 24rpx;
+
+		.action-btn {
+			flex: 1;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding: 28rpx 16rpx;
+			gap: 12rpx;
+		}
+
+		.action-icon {
+			width: 44rpx;
+			height: 44rpx;
+			flex-shrink: 0;
+		}
+
+		.action-text {
+			font-size: 28rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		.action-more {
+			width: 28rpx;
+			height: 28rpx;
+			flex-shrink: 0;
+		}
+
+		.action-divider {
+			width: 1px;
+			height: 40rpx;
+			background: #ebebeb;
+			flex-shrink: 0;
+		}
+	}
+
+	.detail-card {
+		padding: 28rpx 24rpx 8rpx;
+
+		.detail-head {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-bottom: 8rpx;
+
+			.title {
+				font-size: 30rpx;
+				font-weight: 600;
+				color: #333;
+			}
+
+			.more {
+				display: flex;
+				align-items: center;
+				font-size: 24rpx;
+				color: #999;
+				gap: 4rpx;
+			}
+		}
+
+		.item {
+			display: flex;
+			align-items: center;
+			padding: 28rpx 0;
+			border-bottom: 1px solid #f5f5f5;
+
+			&:last-child {
+				border-bottom: none;
+			}
+
+			.left-icon {
+				width: 72rpx;
+				height: 72rpx;
+				margin-right: 20rpx;
+				flex-shrink: 0;
+				border-radius: 50%;
+			}
+
+			.info {
+				flex: 1;
+				min-width: 0;
+			}
+
+			.row1,
+			.row2 {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+			}
+
+			.row1 {
+				margin-bottom: 10rpx;
+
+				.name {
+					font-size: 28rpx;
+					color: #333;
+					font-weight: 500;
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+					max-width: 360rpx;
+				}
+
+				.amount {
+					font-size: 30rpx;
+					font-weight: 600;
+					color: #333;
+					flex-shrink: 0;
+
+					&.plus {
+						color: #2f7bff;
+					}
+				}
+			}
+
+			.row2 {
+				.store,
+				.time {
+					font-size: 22rpx;
+					color: #999;
+				}
+
+				.store {
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
+					max-width: 360rpx;
+				}
+			}
+		}
+
+		.empty {
+			padding: 60rpx 0;
+			text-align: center;
+			color: #ccc;
+			font-size: 26rpx;
+		}
+	}
+
+	.confirm-dialog {
+		width: 560rpx;
+		background: #fff;
+		border-radius: 24rpx;
+		overflow: hidden;
+		padding: 48rpx 40rpx 40rpx;
+		box-sizing: border-box;
+
+		.confirm-title {
+			text-align: center;
+			font-size: 34rpx;
+			font-weight: 600;
+			color: #333;
+			line-height: 1.4;
+			margin-bottom: 28rpx;
+		}
+
+		.confirm-body {
+			padding: 0 8rpx 40rpx;
+
+			.confirm-text {
+				display: block;
+				text-align: center;
+				font-size: 28rpx;
+				color: #333;
+				line-height: 1.6;
+			}
+		}
+
+		.confirm-footer {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 24rpx;
+
+			.btn {
+				flex: 1;
+				height: 80rpx;
+				line-height: 80rpx;
+				text-align: center;
+				border-radius: 40rpx;
+				font-size: 30rpx;
+				box-sizing: border-box;
+			}
+
+			.cancel {
+				color: #999;
+				background: #fff;
+				border: 2rpx solid #dcdcdc;
+			}
+
+			.ok {
+				color: #fff;
+				background: linear-gradient(90deg, #3ab3ff 0%, #2472ff 100%);
+				border: none;
+			}
+		}
+	}
+</style>

+ 414 - 0
pages/topUp/recordDetail.vue

@@ -0,0 +1,414 @@
+<template>
+	<view class="page">
+		<u-navbar :title="pageTitle" :autoBack="true" :placeholder="true"></u-navbar>
+		<view v-if="loading" class="loading">
+			<u-loading mode="circle" size="48"></u-loading>
+		</view>
+
+		<template v-else>
+			<!-- 状态摘要 -->
+			<view class="hero card">
+				<view class="amount" :class="{ plus: isRecharge }">
+					{{ isRecharge ? '+' : '-' }} {{ formatAmount(Math.abs(detail.amount || 0)) }}
+				</view>
+				<view class="status">{{ isRecharge ? '充值成功' : '支出成功' }}</view>
+			</view>
+
+			<!-- 交易详情 -->
+			<view class="card detail-card">
+				<template v-if="isRecharge">
+					<view class="row" v-if="detail.payTime">
+						<text class="label">支付时间</text>
+						<text class="value">{{ detail.payTime }}</text>
+					</view>
+					<view class="row" v-if="detail.payMethod">
+						<text class="label">支付方式</text>
+						<text class="value">{{ detail.payMethod }}</text>
+					</view>
+					<view class="row" v-if="detail.storeName">
+						<text class="label">储值商户</text>
+						<text class="value">{{ detail.storeName }}</text>
+					</view>
+					<view class="row" v-if="detail.orderNo">
+						<text class="label">订单号</text>
+						<text class="value">{{ detail.orderNo }}</text>
+					</view>
+					<view class="row" v-if="detail.userName">
+						<text class="label">用户名</text>
+						<text class="value">{{ detail.userName }}</text>
+					</view>
+					<view class="row" v-if="detail.userAccount">
+						<text class="label">用户账户</text>
+						<text class="value">{{ detail.userAccount }}</text>
+					</view>
+					<view class="row" v-if="detail.paidAmount != null && detail.paidAmount !== ''">
+						<text class="label">实付金额</text>
+						<text class="value">{{ detail.paidAmount }}元</text>
+					</view>
+				</template>
+
+				<template v-else>
+					<view class="row" v-if="detail.orderNo">
+						<text class="label">订单号</text>
+						<text class="value">{{ detail.orderNo }}</text>
+					</view>
+					<view class="row" v-if="detail.storeName">
+						<text class="label">消费门店</text>
+						<text class="value">{{ detail.storeName }}</text>
+					</view>
+					<view class="row" v-if="detail.amount != null">
+						<text class="label">消费金额</text>
+						<text class="value">{{ formatAmount(Math.abs(detail.amount)) }}元</text>
+					</view>
+					<view class="row" v-if="detail.spendTime">
+						<text class="label">消费时间</text>
+						<text class="value">{{ detail.spendTime }}</text>
+					</view>
+					<view class="row" v-if="detail.userName">
+						<text class="label">用户名</text>
+						<text class="value">{{ detail.userName }}</text>
+					</view>
+					<view class="row" v-if="detail.userAccount">
+						<text class="label">用户账户</text>
+						<text class="value">{{ detail.userAccount }}</text>
+					</view>
+				</template>
+			</view>
+
+			<!-- 赠送信息 -->
+			<view class="card gift-card" v-if="isRecharge && showGiftSection">
+				<view class="gift-title">赠送信息</view>
+				<view class="row" v-if="Number(detail.giftAmount) > 0">
+					<text class="label">赠送金额</text>
+					<text class="value">{{ detail.giftAmount }}元</text>
+				</view>
+				<view
+					class="row link"
+					v-if="couponSummary"
+					@click="couponExpanded = !couponExpanded"
+				>
+					<text class="label">赠送优惠券</text>
+					<view class="value-wrap">
+						<text class="value">{{ couponSummary }}</text>
+						<u-icon
+							:name="couponExpanded ? 'arrow-up' : 'arrow-down'"
+							color="#999"
+							size="14"
+						></u-icon>
+					</view>
+				</view>
+
+				<!-- 优惠券展开列表 -->
+				<scroll-view
+					v-if="couponExpanded && coupons.length"
+					class="coupon-scroll"
+					scroll-x
+					:show-scrollbar="false"
+				>
+					<view class="coupon-list">
+						<view class="coupon-item" v-for="(c, idx) in coupons" :key="idx">
+							<image class="coupon-bg" src="/static/topUp/coupon.png" mode="scaleToFill"></image>
+							<view class="coupon-body">
+								<view class="coupon-top">
+									<text class="c-name">{{ c.name }}</text>
+									<text class="c-value">{{ c.display }}</text>
+								</view>
+								<view class="coupon-bottom">
+									<text class="c-qty">x{{ c.qty }}张</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</scroll-view>
+			</view>
+		</template>
+	</view>
+</template>
+
+<script>
+	import prepaidApi from '@/api/memberPrepaid.js'
+
+
+	export default {
+		data() {
+			return {
+				recordId: '',
+				recordType: 'recharge',
+				detail: {},
+				couponExpanded: false,
+				loading: false
+			};
+		},
+		computed: {
+			isRecharge() {
+				return this.recordType !== 'spend';
+			},
+			pageTitle() {
+				return this.isRecharge ? '充值详情' : '支出详情';
+			},
+			coupons() {
+				return Array.isArray(this.detail.coupons) ? this.detail.coupons : [];
+			},
+			couponSummary() {
+				if (!this.coupons.length) return '';
+				const totalQty = this.coupons.reduce((s, c) => s + (Number(c.qty) || 0), 0);
+				if (!totalQty) return '';
+				const face = this.detail.couponFaceText;
+				return face ? `${totalQty}张/${face}` : `${totalQty}张优惠券`;
+			},
+			showGiftSection() {
+				return Number(this.detail.giftAmount) > 0 || !!this.couponSummary;
+			}
+		},
+		onLoad(options) {
+			this.recordId = options.id || '';
+			this.recordType = options.type === 'spend' ? 'spend' : 'recharge';
+			this.loadDetail();
+		},
+		methods: {
+			formatAmount(val) {
+				const n = Number(val) || 0;
+				return Number.isInteger(n) ? String(n) : n.toFixed(2);
+			},
+			loadDetail() {
+				if (!this.recordId) {
+					uni.showToast({ title: '缺少明细ID', icon: 'none' });
+					return;
+				}
+				this.loading = true;
+				prepaidApi
+					.getLedgerDetail(this.recordId)
+					.then((res) => {
+						if (res.data.code !== 200 || !res.data.result) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const data = res.data.result;
+						const isRecharge = data.transactionType === 'RECHARGE';
+						this.recordType = isRecharge ? 'recharge' : 'spend';
+						const coupons = (data.coupons || []).map((c) => {
+							const isDiscount = String(c.couponType) === '2';
+							return {
+								name: c.couponName || '',
+								display: isDiscount
+									? c.faceAmount != null
+										? `${c.faceAmount}折`
+										: '折扣'
+									: c.faceAmount != null
+										? `${c.faceAmount}元`
+										: '优惠',
+								qty: Number(c.quantity) || 1
+							};
+						});
+						const couponTotal = Number(data.couponTotalAmount) || 0;
+						this.detail = {
+							id: data.ledgerId,
+							type: this.recordType,
+							amount: isRecharge
+								? Number(data.transactionAmount != null ? data.transactionAmount : data.payAmount) || 0
+								: Math.abs(Number(data.transactionAmount != null ? data.transactionAmount : data.payAmount) || 0),
+							payTime: data.paidTime || data.successTime || data.createTime || '',
+							spendTime: data.successTime || data.paidTime || data.createTime || '',
+							payMethod: data.payChannel,
+							storeName: isRecharge
+								? data.issuerMerchantName || data.transactionStoreName || ''
+								: data.transactionStoreName || data.issuerMerchantName || '',
+							orderNo: data.orderNo || '',
+							userName: data.userName || '',
+							userAccount: data.userAccount || '',
+							paidAmount: data.payAmount != null ? data.payAmount : '',
+							giftAmount: data.giftAmount != null ? data.giftAmount : 0,
+							coupons,
+							couponFaceText: couponTotal > 0 ? `${this.formatAmount(couponTotal)}元优惠券` : ''
+						};
+						this.couponExpanded = false;
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.loading = false;
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+		padding: 24rpx;
+		padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
+		box-sizing: border-box;
+	}
+
+	.loading {
+		padding-top: 200rpx;
+		display: flex;
+		justify-content: center;
+	}
+
+	.card {
+		background: #fff;
+		border-radius: 16rpx;
+		margin-bottom: 24rpx;
+	}
+
+	.hero {
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		padding: 56rpx 24rpx 48rpx;
+
+		.amount {
+			font-size: 56rpx;
+			font-weight: 700;
+			color: #333;
+			line-height: 1.2;
+
+			&.plus {
+				color: #2b85e4;
+			}
+		}
+
+		.status {
+			margin-top: 16rpx;
+			font-size: 28rpx;
+			color: #333;
+		}
+	}
+
+	.detail-card,
+	.gift-card {
+		padding: 8rpx 28rpx 16rpx;
+	}
+
+	.gift-title {
+		padding: 28rpx 0 8rpx;
+		font-size: 30rpx;
+		font-weight: 600;
+		color: #333;
+	}
+
+	.row {
+		display: flex;
+		align-items: flex-start;
+		justify-content: space-between;
+		padding: 24rpx 0;
+		gap: 24rpx;
+
+		.label {
+			font-size: 28rpx;
+			color: #999;
+			flex-shrink: 0;
+			width: 180rpx;
+		}
+
+		.value {
+			flex: 1;
+			text-align: right;
+			font-size: 28rpx;
+			color: #333;
+			word-break: break-all;
+		}
+
+		&.link .value-wrap {
+			flex: 1;
+			display: flex;
+			align-items: center;
+			justify-content: flex-end;
+			gap: 8rpx;
+
+			.value {
+				flex: none;
+			}
+		}
+	}
+
+	.coupon-scroll {
+		width: 100%;
+		margin: 8rpx 0 24rpx;
+		white-space: nowrap;
+	}
+
+	.coupon-list {
+		display: inline-flex;
+		flex-direction: row;
+		gap: 24rpx;
+		padding-right: 8rpx;
+	}
+
+	/* coupon.png 原图 198x162,按比例铺底,文字分区对齐白卡/口袋 */
+	.coupon-item {
+		position: relative;
+		width: 198rpx;
+		height: 162rpx;
+		flex-shrink: 0;
+	}
+
+	.coupon-bg {
+		position: absolute;
+		left: 0;
+		top: 0;
+		width: 100%;
+		height: 100%;
+		z-index: 0;
+	}
+
+	.coupon-body {
+		position: relative;
+		z-index: 1;
+		width: 100%;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		box-sizing: border-box;
+	}
+
+	.coupon-top {
+		height: 58%;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		padding: 8rpx 12rpx 0;
+		box-sizing: border-box;
+
+		.c-name {
+			font-size: 22rpx;
+			color: #333;
+			line-height: 1.2;
+			text-align: center;
+			max-width: 100%;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.c-value {
+			margin-top: 6rpx;
+			font-size: 34rpx;
+			font-weight: 700;
+			color: #ff3b5c;
+			line-height: 1.15;
+		}
+	}
+
+	.coupon-bottom {
+		height: 42%;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding-bottom: 4rpx;
+		box-sizing: border-box;
+
+		.c-qty {
+			font-size: 24rpx;
+			color: #fff;
+			font-weight: 500;
+			line-height: 1;
+		}
+	}
+</style>

+ 414 - 0
pages/topUp/records.vue

@@ -0,0 +1,414 @@
+<template>
+	<view class="page">
+		<u-navbar title="账户明细" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<!-- Tabs -->
+		<view class="tabs">
+			<view
+				class="tab"
+				v-for="(tab, idx) in tabs"
+				:key="idx"
+				:class="{ active: currentTab === idx }"
+				@click="switchTab(idx)"
+			>
+				<text class="tab-text">{{ tab }}</text>
+				<image v-if="currentTab === idx" class="tab-indicator" :src="tabIndicator" mode="aspectFit"></image>
+			</view>
+		</view>
+
+		<!-- 内容卡片:月份 + 列表 -->
+		<view class="content-card">
+			<view class="filter-bar">
+				<picker mode="date" :value="monthValue" fields="month" @change="onMonthChange">
+					<view class="month">
+						<text>{{ monthLabel }}</text>
+						<u-icon name="arrow-down" color="#666" size="12"></u-icon>
+					</view>
+				</picker>
+				<view class="summary">
+					<template v-if="currentTab === 0">
+						<text>充值¥{{ formatMoney(summary.recharge) }}</text>
+						<text class="sep">|</text>
+						<text>支出¥{{ formatMoney(summary.spend) }}</text>
+					</template>
+					<template v-else-if="currentTab === 1">
+						<text>支出¥{{ formatMoney(summary.spend) }}</text>
+					</template>
+					<template v-else>
+						<text>实付¥{{ formatMoney(summary.paid) }}</text>
+						<text class="sep">|</text>
+						<text>赠送¥{{ formatMoney(summary.gift) }}</text>
+					</template>
+				</view>
+			</view>
+
+			<scroll-view scroll-y class="scroll" @scrolltolower="loadMore">
+				<view class="list" v-if="list.length">
+					<view class="item" v-for="item in list" :key="item.id" @click="goDetail(item)">
+						<image class="left-icon" :src="getTypeIcon(item.type)" mode="aspectFit"></image>
+						<view class="info">
+							<view class="row1">
+								<text class="name">{{ item.title }}</text>
+								<text class="amount" :class="{ plus: item.amount > 0 }">
+									{{ item.amount > 0 ? '+' : '' }}{{ formatMoney(item.amount) }}
+								</text>
+							</view>
+							<view class="row2">
+								<text class="store">{{ item.storeName }}</text>
+								<text class="time">{{ formatListTime(item.time) }}</text>
+							</view>
+						</view>
+					</view>
+					<view class="load-more" v-if="loading">加载中...</view>
+					<view class="load-more" v-else-if="hasMore" @click="loadMore">继续加载</view>
+					<view class="load-more done" v-else>没有更多了</view>
+				</view>
+				<view class="empty-wrap" v-else-if="!loading">
+					<u-empty text="暂无数据" mode="list"></u-empty>
+				</view>
+				<view class="empty-wrap" v-else>
+					<u-loading mode="circle" size="48"></u-loading>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import prepaidApi from '@/api/memberPrepaid.js'
+
+	const PAGE_SIZE = 10;
+	const TAB_TYPES = ['ALL', 'CONSUME', 'RECHARGE'];
+
+	export default {
+		data() {
+			const now = new Date();
+			const y = now.getFullYear();
+			const m = now.getMonth() + 1;
+			return {
+				tabs: ['全部', '支出', '充值'],
+				currentTab: 0,
+				year: y,
+				month: m,
+				list: [],
+				page: 1,
+				hasMore: false,
+				loading: false,
+				tabIndicator: '/static/topUp/tabs.png',
+				iconRecharge: '/static/topUp/recharge.png',
+				iconSpend: '/static/topUp/expenditure.png',
+				iconGive: '/static/topUp/give.png',
+				summary: {
+					recharge: 0,
+					spend: 0,
+					paid: 0,
+					gift: 0
+				}
+			};
+		},
+		computed: {
+			monthLabel() {
+				return `${this.year}年${this.month}月`;
+			},
+			monthValue() {
+				return `${this.year}-${String(this.month).padStart(2, '0')}`;
+			}
+		},
+		onLoad() {
+			this.reload();
+		},
+		methods: {
+			formatMoney(val) {
+				const n = Number(val) || 0;
+				return n.toFixed(2);
+			},
+			formatListTime(time) {
+				const m = String(time).match(/(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{2}:\d{2}:\d{2})/);
+				if (!m) return time;
+				return `${Number(m[2])}月${Number(m[3])}日 ${m[4]}`;
+			},
+			getTypeIcon(type) {
+				if (type === 'spend') return this.iconSpend;
+				if (type === 'gift') return this.iconGive;
+				return this.iconRecharge;
+			},
+			mapItem(item) {
+				const txType = String(item.transactionType || '').toUpperCase();
+				const isRecharge = txType === 'RECHARGE';
+				const isGift = txType === 'GIFT' || txType === 'GIVE';
+				const userName = item.userName || '用户';
+				const changeAmount = Number(item.changeAmount);
+				let type = 'spend';
+				let titlePrefix = '支出';
+				if (isGift) {
+					type = 'gift';
+					titlePrefix = '赠送';
+				} else if (isRecharge) {
+					type = 'recharge';
+					titlePrefix = '充值';
+				}
+				return {
+					id: item.ledgerId,
+					type,
+					title: `${titlePrefix}-${userName}`,
+					storeName: item.storeName || '',
+					amount: Number.isNaN(changeAmount)
+						? isRecharge || isGift
+							? Number(item.payAmount || 0) + Number(item.giftAmount || 0)
+							: -Math.abs(Number(item.payAmount || 0))
+						: changeAmount,
+					time: item.transactionTime || ''
+				};
+			},
+			reload() {
+				this.page = 1;
+				this.list = [];
+				this.hasMore = false;
+				this.fetchPage(true);
+			},
+			fetchPage(reset) {
+				if (this.loading) return;
+				this.loading = true;
+				prepaidApi
+					.getLedgerPage({
+						month: this.monthValue,
+						transactionType: TAB_TYPES[this.currentTab],
+						pageNo: this.page,
+						pageSize: PAGE_SIZE
+					})
+					.then((res) => {
+						if (res.data.code !== 200) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const data = res.data.result || {};
+						const summary = data.summary || {};
+						this.summary = {
+							recharge: Number(summary.rechargeAmount) || 0,
+							spend: Number(summary.consumeAmount) || 0,
+							paid: Number(summary.payAmount) || 0,
+							gift: Number(summary.giftAmount) || 0
+						};
+						const page = data.page || {};
+						const records = Array.isArray(page.records) ? page.records : [];
+						const mapped = records.map((item) => this.mapItem(item));
+						this.list = reset ? mapped : this.list.concat(mapped);
+						const current = Number(page.current) || this.page;
+						const pages = Number(page.pages) || 0;
+						this.hasMore = current < pages;
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.loading = false;
+					});
+			},
+			loadMore() {
+				if (!this.hasMore || this.loading) return;
+				this.page += 1;
+				this.fetchPage(false);
+			},
+			switchTab(idx) {
+				if (this.currentTab === idx) return;
+				this.currentTab = idx;
+				this.reload();
+			},
+			onMonthChange(e) {
+				const val = e.detail.value || '';
+				const parts = val.split('-');
+				if (parts.length >= 2) {
+					this.year = Number(parts[0]);
+					this.month = Number(parts[1]);
+					this.reload();
+				}
+			},
+			goDetail(item) {
+				const type = item.type === 'spend' ? 'spend' : 'recharge';
+				uni.navigateTo({
+					url: `/pages/topUp/recordDetail?id=${item.id}&type=${type}`
+				});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		height: 100vh;
+		background: #f5f6f8;
+		display: flex;
+		flex-direction: column;
+		box-sizing: border-box;
+	}
+
+	.tabs {
+		display: flex;
+		background: #fff;
+		padding-bottom: 4rpx;
+
+		.tab {
+			flex: 1;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			height: 88rpx;
+			position: relative;
+
+			.tab-text {
+				font-size: 30rpx;
+				color: #999;
+				line-height: 1.2;
+			}
+
+			.tab-indicator {
+				position: absolute;
+				left: 50%;
+				bottom: 10rpx;
+				transform: translateX(-50%);
+				width: 48rpx;
+				height: 12rpx;
+			}
+
+			&.active {
+				.tab-text {
+					color: #2f7bff;
+					font-weight: 600;
+				}
+			}
+		}
+	}
+
+	.content-card {
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		margin: 24rpx;
+		margin-bottom: calc(24rpx + env(safe-area-inset-bottom));
+		background: #fff;
+		border-radius: 20rpx;
+		overflow: hidden;
+		min-height: 0;
+	}
+
+	.filter-bar {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 28rpx 28rpx 12rpx;
+		flex-shrink: 0;
+
+		.month {
+			display: flex;
+			align-items: center;
+			gap: 8rpx;
+			font-size: 28rpx;
+			color: #333;
+			font-weight: 500;
+		}
+
+		.summary {
+			display: flex;
+			align-items: center;
+			font-size: 24rpx;
+			color: #999;
+
+			.sep {
+				margin: 0 10rpx;
+				color: #ccc;
+			}
+		}
+	}
+
+	.scroll {
+		flex: 1;
+		height: 0;
+	}
+
+	.list {
+		padding: 0 28rpx 24rpx;
+	}
+
+	.item {
+		display: flex;
+		align-items: center;
+		padding: 28rpx 0;
+
+		.left-icon {
+			width: 72rpx;
+			height: 72rpx;
+			margin-right: 20rpx;
+			flex-shrink: 0;
+			border-radius: 50%;
+		}
+
+		.info {
+			flex: 1;
+			min-width: 0;
+		}
+
+		.row1,
+		.row2 {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+		}
+
+		.row1 {
+			margin-bottom: 10rpx;
+
+			.name {
+				font-size: 28rpx;
+				color: #333;
+				font-weight: 500;
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				max-width: 360rpx;
+			}
+
+			.amount {
+				font-size: 30rpx;
+				font-weight: 600;
+				color: #2f7bff;
+				flex-shrink: 0;
+
+				&.plus {
+					color: #2f7bff;
+				}
+			}
+		}
+
+		.row2 {
+			.store,
+			.time {
+				font-size: 22rpx;
+				color: #999;
+			}
+
+			.store {
+				overflow: hidden;
+				text-overflow: ellipsis;
+				white-space: nowrap;
+				max-width: 360rpx;
+			}
+		}
+	}
+
+	.load-more {
+		text-align: center;
+		padding: 24rpx 0;
+		font-size: 26rpx;
+		color: #2f7bff;
+
+		&.done {
+			color: #bbb;
+		}
+	}
+
+	.empty-wrap {
+		padding-top: 160rpx;
+		display: flex;
+		justify-content: center;
+	}
+</style>

+ 1183 - 0
pages/topUp/settings.vue

@@ -0,0 +1,1183 @@
+<template>
+	<view class="page">
+		<u-navbar title="储值设置" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<!-- 是否接受共享 -->
+		<view class="card switch-card">
+			<text class="label">是否接受其他门店共享储值</text>
+			<u-switch v-model="form.acceptShare" activeColor="#1C7CF9"></u-switch>
+		</view>
+
+		<!-- 自定义最低金额 -->
+		<view class="card min-card">
+			<view class="min-head">
+				<text class="label">自定义充值最低金额</text>
+				<text class="min-tip">默认为固定档位中的最低金额</text>
+			</view>
+			<view class="min-input-wrap">
+				<text class="currency">¥</text>
+				<input
+					class="min-input"
+					type="digit"
+					v-model="form.minAmount"
+					:placeholder="String(lowestTierAmount || '')"
+					placeholder-class="placeholder"
+					@input="onMinAmountInput"
+				/>
+			</view>
+		</view>
+
+		<!-- 固定充值 -->
+		<view class="section-head">
+			<view class="section-title-row">
+				<view class="section-bar"></view>
+				<text class="section-title">固定充值</text>
+			</view>
+			<text class="section-tip">*可点击加号添加固定充值档位</text>
+		</view>
+
+		<view class="tier-list">
+			<view class="tier-card" v-for="(item, index) in form.tiers" :key="item.id || index">
+				<view class="tier-top">
+					<text class="tier-name">档位{{ index + 1 }}</text>
+					<view class="tier-ops">
+						<view class="op" @click.stop="openTierEdit(index)">
+							<u-icon name="edit-pen" color="#1C7CF9" size="35"></u-icon>
+						</view>
+						<view class="op" @click.stop="removeTier(index)">
+							<u-icon name="trash" color="#1C7CF9" size="35"></u-icon>
+						</view>
+					</view>
+				</view>
+				<view class="tier-body" @click="openTierEdit(index)">
+					<view class="tier-line">
+						<text class="k">充值金额</text>
+						<text class="v">{{ item.amount }}</text>
+					</view>
+					<view class="tier-line">
+						<text class="k">赠送金额</text>
+						<text class="v">{{ item.giftAmount || 0 }}</text>
+					</view>
+					<view class="tier-line">
+						<text class="k">优惠券</text>
+						<text class="v">{{ getCouponCount(item) }}张</text>
+					</view>
+				</view>
+			</view>
+
+			<view class="empty" v-if="!form.tiers.length && !loading">暂无固定充值档位,请点击加号添加</view>
+		</view>
+
+		<!-- 添加档位 -->
+		<image class="fab" src="/static/topUp/add.png" mode="aspectFit" @click="openTierAdd"></image>
+
+		<!-- 底部保存 -->
+		<view class="footer">
+			<view class="save-btn" :class="{ disabled: saving }" @click="onSave">
+				{{ saving ? '保存中...' : '保存' }}
+			</view>
+		</view>
+
+		<!-- 固定充值编辑弹层 -->
+		<u-popup v-model="tierVisible" mode="right" width="100%" :mask-close-able="false">
+			<view class="sub-page tier-page">
+				<view class="sub-nav">
+					<view class="sub-nav-back" @click="closeTier">
+						<u-icon name="arrow-left" color="#333" size="36"></u-icon>
+					</view>
+					<text class="sub-nav-title">储值设置</text>
+				</view>
+
+				<view class="form-card">
+					<view class="form-title">{{ tierFormTitle }}</view>
+					<view class="form-row">
+						<text class="form-label">充值金额</text>
+						<input
+							class="form-input"
+							type="number"
+							v-model="tierForm.amount"
+							placeholder="请输入正整数"
+							placeholder-class="form-placeholder"
+							@input="onTierAmountInput('amount', $event)"
+						/>
+					</view>
+					<view class="form-row">
+						<text class="form-label">赠送金额</text>
+						<input
+							class="form-input"
+							type="number"
+							v-model="tierForm.giftAmount"
+							placeholder="选填,默认0"
+							placeholder-class="form-placeholder"
+							@input="onTierAmountInput('giftAmount', $event)"
+						/>
+					</view>
+					<view class="form-row link-row" @click="openCouponSelect">
+						<text class="form-label">赠送优惠券</text>
+						<view class="link-right">
+							<text class="link-text" :class="{ active: couponSelectedCount > 0 }">
+								{{ couponSelectedCount > 0 ? `已选 ${couponSelectedCount} 张` : '去选择' }}
+							</text>
+							<u-icon name="arrow-right" color="#c0c4cc" size="14"></u-icon>
+						</view>
+					</view>
+				</view>
+
+				<view class="footer tier-footer">
+					<view class="save-btn" :class="{ disabled: !canSubmitTier }" @click="submitTier">
+						{{ tierEditIndex === -1 ? '确认添加' : '确认修改' }}
+					</view>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 优惠券选择弹层 -->
+		<u-popup v-model="couponVisible" mode="right" width="100%" :mask-close-able="false">
+			<view class="sub-page coupon-page">
+				<view class="sub-nav">
+					<view class="sub-nav-back" @click="closeCouponSelect">
+						<u-icon name="arrow-left" color="#333" size="36"></u-icon>
+					</view>
+					<text class="sub-nav-title">优惠券</text>
+				</view>
+
+				<view class="tabs">
+					<view
+						class="tab"
+						v-for="(tab, idx) in couponTabs"
+						:key="idx"
+						:class="{ active: couponTab === idx }"
+						@click="switchCouponTab(idx)"
+					>
+						<text class="tab-text">{{ tab }}</text>
+						<image
+							v-if="couponTab === idx"
+							class="tab-indicator"
+							src="/static/topUp/tabs.png"
+							mode="aspectFit"
+						></image>
+					</view>
+				</view>
+
+				<scroll-view scroll-y class="coupon-scroll">
+					<view
+						class="coupon-item"
+						v-for="item in currentCouponList"
+						:key="item.id"
+						@click="toggleCoupon(item)"
+					>
+						<view class="radio" :class="{ checked: isCouponChecked(item.id) }">
+							<u-icon v-if="isCouponChecked(item.id)" name="checkmark" color="#fff" size="12"></u-icon>
+						</view>
+						<view class="coupon-ticket">
+							<image
+								class="ticket-bg"
+								src="/static/topUp/bg-coupon-border.png"
+								mode="scaleToFill"
+							></image>
+							<view class="ticket-left">
+								<text class="ticket-value" :class="{ multi: couponTab === 1 }">{{ item.display }}</text>
+							</view>
+							<view class="ticket-right">
+								<text class="cname">{{ item.name }}</text>
+								<text class="ccond" v-if="item.condition">{{ item.condition }}</text>
+								<view class="ticket-bottom">
+									<text class="cvalid">{{ item.validText }}</text>
+									<view class="stepper" @click.stop>
+										<view
+											class="step-btn minus"
+											:class="{ active: getCouponQty(item.id) > 0 }"
+											@click="changeCouponQty(item.id, -1)"
+										>
+											<text class="step-icon">−</text>
+										</view>
+										<text class="step-num">{{ getCouponQty(item.id) }}</text>
+										<view class="step-btn plus" @click="changeCouponQty(item.id, 1)">
+											<text class="step-icon">+</text>
+										</view>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+					<view class="empty" v-if="!currentCouponList.length && !couponLoading">暂无优惠券</view>
+					<view class="empty" v-if="couponLoading">加载中...</view>
+				</scroll-view>
+
+				<view class="footer coupon-footer">
+					<view class="save-btn" @click="confirmCouponSelect">
+						{{ tempCouponCount > 0 ? `确定选择(已选 ${tempCouponCount} 张)` : '确定选择' }}
+					</view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import prepaidApi from '@/api/memberPrepaid.js'
+
+	function createId() {
+		return `t_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
+	}
+
+	function formatValidText(beginTime, endTime) {
+		if (!beginTime && !endTime) return '长期有效';
+		const begin = beginTime ? String(beginTime).slice(0, 10) : '';
+		const end = endTime ? String(endTime).slice(0, 10) : '';
+		if (begin && end) return `${begin} 至 ${end}`;
+		return begin || end || '长期有效';
+	}
+
+	export default {
+		data() {
+			return {
+				loading: false,
+				saving: false,
+				configVersion: null,
+				form: {
+					acceptShare: true,
+					minAmount: '',
+					tiers: []
+				},
+				tierVisible: false,
+				tierEditIndex: -1,
+				tierForm: {
+					amount: '',
+					giftAmount: '',
+					coupons: []
+				},
+				couponVisible: false,
+				couponTab: 0,
+				couponTabs: ['折扣券', '满减券'],
+				tempCoupons: [],
+				discountCoupons: [],
+				reduceCoupons: [],
+				couponLoading: false
+			};
+		},
+		computed: {
+			hasGiftConfig() {
+				return (this.form.tiers || []).some((tier) => {
+					const gift = Number(tier.giftAmount) || 0;
+					const couponQty = (tier.coupons || []).reduce((sum, c) => sum + (Number(c.qty) || 0), 0);
+					return gift > 0 || couponQty > 0;
+				});
+			},
+			/** 固定档位中的最低充值金额,用作自定义最低金额默认值 */
+			lowestTierAmount() {
+				const amounts = (this.form.tiers || [])
+					.map((t) => Number(t.amount))
+					.filter((n) => !Number.isNaN(n) && n > 0);
+				if (!amounts.length) return 0;
+				return Math.min(...amounts);
+			},
+			canSubmitTier() {
+				const amount = String(this.tierForm.amount || '').trim();
+				return !!amount && /^\d+$/.test(amount) && Number(amount) > 0;
+			},
+			tierFormTitle() {
+				if (this.tierEditIndex === -1) {
+					return `档位${(this.form.tiers || []).length + 1}`;
+				}
+				return `档位${this.tierEditIndex + 1}`;
+			},
+			couponSelectedCount() {
+				return (this.tierForm.coupons || []).reduce((sum, c) => sum + (Number(c.qty) || 0), 0);
+			},
+			tempCouponCount() {
+				return (this.tempCoupons || []).reduce((sum, c) => sum + (Number(c.qty) || 0), 0);
+			},
+			currentCouponList() {
+				return this.couponTab === 0 ? this.discountCoupons : this.reduceCoupons;
+			}
+		},
+		onLoad() {
+			this.loadSettings();
+		},
+		methods: {
+			onMinAmountInput(e) {
+				let val = String((e && e.detail && e.detail.value) || this.form.minAmount || '');
+				// 允许小数:只保留数字和第一个小数点,最多两位小数
+				val = val.replace(/[^\d.]/g, '');
+				const parts = val.split('.');
+				if (parts.length > 1) {
+					val = parts[0] + '.' + parts.slice(1).join('').slice(0, 2);
+				}
+				this.$nextTick(() => {
+					this.form.minAmount = val;
+				});
+			},
+			onTierAmountInput(field, e) {
+				const val = String((e && e.detail && e.detail.value) || this.tierForm[field] || '')
+					.replace(/[^\d]/g, '');
+				this.$nextTick(() => {
+					this.$set(this.tierForm, field, val);
+				});
+			},
+			loadSettings() {
+				this.loading = true;
+				prepaidApi
+					.getRechargeConfig()
+					.then((res) => {
+						if (res.data.code !== 200 || !res.data.result) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const data = res.data.result;
+						this.configVersion = data.version;
+						const tiers = Array.isArray(data.tiers) ? data.tiers : [];
+						const mappedTiers = tiers.map((tier) => ({
+							id: tier.tierId || createId(),
+							amount: Number(tier.rechargeAmount) || 0,
+							giftAmount: Number(tier.giftAmount) || 0,
+							coupons: (tier.coupons || []).map((c) => ({
+								id: c.couponId,
+								name: c.couponName || '',
+								type: String(c.couponType) === '2' ? 'discount' : 'reduce',
+								qty: Number(c.quantity) || 1
+							}))
+						}));
+						const lowest = mappedTiers
+							.map((t) => Number(t.amount))
+							.filter((n) => n > 0);
+						const defaultMin = lowest.length ? Math.min(...lowest) : '';
+						this.form = {
+							acceptShare: data.acceptSharedPrepaid !== false,
+							minAmount:
+								data.customMinAmount != null && data.customMinAmount !== ''
+									? String(data.customMinAmount)
+									: defaultMin !== ''
+										? String(defaultMin)
+										: '',
+							tiers: mappedTiers
+						};
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.loading = false;
+					});
+			},
+			getCouponCount(item) {
+				return (item.coupons || []).reduce((sum, c) => sum + (Number(c.qty) || 0), 0);
+			},
+			openTierAdd() {
+				this.tierEditIndex = -1;
+				this.tierForm = { amount: '', giftAmount: '', coupons: [] };
+				this.tierVisible = true;
+			},
+			openTierEdit(index) {
+				const item = this.form.tiers[index];
+				this.tierEditIndex = index;
+				this.tierForm = {
+					amount: item.amount != null ? String(Math.floor(Number(item.amount)) || '') : '',
+					giftAmount:
+						item.giftAmount != null ? String(Math.floor(Number(item.giftAmount)) || 0) : '',
+					coupons: JSON.parse(JSON.stringify(item.coupons || []))
+				};
+				this.tierVisible = true;
+			},
+			closeTier() {
+				this.tierVisible = false;
+			},
+			removeTier(index) {
+				if ((this.form.tiers || []).length <= 2) {
+					uni.showToast({ title: '至少保留2个固定充值档位', icon: 'none' });
+					return;
+				}
+				uni.showModal({
+					title: '提示',
+					content: '确认删除该充值档位吗?',
+					confirmColor: '#fa3534',
+					success: (res) => {
+						if (res.confirm) {
+							this.form.tiers.splice(index, 1);
+						}
+					}
+				});
+			},
+			submitTier() {
+				if (!this.canSubmitTier) return;
+				const amountStr = String(this.tierForm.amount || '').trim();
+				if (!/^\d+$/.test(amountStr) || Number(amountStr) <= 0) {
+					uni.showToast({ title: '充值金额须为正整数', icon: 'none' });
+					return;
+				}
+				const amount = Number(amountStr);
+				const giftRaw = String(this.tierForm.giftAmount || '').trim();
+				if (giftRaw !== '' && !/^\d+$/.test(giftRaw)) {
+					uni.showToast({ title: '赠送金额须为非负整数', icon: 'none' });
+					return;
+				}
+				const giftAmount = giftRaw === '' ? 0 : Number(giftRaw);
+				const payload = {
+					id: this.tierEditIndex === -1 ? createId() : this.form.tiers[this.tierEditIndex].id,
+					amount,
+					giftAmount,
+					coupons: JSON.parse(JSON.stringify(this.tierForm.coupons || []))
+				};
+				if (this.tierEditIndex === -1) {
+					this.form.tiers.push(payload);
+				} else {
+					this.$set(this.form.tiers, this.tierEditIndex, payload);
+				}
+				this.tierVisible = false;
+			},
+			mapCouponItem(item) {
+				const type = String(item.couponType);
+				const isDiscount = type === '2';
+				return {
+					id: item.couponId,
+					name: item.couponName || '',
+					display: item.contentInfo || (isDiscount ? '折扣' : '优惠'),
+					condition: item.contentConditions || '',
+					validText: formatValidText(item.beginTime, item.endTime),
+					remainingQuantity: Number(item.remainingQuantity) || 0
+				};
+			},
+			switchCouponTab(idx) {
+				this.couponTab = idx;
+				this.loadCoupons();
+			},
+			openCouponSelect() {
+				this.tempCoupons = JSON.parse(JSON.stringify(this.tierForm.coupons || []));
+				this.couponTab = 0;
+				this.couponVisible = true;
+				this.loadCoupons();
+			},
+			closeCouponSelect() {
+				this.couponVisible = false;
+			},
+			loadCoupons() {
+				this.couponLoading = true;
+				// couponType: 1满减 2折扣;tabs: 0折扣 1满减
+				const couponType = this.couponTab === 0 ? '2' : '1';
+				prepaidApi
+					.getCouponPage({ couponType, pageNo: 1, pageSize: 100 })
+					.then((res) => {
+						if (res.data.code !== 200) {
+							uni.showToast({ title: res.data.message || '优惠券加载失败', icon: 'none' });
+							return;
+						}
+						const page = res.data.result || {};
+						const records = Array.isArray(page.records) ? page.records : [];
+						const list = records.map((item) => this.mapCouponItem(item));
+						if (this.couponTab === 0) {
+							this.discountCoupons = list;
+						} else {
+							this.reduceCoupons = list;
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.couponLoading = false;
+					});
+			},
+			isCouponChecked(id) {
+				return this.tempCoupons.some((c) => c.id === id && Number(c.qty) > 0);
+			},
+			getCouponQty(id) {
+				const found = this.tempCoupons.find((c) => c.id === id);
+				return found ? Number(found.qty) || 0 : 0;
+			},
+			findCouponMeta(id) {
+				return (
+					this.discountCoupons.find((c) => c.id === id) ||
+					this.reduceCoupons.find((c) => c.id === id) ||
+					null
+				);
+			},
+			toggleCoupon(item) {
+				const idx = this.tempCoupons.findIndex((c) => c.id === item.id);
+				if (idx >= 0 && Number(this.tempCoupons[idx].qty) > 0) {
+					this.tempCoupons.splice(idx, 1);
+					return;
+				}
+				if (idx >= 0) {
+					this.$set(this.tempCoupons[idx], 'qty', 1);
+				} else {
+					this.tempCoupons.push({
+						id: item.id,
+						name: item.name,
+						type: this.couponTab === 0 ? 'discount' : 'reduce',
+						qty: 1
+					});
+				}
+			},
+			changeCouponQty(id, delta) {
+				const idx = this.tempCoupons.findIndex((c) => c.id === id);
+				const meta = this.findCouponMeta(id);
+				if (idx < 0) {
+					if (delta > 0 && meta) {
+						this.tempCoupons.push({
+							id,
+							name: meta.name,
+							type: this.couponTab === 0 ? 'discount' : 'reduce',
+							qty: 1
+						});
+					}
+					return;
+				}
+				const next = Math.max(0, (Number(this.tempCoupons[idx].qty) || 0) + delta);
+				if (next === 0) {
+					this.tempCoupons.splice(idx, 1);
+				} else {
+					this.$set(this.tempCoupons[idx], 'qty', next);
+				}
+			},
+			confirmCouponSelect() {
+				this.tierForm.coupons = this.tempCoupons
+					.filter((c) => Number(c.qty) > 0)
+					.map((c) => ({ ...c, qty: Number(c.qty) }));
+				this.couponVisible = false;
+			},
+			onSave() {
+				if (this.saving) return;
+				if (this.form.tiers.length < 2) {
+					uni.showToast({ title: '至少配置2个固定充值档位', icon: 'none' });
+					return;
+				}
+				const amounts = this.form.tiers.map((t) => Number(t.amount) || 0);
+				for (let i = 0; i < amounts.length; i++) {
+					if (!Number.isInteger(amounts[i]) || amounts[i] <= 0) {
+						uni.showToast({ title: '档位充值金额须为正整数', icon: 'none' });
+						return;
+					}
+				}
+				for (let i = 1; i < amounts.length; i++) {
+					if (amounts[i] <= amounts[i - 1]) {
+						uni.showToast({ title: '档位充值金额须严格递增', icon: 'none' });
+						return;
+					}
+				}
+				// 有赠送余额/优惠券时接口要求关闭自定义充值
+				const customAmountEnabled = !this.hasGiftConfig;
+				const minRaw = String(this.form.minAmount || '').trim();
+				const lowest = this.lowestTierAmount;
+				let customMinAmount = lowest || 0;
+				if (minRaw !== '') {
+					if (!/^\d+(\.\d{1,2})?$/.test(minRaw) || Number(minRaw) <= 0) {
+						uni.showToast({ title: '自定义最低金额须大于0,最多两位小数', icon: 'none' });
+						return;
+					}
+					customMinAmount = Number(minRaw);
+					if (lowest && customMinAmount < lowest) {
+						uni.showToast({
+							title: `自定义最低金额不能低于最低档位(${lowest})`,
+							icon: 'none'
+						});
+						return;
+					}
+				} else if (!lowest) {
+					uni.showToast({ title: '请配置固定充值档位', icon: 'none' });
+					return;
+				}
+				const payload = {
+					acceptSharedPrepaid: !!this.form.acceptShare,
+					customAmountEnabled,
+					customMinAmount,
+					tiers: this.form.tiers.map((tier) => ({
+						rechargeAmount: Number(tier.amount),
+						giftAmount: Number(tier.giftAmount) || 0,
+						coupons: (tier.coupons || [])
+							.filter((c) => Number(c.qty) > 0)
+							.map((c) => ({
+								couponId: c.id,
+								quantity: Number(c.qty)
+							}))
+					}))
+				};
+				if (this.configVersion != null) {
+					payload.version = this.configVersion;
+				}
+				this.saving = true;
+				prepaidApi
+					.saveRechargeConfig(payload)
+					.then((res) => {
+						if (res.data.code === 200) {
+							uni.showToast({ title: '保存成功', icon: 'none' });
+							setTimeout(() => {
+								uni.navigateBack();
+							}, 500);
+						} else {
+							uni.showToast({ title: res.data.message || '保存失败', icon: 'none' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.saving = false;
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f5f5f5;
+		padding: 24rpx;
+		padding-bottom: calc(160rpx + env(safe-area-inset-bottom));
+		box-sizing: border-box;
+	}
+
+	.card {
+		background: #fff;
+		border-radius: 16rpx;
+		margin-bottom: 24rpx;
+	}
+
+	.switch-card {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 28rpx 28rpx;
+
+		.label {
+			font-size: 28rpx;
+			color: #333;
+			flex: 1;
+			padding-right: 24rpx;
+		}
+	}
+
+	.min-card {
+		padding: 28rpx 28rpx 32rpx;
+
+		.min-head {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-bottom: 28rpx;
+
+			.label {
+				font-size: 28rpx;
+				color: #333;
+			}
+
+			.min-tip {
+				font-size: 22rpx;
+				color: #999;
+			}
+		}
+
+		.min-input-wrap {
+			display: flex;
+			align-items: baseline;
+			padding-bottom: 16rpx;
+			border-bottom: 1px solid #e8e8e8;
+
+			.currency {
+				font-size: 40rpx;
+				color: #999;
+				margin-right: 8rpx;
+				line-height: 1;
+			}
+
+			.min-input {
+				flex: 1;
+				font-size: 40rpx;
+				color: #333;
+				line-height: 1.2;
+			}
+		}
+	}
+
+	.placeholder {
+		color: #bbb;
+		font-size: 40rpx;
+	}
+
+	.section-head {
+		margin: 8rpx 0 20rpx;
+		padding: 0 4rpx;
+
+		.section-title-row {
+			display: flex;
+			align-items: center;
+			margin-bottom: 10rpx;
+		}
+
+		.section-bar {
+			width: 8rpx;
+			height: 28rpx;
+			border-radius: 4rpx;
+			background: #1c7cf9;
+			margin-right: 12rpx;
+			flex-shrink: 0;
+		}
+
+		.section-title {
+			font-size: 30rpx;
+			font-weight: 600;
+			color: #333;
+		}
+
+		.section-tip {
+			font-size: 22rpx;
+			color: #999;
+			padding-left: 20rpx;
+		}
+	}
+
+	.tier-list {
+		padding-bottom: 40rpx;
+	}
+
+	.tier-card {
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 28rpx;
+		margin-bottom: 20rpx;
+
+		.tier-top {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-bottom: 8rpx;
+
+			.tier-name {
+				font-size: 30rpx;
+				font-weight: 600;
+				color: #333;
+			}
+
+			.tier-ops {
+				display: flex;
+				align-items: center;
+				gap: 28rpx;
+
+				.op {
+					padding: 4rpx;
+				}
+			}
+		}
+
+		.tier-line {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 18rpx 0;
+			border-bottom: 1px solid #f2f2f2;
+			font-size: 26rpx;
+
+			&:last-child {
+				border-bottom: none;
+				padding-bottom: 0;
+			}
+
+			.k {
+				color: #999;
+			}
+
+			.v {
+				color: #333;
+			}
+		}
+	}
+
+	.empty {
+		padding: 80rpx 0;
+		text-align: center;
+		color: #ccc;
+		font-size: 26rpx;
+	}
+
+	.fab {
+		position: fixed;
+		right: 40rpx;
+		bottom: calc(170rpx + env(safe-area-inset-bottom));
+		width: 60rpx;
+		height: 60rpx;
+		z-index: 20;
+	}
+
+	.footer {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
+		background: transparent;
+		z-index: 30;
+
+		.save-btn {
+			height: 88rpx;
+			line-height: 88rpx;
+			text-align: center;
+			border-radius: 44rpx;
+			background: #1c7cf9;
+			color: #fff;
+			font-size: 32rpx;
+			font-weight: 500;
+
+			&.disabled {
+				background: #c0c4cc;
+			}
+		}
+
+		&.sub-footer {
+			position: relative;
+			z-index: 1;
+			flex-shrink: 0;
+			background: #fff;
+			box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
+
+			.save-btn {
+				border-radius: 12rpx;
+				background: #2979ff;
+			}
+		}
+
+		&.tier-footer,
+		&.coupon-footer {
+			position: fixed;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			padding: 20rpx 40rpx calc(20rpx + env(safe-area-inset-bottom));
+			background: transparent;
+			box-shadow: none;
+
+			.save-btn {
+				border-radius: 44rpx;
+				background: linear-gradient(90deg, #4da3ff 0%, #1c7cf9 100%);
+
+				&.disabled {
+					background: linear-gradient(90deg, rgba(77, 163, 255, 0.35) 0%, rgba(28, 124, 249, 0.35) 100%);
+					color: rgba(255, 255, 255, 0.9);
+				}
+			}
+		}
+	}
+
+	.sub-nav {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		height: 88rpx;
+		padding-top: var(--status-bar-height);
+		background: #fff;
+		border-bottom: 1px solid #f0f0f0;
+		box-sizing: content-box;
+
+		.sub-nav-back {
+			position: absolute;
+			left: 24rpx;
+			bottom: 0;
+			height: 88rpx;
+			display: flex;
+			align-items: center;
+			padding: 0 12rpx;
+		}
+
+		.sub-nav-title {
+			font-size: 32rpx;
+			color: #333;
+			font-weight: 500;
+			line-height: 88rpx;
+		}
+	}
+
+	.sub-page {
+		width: 100vw;
+		height: 100vh;
+		background: #f5f5f5;
+		display: flex;
+		flex-direction: column;
+		box-sizing: border-box;
+		padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
+	}
+
+	.tier-page {
+		.form-card {
+			margin-top: 24rpx;
+		}
+	}
+
+	.form-card {
+		margin: 24rpx;
+		background: #fff;
+		border-radius: 16rpx;
+		overflow: hidden;
+		flex-shrink: 0;
+
+		.form-title {
+			padding: 28rpx 28rpx 8rpx;
+			font-size: 30rpx;
+			font-weight: 600;
+			color: #333;
+		}
+
+		.form-row {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 28rpx;
+			border-bottom: 1px solid #f2f2f2;
+
+			&:last-child {
+				border-bottom: none;
+			}
+		}
+
+		.form-label {
+			font-size: 28rpx;
+			color: #333;
+			width: 200rpx;
+			flex-shrink: 0;
+		}
+
+		.form-input {
+			flex: 1;
+			text-align: right;
+			font-size: 28rpx;
+			color: #333;
+		}
+
+		.form-placeholder {
+			color: #c0c4cc;
+			font-size: 28rpx;
+		}
+
+		.link-row {
+			.link-right {
+				display: flex;
+				align-items: center;
+				gap: 8rpx;
+			}
+
+			.link-text {
+				font-size: 28rpx;
+				color: #c0c4cc;
+
+				&.active {
+					color: #333;
+				}
+			}
+		}
+	}
+
+	.coupon-page {
+		$coupon-orange: #ff5f3e;
+		$ticket-left-w: 28%;
+
+		.tabs {
+			display: flex;
+			background: #fff;
+			border-bottom: 1px solid #f0f0f0;
+			flex-shrink: 0;
+			padding-bottom: 4rpx;
+
+			.tab {
+				flex: 1;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				height: 88rpx;
+				position: relative;
+
+				.tab-text {
+					font-size: 28rpx;
+					color: #999;
+					line-height: 1.2;
+				}
+
+				.tab-indicator {
+					position: absolute;
+					left: 50%;
+					bottom: 10rpx;
+					transform: translateX(-50%);
+					width: 48rpx;
+					height: 12rpx;
+				}
+
+				&.active {
+					.tab-text {
+						color: #1c7cf9;
+						font-weight: 600;
+					}
+				}
+			}
+		}
+
+		.coupon-scroll {
+			flex: 1;
+			height: 0;
+			padding: 24rpx;
+			box-sizing: border-box;
+		}
+
+		.coupon-item {
+			display: flex;
+			align-items: center;
+			background: #fff;
+			border-radius: 16rpx;
+			padding: 24rpx 20rpx;
+			margin-bottom: 20rpx;
+			gap: 18rpx;
+		}
+
+		.radio {
+			width: 34rpx;
+			height: 34rpx;
+			border-radius: 50%;
+			border: 2rpx solid #c8c8c8;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			flex-shrink: 0;
+			box-sizing: border-box;
+
+			&.checked {
+				background: #1c7cf9;
+				border-color: #1c7cf9;
+			}
+		}
+
+		.coupon-ticket {
+			position: relative;
+			flex: 1;
+			min-width: 0;
+			display: flex;
+			align-items: stretch;
+			min-height: 180rpx;
+			box-sizing: border-box;
+		}
+
+		.ticket-bg {
+			position: absolute;
+			left: 0;
+			top: 0;
+			width: 100%;
+			height: 100%;
+			z-index: 0;
+			pointer-events: none;
+		}
+
+		.ticket-left {
+			position: relative;
+			z-index: 1;
+			width: $ticket-left-w;
+			flex-shrink: 0;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding: 24rpx 16rpx 24rpx 20rpx;
+			box-sizing: border-box;
+		}
+
+		.ticket-value {
+			font-size: 44rpx;
+			font-weight: 700;
+			color: $coupon-orange;
+			text-align: center;
+			line-height: 1.2;
+			word-break: break-all;
+
+			&.multi {
+				font-size: 26rpx;
+				line-height: 1.35;
+			}
+		}
+
+		.ticket-right {
+			position: relative;
+			z-index: 1;
+			flex: 1;
+			min-width: 0;
+			padding: 22rpx 24rpx 18rpx 28rpx;
+			display: flex;
+			flex-direction: column;
+			box-sizing: border-box;
+		}
+
+		.cname {
+			font-size: 28rpx;
+			font-weight: 600;
+			color: #333;
+			margin-bottom: 8rpx;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+			line-height: 1.3;
+		}
+
+		.ccond {
+			font-size: 24rpx;
+			color: $coupon-orange;
+			line-height: 1.4;
+			margin-bottom: 8rpx;
+		}
+
+		.ticket-bottom {
+			margin-top: auto;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 12rpx;
+		}
+
+		.cvalid {
+			flex: 1;
+			min-width: 0;
+			font-size: 22rpx;
+			color: #999;
+			line-height: 1.4;
+			overflow: hidden;
+			text-overflow: ellipsis;
+			white-space: nowrap;
+		}
+
+		.stepper {
+			display: flex;
+			align-items: center;
+			flex-shrink: 0;
+			gap: 12rpx;
+
+			.step-btn {
+				width: 36rpx;
+				height: 36rpx;
+				border-radius: 6rpx;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				box-sizing: border-box;
+
+				.step-icon {
+					font-size: 26rpx;
+					line-height: 1;
+					color: #fff;
+					font-weight: 500;
+				}
+
+				&.minus {
+					background: #d0d0d0;
+
+					&.active {
+						background: $coupon-orange;
+					}
+				}
+
+				&.plus {
+					background: $coupon-orange;
+				}
+			}
+
+			.step-num {
+				min-width: 28rpx;
+				text-align: center;
+				font-size: 28rpx;
+				color: #333;
+				line-height: 36rpx;
+			}
+		}
+	}
+</style>

+ 428 - 0
pages/topUp/shareStore.vue

@@ -0,0 +1,428 @@
+<template>
+	<view class="page">
+		<u-navbar title="共享门店" :autoBack="true" :placeholder="true"></u-navbar>
+		<view class="list" v-if="storeList.length">
+			<view class="store-card" v-for="item in storeList" :key="item.id">
+				<view class="del-btn" @click.stop="onDelete(item)">
+					<image class="del-bg" src="/static/topUp/del.png" mode="scaleToFill"></image>
+					<text class="del-text">删除</text>
+				</view>
+				<image class="logo" :src="item.logo || defaultLogo" mode="aspectFill"></image>
+				<view class="info">
+					<view class="name u-line-1">{{ item.name }}</view>
+					<view class="meta">
+						<view class="stars">
+							<u-icon
+								v-for="n in 5"
+								:key="n"
+								name="heart-fill"
+								:color="n <= Math.round(item.rating) ? '#FF4D4F' : '#E5E5E5'"
+								size="20"
+							></u-icon>
+						</view>
+						<text class="meta-text">{{ formatRating(item) }}</text>
+					</view>
+					<view class="addr u-line-1">{{ item.address || '暂无地址' }}</view>
+					<view class="distance" v-if="item.distance">距您{{ item.distance }}</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="empty-wrap" v-else-if="loading">
+			<u-loading mode="circle" size="48"></u-loading>
+			<text class="empty-text">加载中...</text>
+		</view>
+		<view class="empty-wrap" v-else>
+			<u-empty text="暂无数据" mode="list"></u-empty>
+		</view>
+
+		<view class="footer">
+			<view class="add-btn" :class="{ disabled: !canAdd }" @click="goAdd">添加</view>
+		</view>
+
+		<!-- 删除确认弹窗 -->
+		<u-popup v-model="confirmVisible" mode="center" border-radius="24" :mask-close-able="false">
+			<view class="confirm-dialog">
+				<view class="confirm-title">提示</view>
+				<view class="confirm-body">
+					<text class="confirm-text">是否确认删除该共享门店?</text>
+				</view>
+				<view class="confirm-footer">
+					<view class="btn cancel" @click="onConfirmCancel">取消</view>
+					<view class="btn ok" @click="confirmDelete">确定</view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantId, getMerchantLocation, formatStoreDistance } from '@/api/memberPrepaid.js'
+
+	const DEFAULT_LOGO = '/static/index/shop.png'
+
+	export default {
+		data() {
+			return {
+				defaultLogo: DEFAULT_LOGO,
+				canAdd: false,
+				loading: false,
+				storeList: [],
+				confirmVisible: false,
+				pendingDelete: null,
+				deleting: false,
+				longitude: null,
+				latitude: null
+			};
+		},
+		onShow() {
+			this.confirmVisible = false;
+			this.pendingDelete = null;
+			this.refreshEnabled();
+			this.loadList();
+		},
+		methods: {
+			formatRating(item) {
+				const score = Number(item.rating) || 0;
+				const count = item.reviewCount;
+				let countText = '';
+				if (count === undefined || count === null || count === '') {
+					countText = '暂无评论';
+				} else if (typeof count === 'string') {
+					countText = /评价|评论/.test(count) ? count : `${count}条评论`;
+				} else {
+					countText = `${count}条评论`;
+				}
+				return `${score.toFixed(1)}分 | ${countText}`;
+			},
+			mapStore(item) {
+				return {
+					id: item.merchantId || item.id,
+					name: item.storeName || item.name || '',
+					logo: item.headPhoto || item.logo || DEFAULT_LOGO,
+					address: item.address || '',
+					rating: Number(item.grade || item.rating || 0),
+					reviewCount: item.commentNum ?? item.reviewCount ?? '',
+					distance: formatStoreDistance(item)
+				};
+			},
+			parseRecords(result) {
+				if (!result) return [];
+				if (Array.isArray(result)) return result;
+				if (Array.isArray(result.records)) return result.records;
+				if (result.page && Array.isArray(result.page.records)) return result.page.records;
+				return [];
+			},
+			refreshEnabled() {
+				const merchantId = getMerchantId();
+				if (!merchantId) {
+					this.canAdd = false;
+					return;
+				}
+				prepaidApi.getFeatureStatus(merchantId).then((res) => {
+					if (res.data.code !== 200) return;
+					const data = res.data.result || {};
+					this.canAdd = Number(data.auditStatus) === 2 && Number(data.operationStatus) === 1;
+				});
+			},
+			ensureLocation() {
+				if (this.longitude != null && this.latitude != null) {
+					return Promise.resolve({
+						longitude: this.longitude,
+						latitude: this.latitude
+					});
+				}
+				return getMerchantLocation().then((loc) => {
+					this.longitude = loc.longitude;
+					this.latitude = loc.latitude;
+					return loc;
+				});
+			},
+			loadList() {
+				this.loading = true;
+				this.ensureLocation()
+					.then((loc) =>
+						prepaidApi.getSharedStores({
+							pageNo: 1,
+							pageSize: 100,
+							longitude: loc.longitude,
+							latitude: loc.latitude
+						})
+					)
+					.then((res) => {
+						if (res.data.code !== 200) {
+							this.storeList = [];
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const records = this.parseRecords(res.data.result);
+						this.storeList = records.map((item) => this.mapStore(item));
+					})
+					.catch((err) => {
+						this.storeList = [];
+						uni.showToast({
+							title: (err && err.message) || '加载失败,请稍后重试',
+							icon: 'none'
+						});
+					})
+					.then(() => {
+						this.loading = false;
+					});
+			},
+			goAdd() {
+				if (!this.canAdd) {
+					uni.showToast({ title: '请先开启储值功能', icon: 'none' });
+					return;
+				}
+				uni.navigateTo({ url: '/pages/topUp/shareStoreAdd' });
+			},
+			onDelete(item) {
+				this.pendingDelete = item;
+				this.confirmVisible = true;
+			},
+			onConfirmCancel() {
+				this.confirmVisible = false;
+				this.pendingDelete = null;
+			},
+			confirmDelete() {
+				const item = this.pendingDelete;
+				this.confirmVisible = false;
+				this.pendingDelete = null;
+				if (!item || this.deleting) return;
+
+				this.deleting = true;
+				prepaidApi
+					.removeSharedStore(item.id)
+					.then((res) => {
+						if (res.data.code === 200) {
+							uni.showToast({ title: '已删除共享', icon: 'none' });
+							this.loadList();
+						} else {
+							uni.showToast({ title: res.data.message || '删除失败', icon: 'none' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.then(() => {
+						this.deleting = false;
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+		padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
+		box-sizing: border-box;
+	}
+
+	.list {
+		padding: 24rpx;
+	}
+
+	.store-card {
+		position: relative;
+		display: flex;
+		align-items: flex-start;
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		margin-bottom: 20rpx;
+		overflow: hidden;
+
+		.del-btn {
+			position: absolute;
+			top: 0;
+			right: 0;
+			width: 96rpx;
+			height: 44rpx;
+			z-index: 2;
+
+			.del-bg {
+				position: absolute;
+				left: 0;
+				top: 0;
+				width: 100%;
+				height: 100%;
+			}
+
+			.del-text {
+				position: relative;
+				z-index: 1;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				width: 100%;
+				height: 100%;
+				font-size: 22rpx;
+				color: #1C7CF9;
+				line-height: 1;
+				padding-left: 8rpx;
+				box-sizing: border-box;
+			}
+		}
+
+		.logo {
+			width: 120rpx;
+			height: 120rpx;
+			border-radius: 12rpx;
+			background: #f0f0f0;
+			flex-shrink: 0;
+			margin-right: 20rpx;
+		}
+
+		.info {
+			flex: 1;
+			min-width: 0;
+			padding-right: 80rpx;
+			position: relative;
+
+			.name {
+				font-size: 30rpx;
+				font-weight: 600;
+				color: #333;
+				line-height: 1.35;
+				margin-bottom: 10rpx;
+			}
+
+			.meta {
+				display: flex;
+				align-items: center;
+				gap: 8rpx;
+				margin-bottom: 10rpx;
+
+				.stars {
+					display: flex;
+					align-items: center;
+					gap: 2rpx;
+				}
+
+				.meta-text {
+					font-size: 22rpx;
+					color: #FF4D4F;
+					line-height: 1.2;
+				}
+			}
+
+			.addr {
+				font-size: 22rpx;
+				color: #999;
+				line-height: 1.4;
+				padding-right: 120rpx;
+			}
+
+			.distance {
+				position: absolute;
+				right: 0;
+				bottom: 0;
+				font-size: 22rpx;
+				color: #666;
+				line-height: 1.4;
+			}
+		}
+	}
+
+	.empty-wrap {
+		padding-top: 200rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		gap: 16rpx;
+
+		.empty-text {
+			font-size: 26rpx;
+			color: #999;
+		}
+	}
+
+	.footer {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom));
+		background: #fff;
+		box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
+		z-index: 20;
+
+		.add-btn {
+			height: 88rpx;
+			line-height: 88rpx;
+			text-align: center;
+			background: linear-gradient(269deg, #2260F6 0%, #0FB0FF 100%);
+			border-radius: 160rpx;
+			color: #fff;
+			font-size: 32rpx;
+			font-weight: 500;
+
+			&.disabled {
+				background: #B7D4FF;
+				color: #fff;
+			}
+		}
+	}
+
+	.confirm-dialog {
+		width: 560rpx;
+		background: #fff;
+		border-radius: 24rpx;
+		overflow: hidden;
+		padding: 48rpx 40rpx 40rpx;
+		box-sizing: border-box;
+
+		.confirm-title {
+			text-align: center;
+			font-size: 34rpx;
+			font-weight: 600;
+			color: #333;
+			line-height: 1.4;
+			margin-bottom: 28rpx;
+		}
+
+		.confirm-body {
+			padding: 0 8rpx 40rpx;
+
+			.confirm-text {
+				display: block;
+				text-align: center;
+				font-size: 28rpx;
+				color: #333;
+				line-height: 1.6;
+			}
+		}
+
+		.confirm-footer {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			gap: 24rpx;
+
+			.btn {
+				flex: 1;
+				height: 80rpx;
+				line-height: 80rpx;
+				text-align: center;
+				border-radius: 40rpx;
+				font-size: 30rpx;
+				box-sizing: border-box;
+			}
+
+			.cancel {
+				color: #999;
+				background: #fff;
+				border: 2rpx solid #dcdcdc;
+			}
+
+			.ok {
+				color: #fff;
+				background: linear-gradient(90deg, #3ab3ff 0%, #2472ff 100%);
+				border: none;
+			}
+		}
+	}
+</style>

+ 383 - 0
pages/topUp/shareStoreAdd.vue

@@ -0,0 +1,383 @@
+<template>
+	<view class="page">
+		<u-navbar title="共享门店" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<!-- 可选门店列表 -->
+		<view class="list" v-if="candidateList.length">
+			<view
+				class="store-card"
+				v-for="item in candidateList"
+				:key="item.id"
+				@click="toggleSelect(item)"
+			>
+				<view class="checkbox" :class="{ checked: isSelected(item.id) }">
+					<u-icon v-if="isSelected(item.id)" name="checkmark" color="#fff" size="14"></u-icon>
+				</view>
+				<image class="logo" :src="item.logo || defaultLogo" mode="aspectFill"></image>
+				<view class="info">
+					<view class="name u-line-1">{{ item.name }}</view>
+					<view class="meta">
+						<view class="stars">
+							<u-icon
+								v-for="n in 5"
+								:key="n"
+								name="heart-fill"
+								:color="n <= Math.round(item.rating) ? '#FF4D4F' : '#E5E5E5'"
+								size="20"
+							></u-icon>
+						</view>
+						<text class="meta-text">{{ formatRating(item) }}</text>
+					</view>
+					<view class="addr-row">
+						<text class="addr u-line-1">{{ item.address || '暂无地址' }}</text>
+						<text class="distance" v-if="item.distance">距您{{ item.distance }}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="empty-wrap" v-else-if="!loading">
+			<u-empty text="暂无可添加门店" mode="list"></u-empty>
+		</view>
+		<view class="empty-wrap" v-else>
+			<u-loading mode="circle" size="48"></u-loading>
+		</view>
+
+		<!-- 数据说明 -->
+		<view class="tips-box" v-if="candidateList.length || !loading">
+			<view class="tips-title">数据说明</view>
+			<view class="tips-content" :class="{ collapsed: !tipsExpanded }">
+				<text class="tips-text">{{ tipsDisplayText }}</text>
+			</view>
+			<view class="tips-toggle" @click="tipsExpanded = !tipsExpanded">
+				<text>{{ tipsExpanded ? '收起' : '展开更多' }}</text>
+				<u-icon :name="tipsExpanded ? 'arrow-up' : 'arrow-down'" color="#1C7CF9" size="12"></u-icon>
+			</view>
+		</view>
+
+		<view class="footer">
+			<view
+				class="confirm-btn"
+				:class="{ disabled: !selectedIds.length || submitting }"
+				@click="onConfirm"
+			>
+				{{ submitting ? '提交中...' : '确认并添加' }}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantLocation, formatStoreDistance } from '@/api/memberPrepaid.js'
+
+	const DEFAULT_LOGO = '/static/index/shop.png'
+
+	const TIPS_SHORT =
+		'1、共享门店由对方商户自主开启权限控制,未开启权限的门店无法添加;若对方后续关闭权限,该门店将自动从共享列表中移除。'
+	const TIPS_FULL =
+		'1、共享门店由对方商户自主开启权限控制,未开启权限的门店无法添加;若对方后续关闭权限,该门店将自动从共享列表中移除。\n' +
+		'2、添加共享后,储值本金可在所选门店通用消费;充值赠送的优惠券仅限原充值门店使用,不可跨店核销。\n' +
+		'3、勾选门店并点击「确认并添加」后生效;结算时优先扣除共享储值余额。可随时取消共享,不影响已产生的订单。'
+
+	export default {
+		data() {
+			return {
+				defaultLogo: DEFAULT_LOGO,
+				tipsExpanded: false,
+				candidateList: [],
+				selectedIds: [],
+				loading: false,
+				submitting: false,
+				longitude: null,
+				latitude: null
+			};
+		},
+		computed: {
+			tipsDisplayText() {
+				return this.tipsExpanded ? TIPS_FULL : TIPS_SHORT;
+			}
+		},
+		onLoad() {
+			this.loadCandidates();
+		},
+		methods: {
+			formatRating(item) {
+				const score = Number(item.rating) || 0;
+				const count = item.reviewCount;
+				let countText = '';
+				if (count === undefined || count === null || count === '') {
+					countText = '暂无评论';
+				} else if (typeof count === 'string') {
+					countText = /评价|评论/.test(count) ? count : `${count}条评论`;
+				} else {
+					countText = `${count}条评论`;
+				}
+				return `${score.toFixed(1)}分 | ${countText}`;
+			},
+			mapStore(item) {
+				return {
+					id: item.merchantId || item.id,
+					name: item.storeName || item.name || '',
+					logo: item.headPhoto || item.logo || DEFAULT_LOGO,
+					address: item.address || '',
+					rating: Number(item.grade || item.rating || 0),
+					reviewCount: item.commentNum ?? item.reviewCount ?? '',
+					distance: formatStoreDistance(item)
+				};
+			},
+			ensureLocation() {
+				if (this.longitude != null && this.latitude != null) {
+					return Promise.resolve({
+						longitude: this.longitude,
+						latitude: this.latitude
+					});
+				}
+				return getMerchantLocation().then((loc) => {
+					this.longitude = loc.longitude;
+					this.latitude = loc.latitude;
+					return loc;
+				});
+			},
+			loadCandidates() {
+				this.loading = true;
+				this.ensureLocation()
+					.then((loc) =>
+						prepaidApi.getShareableStores({
+							pageNo: 1,
+							pageSize: 100,
+							longitude: loc.longitude,
+							latitude: loc.latitude
+						})
+					)
+					.then((res) => {
+						if (res.data.code !== 200) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const page = res.data.result || {};
+						const records = Array.isArray(page.records) ? page.records : [];
+						this.candidateList = records.map((item) => this.mapStore(item));
+						this.selectedIds = [];
+					})
+					.catch((err) => {
+						this.candidateList = [];
+						uni.showToast({
+							title: (err && err.message) || '加载失败,请稍后重试',
+							icon: 'none'
+						});
+					})
+					.finally(() => {
+						this.loading = false;
+					});
+			},
+			isSelected(id) {
+				return this.selectedIds.includes(id);
+			},
+			toggleSelect(item) {
+				const idx = this.selectedIds.indexOf(item.id);
+				if (idx >= 0) {
+					this.selectedIds.splice(idx, 1);
+				} else {
+					this.selectedIds.push(item.id);
+				}
+			},
+			onConfirm() {
+				if (!this.selectedIds.length || this.submitting) return;
+
+				this.submitting = true;
+				prepaidApi
+					.addSharedStores(this.selectedIds.slice())
+					.then((res) => {
+						if (res.data.code === 200) {
+							uni.showToast({ title: '已添加共享门店', icon: 'none' });
+							setTimeout(() => {
+								uni.navigateBack();
+							}, 400);
+						} else {
+							uni.showToast({ title: res.data.message || '添加失败', icon: 'none' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.submitting = false;
+					});
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+		padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
+		box-sizing: border-box;
+	}
+
+	.list {
+		padding: 24rpx 24rpx 0;
+	}
+
+	.store-card {
+		display: flex;
+		align-items: center;
+		background: #fff;
+		border-radius: 16rpx;
+		padding: 24rpx;
+		margin-bottom: 20rpx;
+
+		.checkbox {
+			width: 36rpx;
+			height: 36rpx;
+			border-radius: 50%;
+			border: 2rpx solid #ccc;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			flex-shrink: 0;
+			margin-right: 16rpx;
+			box-sizing: border-box;
+
+			&.checked {
+				background: #1C7CF9;
+				border-color: #1C7CF9;
+			}
+		}
+
+		.logo {
+			width: 120rpx;
+			height: 120rpx;
+			border-radius: 12rpx;
+			background: #f0f0f0;
+			flex-shrink: 0;
+			margin-right: 20rpx;
+		}
+
+		.info {
+			flex: 1;
+			min-width: 0;
+
+			.name {
+				font-size: 30rpx;
+				font-weight: 600;
+				color: #333;
+				line-height: 1.35;
+				margin-bottom: 10rpx;
+			}
+
+			.meta {
+				display: flex;
+				align-items: center;
+				gap: 8rpx;
+				margin-bottom: 10rpx;
+
+				.stars {
+					display: flex;
+					align-items: center;
+					gap: 2rpx;
+				}
+
+				.meta-text {
+					font-size: 22rpx;
+					color: #FF4D4F;
+					line-height: 1.2;
+				}
+			}
+
+			.addr-row {
+				display: flex;
+				align-items: center;
+				gap: 12rpx;
+
+				.addr {
+					flex: 1;
+					min-width: 0;
+					font-size: 22rpx;
+					color: #999;
+					line-height: 1.4;
+				}
+
+				.distance {
+					flex-shrink: 0;
+					font-size: 22rpx;
+					color: #999;
+					line-height: 1.4;
+				}
+			}
+		}
+	}
+
+	.tips-box {
+		margin: 8rpx 24rpx 24rpx;
+		padding: 24rpx;
+		background: #fff;
+		border-radius: 16rpx;
+
+		.tips-title {
+			font-size: 28rpx;
+			font-weight: 600;
+			color: #333;
+			margin-bottom: 16rpx;
+		}
+
+		.tips-content {
+			.tips-text {
+				font-size: 24rpx;
+				color: #999;
+				line-height: 1.7;
+				white-space: pre-wrap;
+			}
+
+			&.collapsed {
+				display: -webkit-box;
+				-webkit-box-orient: vertical;
+				-webkit-line-clamp: 3;
+				overflow: hidden;
+			}
+		}
+
+		.tips-toggle {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			gap: 6rpx;
+			margin-top: 16rpx;
+			font-size: 24rpx;
+			color: #1C7CF9;
+		}
+	}
+
+	.empty-wrap {
+		padding-top: 120rpx;
+		display: flex;
+		justify-content: center;
+	}
+
+	.footer {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom));
+		background: #fff;
+		box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.04);
+		z-index: 20;
+
+		.confirm-btn {
+			height: 88rpx;
+			line-height: 88rpx;
+			text-align: center;
+			border-radius: 160rpx;
+			background: linear-gradient(269deg, #2260F6 0%, #0FB0FF 100%);
+			color: #fff;
+			font-size: 32rpx;
+			font-weight: 500;
+
+			&.disabled {
+				background: #B7D4FF;
+				color: #fff;
+			}
+		}
+	}
+</style>

+ 197 - 0
pages/topUp/status.vue

@@ -0,0 +1,197 @@
+<template>
+	<view class="page">
+		<u-navbar title="储值管理" :autoBack="true" :placeholder="true"></u-navbar>
+		<!-- 遮罩弹窗:审核中 / 申请失败 -->
+		<view class="mask" v-if="!loading">
+			<view class="dialog" @click.stop>
+				<!-- 审核中 auditStatus=1 -->
+				<template v-if="auditStatus === 1">
+					<view class="title">提交申请</view>
+					<view class="msg">你的提交申请,请耐心等待平台审核。</view>
+					<view class="btn" @click="goHome">我知道了</view>
+				</template>
+
+				<!-- 已驳回 auditStatus=3 -->
+				<template v-else-if="auditStatus === 3">
+					<view class="title">申请失败</view>
+					<view class="reason-block" v-if="rejectReason">
+						<text class="reason-label">原因:</text>
+						<text class="reason-text">{{ rejectReason }}</text>
+					</view>
+					<view class="reason-block" v-else>
+						<text class="reason-text">暂无失败原因说明</text>
+					</view>
+					<view
+						class="btn"
+						:class="{ disabled: reapplyStatus === 1 }"
+						@click="reApply"
+					>
+						{{ reapplyStatus === 1 ? '暂不可重新申请' : '重新申请' }}
+					</view>
+				</template>
+
+				<!-- 异常兜底 -->
+				<template v-else>
+					<view class="msg">状态异常,请返回重试</view>
+					<view class="btn" @click="goBack">返回</view>
+				</template>
+			</view>
+		</view>
+
+		<view class="loading" v-else>
+			<u-loading mode="circle" size="48"></u-loading>
+		</view>
+	</view>
+</template>
+
+<script>
+	import prepaidApi, { getMerchantId } from '@/api/memberPrepaid.js'
+
+	export default {
+		data() {
+			return {
+				loading: true,
+				auditStatus: 1,
+				reapplyStatus: 0,
+				rejectReason: ''
+			};
+		},
+		onShow() {
+			this.loadStatus();
+		},
+		methods: {
+			loadStatus() {
+				const merchantId = getMerchantId();
+				if (!merchantId) {
+					this.loading = false;
+					uni.showToast({ title: '未获取到门店信息', icon: 'none' });
+					return;
+				}
+				this.loading = true;
+				prepaidApi
+					.getFeatureStatus(merchantId)
+					.then((res) => {
+						if (res.data.code !== 200) {
+							uni.showToast({ title: res.data.message || '加载失败', icon: 'none' });
+							return;
+						}
+						const data = res.data.result || {};
+						this.auditStatus = Number(data.auditStatus);
+						this.reapplyStatus = Number(data.reapplyStatus);
+						this.rejectReason = data.rejectReason || '';
+						if (this.auditStatus === 0) {
+							uni.redirectTo({ url: '/pages/topUp/agreement' });
+						} else if (this.auditStatus === 2) {
+							uni.redirectTo({ url: '/pages/topUp/manage' });
+						}
+					})
+					.catch(() => {
+						uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
+					})
+					.finally(() => {
+						this.loading = false;
+					});
+			},
+			goHome() {
+				uni.switchTab({ url: '/pages/index/index' });
+			},
+			goBack() {
+				uni.redirectTo({ url: '/pages/topUp/index' });
+			},
+			reApply() {
+				if (this.reapplyStatus === 1) {
+					uni.showToast({ title: '当前不可重新申请', icon: 'none' });
+					return;
+				}
+				uni.redirectTo({ url: '/pages/topUp/agreement?reapply=1' });
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.page {
+		min-height: 100vh;
+		background: #f7f7f7;
+	}
+
+	.mask {
+		position: fixed;
+		left: 0;
+		right: 0;
+		top: 0;
+		bottom: 0;
+		background: rgba(0, 0, 0, 0.45);
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		z-index: 100;
+		padding: 0 64rpx;
+		box-sizing: border-box;
+	}
+
+	.dialog {
+		width: 100%;
+		background: #fff;
+		border-radius: 24rpx;
+		padding: 56rpx 48rpx 48rpx;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+	}
+
+	.loading {
+		padding-top: 200rpx;
+		display: flex;
+		justify-content: center;
+	}
+
+	.title {
+		font-size: 36rpx;
+		font-weight: 600;
+		color: #1a1a1a;
+		text-align: center;
+		margin-bottom: 28rpx;
+	}
+
+	.msg {
+		font-size: 28rpx;
+		color: #333;
+		text-align: center;
+		line-height: 1.7;
+		margin-bottom: 48rpx;
+	}
+
+	.reason-block {
+		width: 100%;
+		margin-bottom: 48rpx;
+		font-size: 28rpx;
+		line-height: 1.7;
+		color: #333;
+		word-break: break-all;
+		text-align: left;
+	}
+
+	.reason-label {
+		color: #333;
+	}
+
+	.reason-text {
+		color: #333;
+	}
+
+	.btn {
+		width: 100%;
+		height: 88rpx;
+		line-height: 88rpx;
+		text-align: center;
+		background: linear-gradient(90deg, #51a2ff 0%, #4ec2ff 100%);
+		color: #fff;
+		font-size: 32rpx;
+		border-radius: 140rpx;
+
+		&.disabled {
+			opacity: 0.5;
+		}
+	}
+</style>

BIN=BIN
static/index/toup.png


BIN=BIN
static/topUp/add.png


BIN=BIN
static/topUp/back.png


BIN=BIN
static/topUp/bg-coupon-border.png


BIN=BIN
static/topUp/coupon.png


BIN=BIN
static/topUp/del.png


BIN=BIN
static/topUp/expenditure.png


BIN=BIN
static/topUp/give.png


BIN=BIN
static/topUp/indexBg.png


BIN=BIN
static/topUp/indexLift.png


BIN=BIN
static/topUp/more.png


BIN=BIN
static/topUp/prepaidsettings.png


BIN=BIN
static/topUp/recharge.png


BIN=BIN
static/topUp/sharedstores.png


BIN=BIN
static/topUp/tabs.png