Ver Fonte

feat(售后流程): 重构申请售后页面为分步式流程并新增历史退款记录功能

- 将原申请售后页面拆分为两个步骤组件(refund-step-one和refund-step-two)
- 新增退款商品选择和原因选择界面(refund-step-one)
- 新增退款金额调整和凭证上传界面(refund-step-two)
- 在退款详情页添加历史退款记录查看功能
- 新增退款历史弹窗组件(refund-history-popup)
- 优化API接口,新增获取商品历史退款列表和退款预览接口
- 调整订单详情页退款按钮跳转逻辑
- 完善退款类型切换和商品可选状态判断
ylong há 2 semanas atrás
pai
commit
2373a7fb61

+ 56 - 0
CLAUDE.md

@@ -0,0 +1,56 @@
+# Book-Hi 项目开发指南 (Project Instructions)
+
+## Tech Stack
+- **Framework**: uni-app
+- **UI Library**: uView UI v1
+- **Language**: JavaScript (ES6+), Vue 2, SCSS
+- **State Management**: Vuex
+- **Platform**: 微信小程序 (WeChat Mini Program), 支付宝小程序 (Alipay Mini Program)
+
+## Code Style
+- **File Naming**: 
+  - 组件 (Components): `kebab-case.vue` (例如: `address-card.vue`) 或 `PascalCase.vue` (例如: `BookItem.vue`)
+  - 页面 (Pages): `kebab-case.vue`
+  - JS 文件: `camelCase.js`
+- **CSS**: 
+  - 必须使用 `<style lang="scss" scoped>`
+  - 单位统一使用 `rpx` 以保证响应式布局
+  - 优先使用 `theme.scss` 中的全局变量
+- **UI Component Usage**:
+  - uView 组件前缀为 `u-` (例如 `<u-button>`),已通过 `easycom` 全局注册,无需手动引入。
+
+## Project Structure
+- `pages/` → 主包页面(仅包含底部的 Tabbar 页面:首页、卖书、购物车、我的)
+- `pages-sell/` → 买书/商城子包(商品详情、搜索、热门推荐等)
+- `pages-home/` → 卖书子包(扫码卖书、确认订单、协议等)
+- `pages-car/` → 购物车与订单子包(购物车列表、结算台、退款售后等)
+- `pages-mine/` → 个人中心子包(收货地址、钱包、合伙人、设置等)
+- `api/` → 接口请求定义 (`config.js` 包含请求拦截器)
+- `components/` → 全局公共组件
+- `store/` → Vuex 状态管理
+- `static/` → 全局静态资源(子包静态资源在各自子包内的 `static/` 目录下)
+
+## API Conventions
+- **接口封装**: 
+  - 所有模块化接口位于 `api/modules/` 下(如 `mall.js`, `user.js`)
+  - 命名规范: 接口方法统一以 `Ajax` 结尾(例如 `getGoodsListAjax`)
+  - 挂载方式: 通过 `vm.$u.api` 全局挂载
+- **接口调用**:
+  - 标准方式: `this.$u.api.methodName(params).then(...)`
+  - 直接调用: `uni.$u.http.post('/path', params).then(...)`
+- **拦截与错误处理**: 
+  - 全局拦截器位于 `api/config.js`
+  - 401 状态码会自动触发 `silentLogin` (静默登录)
+  - 其他非 200 错误码会自动 `showToast` 提示
+
+## Build & Run
+- **微信开发版**: `npm run dev`
+- **微信正式版**: `npm run prod`
+- **环境配置**: 通过 `.env.js`, `.env.dev.js`, `.env.prod.js` 区分环境。
+
+## Conventions
+- **代码注释**: 所有复杂的业务逻辑和代码注释必须使用简体中文。
+- **页面路由**: 
+  - Tabbar 页面跳转使用 `uni.switchTab`
+  - 子包页面跳转使用 `uni.navigateTo`,并且 **必须使用绝对路径** (例如: `/pages-sell/pages/detail`)。
+- **重构原则**: 优先使用 uView 组件替代原生 HTML/Vue 标签,以保持项目整体一致性。

+ 6 - 0
api/modules/mall.js

@@ -7,6 +7,9 @@ export const useMallApi = (Vue, vm) => {
 	
 		// 售后详情 (新版)
 		getNewRefundOrderDetailAjax: (params) => vm.$u.get('/token/shop/order/getRefundOrderDetail', params),
+
+		// 查看商品历史退款订单列表
+		getRefundOrderDetailListAjax: (params) => vm.$u.get('/token/shop/order/getRefundOrderDetailList', params),
 		
 		// 热门搜索列表
 		getHotSearchListAjax: () => vm.$u.get('/token/shop/user/hotSearchList'),
@@ -62,6 +65,9 @@ export const useMallApi = (Vue, vm) => {
 		// 申请退款
 		applyRefundAjax: (data) => vm.$u.post('/token/shop/order/applyRefund', data),
 		
+		// 申请退款预览 (可退数量及金额判断)
+		applyRefundPreAjax: (data) => vm.$u.post('/token/shop/order/applyRefundPre', data),
+		
 		// 取消订单
 		cancelOrderAjax: (data) => vm.$u.post('/token/shop/order/orderCancel', data),
 

+ 2 - 4
pages-car/components/buy-order-item.vue

@@ -169,10 +169,8 @@
                     return;
                 }
                 if (type === 'detail') {
-                    // 跳转至退款详情页
-                    const id = this.order.refundOrderId;
                     uni.navigateTo({
-                        url: `/pages-car/pages/refund-detail?refundOrderId=${id}`
+                        url: `/pages-car/pages/order-detail?orderId=${data.orderId}`
                     });
                     return;
                 }
@@ -344,4 +342,4 @@
             }
         }
     }
-</style>
+</style>

+ 164 - 0
pages-car/components/refund-history-popup.vue

@@ -0,0 +1,164 @@
+<template>
+	<u-popup v-model="visible" mode="bottom" border-radius="24" :safe-area-inset-bottom="true" :mask-close-able="true"
+		@close="close">
+		<view class="history-popup">
+			<view class="header">
+				<text class="title">宝贝售后详情</text>
+				<image src="/pages-sell/static/select-good/icon-close.png" class="close-icon" @click="close"></image>
+			</view>
+			<view class="list" v-if="list.length > 0">
+				<view class="item" v-for="(item, index) in list" :key="index" @click="handleView(item)">
+					<image class="cover" :src="item.cover" mode="aspectFill"></image>
+					<view class="content">
+						<view class="name u-line-2">{{ item.bookName }}</view>
+						<view class="meta">
+							<text class="meta-text">数量:{{ item.refundNum }}</text>
+							<text class="meta-text">¥ {{ formatMoney(item.refundMoney) }}</text>
+						</view>
+					</view>
+					<view class="right">
+						<text class="link-text">查看</text>
+						<u-icon name="arrow-right" color="#C9C9C9" size="24"></u-icon>
+					</view>
+				</view>
+			</view>
+			<view class="empty" v-else>
+				<u-empty text="暂无历史退款记录" mode="history"></u-empty>
+			</view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				visible: false,
+				list: []
+			};
+		},
+		methods: {
+			open(list) {
+				this.list = Array.isArray(list) ? list : [];
+				this.visible = true;
+			},
+			close() {
+				this.visible = false;
+			},
+			handleView(item) {
+				this.$emit('view', item);
+				this.close();
+			},
+			formatMoney(money) {
+				const num = Number(money || 0);
+				if (Number.isNaN(num)) return '0.00';
+				return num.toFixed(2);
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.history-popup {
+		background-color: #fff;
+		padding: 30rpx;
+		min-height: 420rpx;
+		max-height: 900rpx;
+		box-sizing: border-box;
+		display: flex;
+		flex-direction: column;
+		overflow: hidden;
+	}
+
+	.header {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		position: relative;
+		margin-bottom: 20rpx;
+		padding-bottom: 20rpx;
+		border-bottom: 2rpx dashed #eee;
+		flex-shrink: 0;
+	}
+
+	.title {
+		font-size: 34rpx;
+		font-weight: bold;
+		color: #333;
+	}
+
+	.close-icon {
+		position: absolute;
+		right: 0;
+		top: 0;
+		width: 24rpx;
+		height: 24rpx;
+		padding: 10rpx;
+		box-sizing: content-box;
+	}
+
+	.list {
+		flex: 1;
+		overflow: auto;
+	}
+
+	.item {
+		display: flex;
+		align-items: center;
+		padding: 24rpx 0;
+		border-bottom: 1rpx solid #F0F0F0;
+	}
+
+	.cover {
+		width: 120rpx;
+		height: 140rpx;
+		border-radius: 8rpx;
+		margin-right: 20rpx;
+		flex-shrink: 0;
+		background-color: #f5f5f5;
+	}
+
+	.content {
+		flex: 1;
+		min-width: 0;
+	}
+
+	.name {
+		font-size: 28rpx;
+		color: #333;
+		line-height: 1.4;
+		margin-bottom: 10rpx;
+	}
+
+	.meta {
+		display: flex;
+		align-items: center;
+		gap: 16rpx;
+	}
+
+	.meta-text {
+		font-size: 24rpx;
+		color: #666;
+	}
+
+	.right {
+		display: flex;
+		align-items: center;
+		margin-left: 16rpx;
+		flex-shrink: 0;
+	}
+
+	.link-text {
+		font-size: 26rpx;
+		color: #38C148;
+		margin-right: 6rpx;
+	}
+
+	.empty {
+		flex: 1;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+	}
+</style>
+

+ 465 - 0
pages-car/components/refund-step-one.vue

@@ -0,0 +1,465 @@
+<template>
+	<view class="refund-step-one">
+		<view class="card">
+			<view class="type-section">
+				<view class="section-title">选择售后类型</view>
+				<view class="type-btn-group">
+					<view class="type-btn" :class="{ active: localRefundType === '1' }"
+						@click="changeRefundType('1')">
+						<text>退货退款</text>
+					</view>
+					<view class="type-btn" :class="{ active: localRefundType === '2' }"
+						@click="changeRefundType('2')">
+						<text>仅退款</text>
+					</view>
+				</view>
+			</view>
+
+			<view class="card-header">
+				<text class="card-title">选择售后商品</text>
+				<text class="sub-title">已选 {{ selectedCount }}/{{ goodsList.length }}</text>
+			</view>
+			<view class="goods-list">
+				<view v-for="(item, index) in localGoodsList" :key="index" class="goods-item" :class="{ 'is-disabled': item.disabled }" @click="toggleCheck(index)">
+					<view class="checkbox-box">
+						<u-icon v-if="item.disabled" name="minus-circle" color="#ccc" size="44"></u-icon>
+						<u-icon v-else-if="item.checked" name="checkmark-circle-fill" color="#38C148" size="44"></u-icon>
+						<u-icon v-else name="checkmark-circle" color="#ccc" size="44"></u-icon>
+					</view>
+					<image :src="item.cover" mode="aspectFill" class="goods-cover"></image>
+					<view class="goods-info">
+						<view class="goods-name u-line-2">{{ item.bookName }}</view>
+						<view class="goods-sku" v-if="item.isbn">ISBN: {{ item.isbn }}</view>
+						<view class="goods-sku" v-if="item.conditionType">品相:{{ item.conditionType | conditionText }}</view>
+						<view class="price-box">
+							<text class="price">¥{{ item.payPrice || item.price }}</text>
+							<view class="num-wrap">
+								<text class="status-tag" v-if="item.status == '2'">退款中</text>
+								<text class="status-tag" v-if="item.status == '3'">退款成功</text>
+								<text class="num">x{{ item.refundNum || item.num || 1 }}</text>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="card">
+			<view class="card-header" style="border-bottom: none; padding-bottom: 0;">
+				<text class="card-title">选择售后原因</text>
+			</view>
+			
+			<!-- 货物状态选择 (如果是退货退款) -->
+			<view v-if="localRefundType === '1'" class="status-tabs">
+				<view class="status-tab" :class="{ active: globalShopStatus === '1' }" @click="globalShopStatus = '1'">未收到货</view>
+				<view class="status-tab" :class="{ active: globalShopStatus === '2' }" @click="globalShopStatus = '2'">已收到货</view>
+			</view>
+
+			<view class="reason-list">
+				<view class="reason-item" v-for="(reason, index) in reasonList" :key="index" @click="selectReason(reason)">
+					<text class="reason-label">{{ reason.label }}</text>
+					<u-icon v-if="globalRefundReason === reason.label" name="checkmark-circle-fill" color="#38C148" size="44"></u-icon>
+					<u-icon v-else name="checkmark-circle" color="#ccc" size="44"></u-icon>
+				</view>
+			</view>
+		</view>
+
+		<view class="footer-bar two-btns">
+			<view class="btn-wrap">
+				<u-button shape="circle" :custom-style="cancelBtnStyle" @click="cancelRefund">先不退了</u-button>
+			</view>
+			<view class="btn-wrap">
+				<u-button type="primary" shape="circle" :custom-style="btnStyle" @click="goNextInnerStep">下一步</u-button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	name: 'RefundStepOne',
+	props: {
+		goodsList: {
+			type: Array,
+			default: () => []
+		},
+		refundType: {
+			type: String,
+			default: '1'
+		},
+		reasonList: {
+			type: Array,
+			default: () => []
+		},
+		statusList: {
+			type: Array,
+			default: () => [
+				{ value: '1', label: '未收到货' },
+				{ value: '2', label: '已收到货' }
+			]
+		}
+	},
+	data() {
+			return {
+				localRefundType: this.refundType,
+				localGoodsList: [],
+				
+				globalShopStatus: '1',
+				globalShopStatusText: '未收到货',
+				globalRefundReason: '',
+				globalRefundReasonText: '',
+				
+				btnStyle: {
+					width: '100%',
+					height: '80rpx',
+					fontSize: '30rpx',
+					backgroundColor: '#38C148',
+					color: '#ffffff',
+					border: 'none'
+				},
+				cancelBtnStyle: {
+					width: '100%',
+					height: '80rpx',
+					fontSize: '30rpx',
+					backgroundColor: '#ffffff',
+					color: '#333',
+					border: '1px solid #f0f0f0'
+				}
+		};
+	},
+	watch: {
+			goodsList: {
+				handler(val) {
+					// 深度拷贝,避免直接修改 props
+					this.localGoodsList = JSON.parse(JSON.stringify(val));
+					
+					// 自动回填售后原因(如果是修改申请且已存在全局原因或第一件商品有原因)
+					if (val.length > 0 && val[0].refundReason) {
+						this.globalRefundReason = val[0].refundReason;
+						this.globalRefundReasonText = val[0].refundReasonText || val[0].refundReason;
+					}
+					if (val.length > 0 && val[0].shopStatus) {
+						this.globalShopStatus = val[0].shopStatus;
+						this.globalShopStatusText = val[0].shopStatusText;
+					}
+				},
+				deep: true,
+				immediate: true
+			},
+			refundType(val) {
+				this.localRefundType = val;
+				this.globalShopStatus = val === '1' ? '2' : '1';
+			},
+			globalShopStatus(val) {
+				this.globalShopStatusText = val === '1' ? '未收到货' : '已收到货';
+			}
+		},
+	computed: {
+		selectedCount() {
+			return this.localGoodsList.filter(item => item.checked).length;
+		},
+		selectedGoods() {
+			return this.localGoodsList.filter(item => item.checked);
+		}
+	},
+	methods: {
+		changeRefundType(type) {
+			if (this.localRefundType !== type) {
+				this.localRefundType = type;
+				this.$emit('update:refundType', type);
+			}
+		},
+		toggleCheck(index) {
+			if (this.localGoodsList[index].disabled) {
+				this.$u.toast('该商品状态不可退款');
+				return;
+			}
+			this.$set(this.localGoodsList[index], 'checked', !this.localGoodsList[index].checked);
+		},
+		goNextInnerStep() {
+				if (this.selectedCount === 0) {
+					this.$u.toast('请选择售后商品');
+					return;
+				}
+				if (!this.globalRefundReason) {
+					this.$u.toast('请选择售后原因');
+					return;
+				}
+				
+				// 将选定的全局原因和货物状态填充到每一个被选中的商品身上
+				this.selectedGoods.forEach(item => {
+					this.$set(item, 'shopStatus', this.localRefundType === '1' ? this.globalShopStatus : '1');
+					this.$set(item, 'shopStatusText', this.localRefundType === '1' ? this.globalShopStatusText : '未收到货');
+					this.$set(item, 'refundReason', this.globalRefundReason);
+					this.$set(item, 'refundReasonText', this.globalRefundReasonText);
+				});
+				
+				// 直接派发 next 事件到第二步
+				this.$emit('next', {
+					selectedGoods: this.selectedGoods,
+					refundType: this.localRefundType
+				});
+			},
+			selectReason(reason) {
+				this.globalRefundReason = reason.label;
+				this.globalRefundReasonText = reason.label;
+			},
+			cancelRefund() {
+				uni.navigateBack();
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.refund-step-one {
+	padding-bottom: 140rpx;
+}
+
+.card {
+	background-color: #fff;
+	border-radius: 16rpx;
+	margin-bottom: 20rpx;
+	padding: 30rpx;
+
+	.goods-list {
+		.goods-item {
+			display: flex;
+			align-items: center;
+			margin-bottom: 30rpx;
+			transition: opacity 0.3s;
+
+			&.is-disabled {
+				opacity: 0.5;
+			}
+
+			&:last-child {
+				margin-bottom: 0;
+			}
+		}
+	}
+
+	.type-section {
+		margin-bottom: 30rpx;
+
+		.section-title {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #333;
+			margin-bottom: 20rpx;
+		}
+
+		.type-btn-group {
+			display: flex;
+
+			.type-btn {
+				flex: 1;
+				height: 64rpx;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				border: 2rpx solid #e5e5e5;
+				border-radius: 8rpx;
+				margin-right: 20rpx;
+				font-size: 28rpx;
+				color: #333;
+				transition: all 0.3s;
+
+				&:last-child {
+					margin-right: 0;
+				}
+
+				&.active {
+					border-color: #38C148;
+					color: #38C148;
+					background-color: rgba(56, 193, 72, 0.05);
+					font-weight: bold;
+				}
+			}
+		}
+	}
+
+	.card-header {
+		padding: 10rpx 0 20rpx;
+		border-bottom: 1rpx solid #f5f5f5;
+		margin-bottom: 20rpx;
+		display: flex;
+		align-items: center;
+
+		.card-title {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #333;
+			margin-right: 20rpx;
+		}
+
+		.sub-title {
+			font-size: 24rpx;
+			color: #38C148;
+		}
+	}
+
+	.status-tabs {
+		display: flex;
+		padding: 20rpx 0;
+		
+		.status-tab {
+			flex: 1;
+			height: 72rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			background-color: #f5f5f5;
+			color: #333;
+			font-size: 28rpx;
+			border-radius: 36rpx;
+			margin-right: 20rpx;
+			transition: all 0.3s;
+			
+			&:last-child {
+				margin-right: 0;
+			}
+			
+			&.active {
+				background-color: rgba(56, 193, 72, 0.1);
+				color: #38C148;
+				font-weight: bold;
+				border: 1px solid #38C148;
+			}
+		}
+	}
+
+	.reason-list {
+		.reason-item {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			padding: 30rpx 0;
+			border-bottom: 1rpx solid #f5f5f5;
+
+			&:last-child {
+				border-bottom: none;
+			}
+
+			.reason-label {
+				font-size: 30rpx;
+				color: #333;
+			}
+		}
+	}
+}
+
+.goods-list {
+	.goods-item {
+		display: flex;
+		align-items: center;
+		margin-bottom: 30rpx;
+
+		&:last-child {
+			margin-bottom: 0;
+		}
+
+		.checkbox-box {
+			margin-right: 20rpx;
+		}
+	}
+}
+
+.goods-item {
+	display: flex;
+	align-items: center;
+
+	.goods-cover {
+		width: 140rpx;
+		height: 150rpx;
+		border-radius: 8rpx;
+		margin-right: 20rpx;
+	}
+
+	.goods-info {
+		flex: 1;
+		height: 160rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+
+		.goods-name {
+			font-size: 28rpx;
+			color: #333;
+			line-height: 1.4;
+		}
+
+		.goods-sku {
+			font-size: 24rpx;
+			color: #999;
+		}
+
+		.price-box {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin-top: 10rpx;
+
+			.price {
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333;
+			}
+			
+			.num-wrap {
+				display: flex;
+				align-items: center;
+				
+				.status-tag {
+					font-size: 22rpx;
+					color: #ff3b30;
+					border: 1rpx solid #ff3b30;
+					padding: 2rpx 10rpx;
+					border-radius: 6rpx;
+					margin-right: 10rpx;
+				}
+				
+				.num {
+					font-size: 26rpx;
+					color: #999;
+				}
+			}
+		}
+	}
+}
+
+.mb-30 {
+	margin-bottom: 30rpx;
+}
+
+.mt-10 {
+	margin-top: 10rpx;
+}
+
+.footer-bar {
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background-color: #fff;
+	display: flex;
+	align-items: center;
+	padding: 24rpx 30rpx;
+	padding-bottom: 0;
+	box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+	z-index: 100;
+	padding-bottom: constant(safe-area-inset-bottom);
+	padding-bottom: env(safe-area-inset-bottom);
+
+	&.two-btns {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		gap: 40rpx;
+
+		.btn-wrap {
+			flex: 1;
+		}
+	}
+}
+</style>

+ 595 - 0
pages-car/components/refund-step-two.vue

@@ -0,0 +1,595 @@
+<template>
+	<view class="refund-step-two">
+		<view class="warning-tip">
+			<text>确认退款信息后提交,提交后不可再次退款。</text>
+		</view>
+
+		<!-- 遍历每个选中的商品 -->
+		<view class="card" v-for="(item, index) in localGoods" :key="index">
+			<view class="goods-item mb-30">
+				<image :src="item.cover" mode="aspectFill" class="goods-cover"></image>
+				<view class="goods-info">
+					<view class="goods-name u-line-2">{{ item.bookName }}</view>
+					<view class="price-box mt-10">
+						<text class="price">¥{{ item.payPrice || item.price }}</text>
+						<view v-if="item.canModifyNum == 1" class="num-box">
+							<u-number-box v-model="item.refundNum" :min="1" :max="item.canRefundNum" @change="calculateRefundMoney(index)"></u-number-box>
+						</view>
+						<text v-else class="num">x{{ item.refundNum }}</text>
+					</view>
+				</view>
+			</view>
+
+			<u-cell-group :border="false">
+				<u-cell-item v-if="refundType === '1'" title="货物状态" :value="item.shopStatusText" @click="openStatusPicker(index)"></u-cell-item>
+				<u-cell-item title="退款原因" :value="item.refundReasonText" @click="openReasonPicker(index)"></u-cell-item>
+				
+				<view class="money-cell">
+					<view class="cell-left">
+						<text class="label">退款金额</text>
+					</view>
+					<view class="cell-right">
+						<view class="edit-btn" v-if="item.canModifyMoney == 1" @click="openMoneyEdit(index)">
+							<u-icon name="edit-pen" size="28"></u-icon>
+							<text>修改</text>
+						</view>
+						<text class="money-text ml-20">¥{{ item.refundMoney }}</text>
+					</view>
+				</view>
+				
+				<u-cell-item title="上传描述和凭证" :value="getUploadStatusText(item)" 
+					@click="openUploadPopup(index)" :border-bottom="false"></u-cell-item>
+			</u-cell-group>
+		</view>
+		
+		<!-- 如果是退货退款,还需要选择退货方式和地址?
+		 原申请页面有退货方式,但图 3 并没有显示这部分,可能是在其它地方或者不需要?
+		 我们根据原有的退货方式逻辑,在最底部或者顶部加入 -->
+		<view class="card" v-if="refundType === '1'">
+			<u-cell-group :border="false">
+				<u-cell-item title="退货方式" :value="returnMethodText || '请选择'" @click="showReturnMethodPicker = true" required></u-cell-item>
+			</u-cell-group>
+			
+			<view class="address-section" v-if="returnMethod === '1'" @click="chooseAddress">
+				<view class="flex-a flex-j-b" v-if="address && address.name">
+					<view class="address-label">取件地址</view>
+					<view class="flex-d flex-1 ml-24" style="margin-right: 20rpx">
+						<view class="flex-a flex-j-b mb-10">
+							<view class="address-text">{{ address.name }}</view>
+							<view class="address-text">{{ address.mobile }}</view>
+						</view>
+						<view class="address-text">{{ address.fullAddress }}</view>
+					</view>
+					<u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
+				</view>
+				<view class="flex-a flex-j-b" v-else>
+					<view class="flex-a">
+						<u-icon name="plus-circle-fill" :size="48" color="#38C148" top="2"></u-icon>
+						<view class="ml-10 font-30 address-text">选择取件地址</view>
+					</view>
+					<u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
+				</view>
+			</view>
+		</view>
+
+		<view style="height: 140rpx;"></view>
+
+		<!-- 底部操作栏 -->
+		<view class="footer-bar">
+			<view class="footer-left">
+				<text class="total-label">共退款</text>
+				<text class="total-money">¥{{ totalRefundMoney }}</text>
+				<text class="total-num">共{{ totalRefundNum }}件</text>
+			</view>
+			<view class="footer-right">
+				<view class="btn-wrap" style="margin-left: 30rpx;">
+					<u-button shape="circle" :custom-style="cancelBtnStyle" @click="$emit('back')">上一步</u-button>
+				</view>
+				<view class="btn-wrap">
+					<u-button type="primary" shape="circle" :custom-style="submitBtnStyle" @click="submit">提交申请</u-button>
+				</view>
+			</view>
+		</view>
+
+		<!-- 修改退款金额弹窗 -->
+		<u-popup v-model="showMoneyPopup" mode="bottom" border-radius="24">
+			<view class="popup-container">
+				<view class="popup-header">
+					<text class="title">修改退款金额</text>
+					<u-icon name="close" size="32" color="#999" @click="showMoneyPopup = false"></u-icon>
+				</view>
+				<view class="popup-content">
+					<view class="input-row">
+						<text>退款金额:</text>
+						<u-input v-model="tempMoney" type="number" placeholder="请输入退款金额" class="money-input" />
+					</view>
+					<view class="max-hint">最大可退金额: ¥{{ maxMoneyForCurrent }}</view>
+				</view>
+				<view class="popup-footer">
+					<u-button type="primary" shape="circle" :custom-style="submitBtnStyle" @click="confirmMoney">确认</u-button>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 上传描述和凭证弹窗 -->
+		<u-popup v-model="showUploadPopup" mode="bottom" border-radius="24" height="800">
+			<view class="popup-container full-height">
+				<view class="popup-header">
+					<text class="title">上传描述和凭证</text>
+					<u-icon name="close" size="32" color="#999" @click="showUploadPopup = false"></u-icon>
+				</view>
+				<view class="popup-content flex-1">
+					<view class="upload-textarea-box">
+						<u-input v-model="tempDesc" type="textarea" placeholder="补充描述,有助于平台更好的处理售后问题" :height="100" maxlength="200" />
+					</view>
+					<view class="upload-area mt-20">
+						<common-image-upload v-model="tempFileList" :max-count="5" code="shopRefund" width="160" height="160">
+						</common-image-upload>
+					</view>
+				</view>
+				<view class="popup-footer">
+					<u-button type="primary" shape="circle" :custom-style="submitBtnStyle" @click="confirmUpload">完成</u-button>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 退货方式弹窗 -->
+		<u-select v-model="showReturnMethodPicker" :list="returnMethodList" @confirm="confirmReturnMethod"></u-select>
+		
+		<!-- 退款原因弹窗 -->
+		<u-select v-model="showReasonPicker" :list="reasonList" @confirm="confirmReason"></u-select>
+		
+		<!-- 货物状态弹窗 -->
+		<u-select v-model="showStatusPicker" :list="statusList" @confirm="confirmStatus"></u-select>
+	</view>
+</template>
+
+<script>
+	import CommonImageUpload from "@/components/image-upload.vue";
+
+	export default {
+		name: 'RefundStepTwo',
+		components: {
+			CommonImageUpload
+		},
+		props: {
+			selectedGoods: {
+				type: Array,
+				default: () => []
+			},
+			refundType: {
+				type: String,
+				default: '1'
+			},
+			defaultAddress: {
+				type: Object,
+				default: () => null
+			},
+			reasonList: {
+				type: Array,
+				default: () => []
+			},
+			statusList: {
+				type: Array,
+				default: () => []
+			}
+		},
+		data() {
+			return {
+				localGoods: [],
+				
+				showMoneyPopup: false,
+				showUploadPopup: false,
+				showReasonPicker: false,
+				showStatusPicker: false,
+				currentIndex: -1,
+				
+				tempMoney: '',
+				maxMoneyForCurrent: '0.00',
+				
+				tempDesc: '',
+				tempFileList: [],
+
+				returnMethod: '3',
+				returnMethodText: '自行寄回',
+				showReturnMethodPicker: false,
+				returnMethodList: [
+					{ value: '1', label: '上门取件' },
+					{ value: '2', label: '寄件点自寄' },
+					{ value: '3', label: '自行寄回' }
+				],
+				
+				address: null,
+
+				submitBtnStyle: {
+					height: '70rpx',
+					fontSize: '28rpx',
+					backgroundColor: '#38C148',
+					color: '#ffffff',
+					border: 'none',
+					padding: '0'
+				},
+				cancelBtnStyle: {
+					height: '70rpx',
+					fontSize: '28rpx',
+					backgroundColor: '#ffffff',
+					color: '#333',
+					border: '1px solid #ccc',
+					padding: '0'
+				}
+			};
+		},
+		watch: {
+			selectedGoods: {
+				handler(val) {
+					this.localGoods = JSON.parse(JSON.stringify(val));
+				},
+				deep: true,
+				immediate: true
+			},
+			defaultAddress: {
+				handler(val) {
+					if (val) this.address = { ...val };
+				},
+				deep: true,
+				immediate: true
+			}
+		},
+		computed: {
+			totalRefundMoney() {
+				let total = 0;
+				this.localGoods.forEach(item => {
+					total += Number(item.refundMoney || 0);
+				});
+				return total.toFixed(2);
+			},
+			totalRefundNum() {
+				let total = 0;
+				this.localGoods.forEach(item => {
+					total += Number(item.refundNum || 0);
+				});
+				return total;
+			}
+		},
+		methods: {
+			calculateRefundMoney(index) {
+				const item = this.localGoods[index];
+				// 简单按比例或单价计算最大可退金额和当前退款金额
+				// 此处以单价 * 数量计算作为基准。如果后台返回了具体的单件金额或算法,可以进一步优化。
+				const unitPrice = Number(item.payPrice || item.price || 0);
+				const newMoney = (unitPrice * item.refundNum).toFixed(2);
+				
+				// 同步更新 canRefundMoney (如果有的话) 和 refundMoney
+				this.$set(item, 'refundMoney', newMoney);
+				this.$set(item, 'canRefundMoney', newMoney);
+			},
+			getUploadStatusText(item) {
+				if ((item.description && item.description.trim().length > 0) || (item.fileUrlList && item.fileUrlList.length > 0)) {
+					return '已补充';
+				}
+				return '上传有助处理退款';
+			},
+			openMoneyEdit(index) {
+				this.currentIndex = index;
+				const item = this.localGoods[index];
+				this.tempMoney = String(item.refundMoney);
+				this.maxMoneyForCurrent = String(item.canRefundMoney || 0);
+				this.showMoneyPopup = true;
+			},
+			confirmMoney() {
+				let val = Number(this.tempMoney);
+				let max = Number(this.maxMoneyForCurrent);
+				if (val < 0) val = 0;
+				if (val > max) val = max;
+				
+				this.$set(this.localGoods[this.currentIndex], 'refundMoney', val.toFixed(2));
+				this.showMoneyPopup = false;
+			},
+			openUploadPopup(index) {
+				this.currentIndex = index;
+				const item = this.localGoods[index];
+				this.tempDesc = item.description || '';
+				this.tempFileList = item.fileUrlList ? [...item.fileUrlList] : [];
+				this.showUploadPopup = true;
+			},
+			confirmUpload() {
+				const item = this.localGoods[this.currentIndex];
+				this.$set(item, 'description', this.tempDesc);
+				this.$set(item, 'fileUrlList', [...this.tempFileList]);
+				this.showUploadPopup = false;
+			},
+			openStatusPicker(index) {
+				this.currentIndex = index;
+				this.showStatusPicker = true;
+			},
+			openReasonPicker(index) {
+				this.currentIndex = index;
+				this.showReasonPicker = true;
+			},
+			confirmStatus(e) {
+				const item = this.localGoods[this.currentIndex];
+				this.$set(item, 'shopStatus', e[0].value);
+				this.$set(item, 'shopStatusText', e[0].label);
+			},
+			confirmReason(e) {
+				const item = this.localGoods[this.currentIndex];
+				this.$set(item, 'refundReason', e[0].value);
+				this.$set(item, 'refundReasonText', e[0].label);
+			},
+			confirmReturnMethod(e) {
+				this.returnMethod = e[0].value;
+				this.returnMethodText = e[0].label;
+			},
+			chooseAddress() {
+				uni.navigateTo({
+					url: `/pages-mine/pages/address/list?id=${this.address ? this.address.id : ''}&isSelect=1`
+				});
+			},
+			updateAddress(addr) {
+				this.address = addr;
+			},
+			submit() {
+				if (this.refundType === '1') {
+					if (!this.returnMethod) {
+						this.$u.toast('请选择退货方式');
+						return;
+					}
+					if (this.returnMethod === '1' && (!this.address || !this.address.name)) {
+						this.$u.toast('请选择取件地址');
+						return;
+					}
+				}
+				
+				this.$emit('submit', {
+					goodsList: this.localGoods,
+					returnMethod: this.returnMethod,
+					address: this.address
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.refund-step-two {
+		padding-bottom: 140rpx;
+	}
+
+	.warning-tip {
+		padding: 20rpx 30rpx;
+		color: #38C148;
+		font-size: 24rpx;
+		background-color: #d1f2d8;
+		margin-bottom: 20rpx;
+	}
+
+	.card {
+		background-color: #fff;
+		border-radius: 16rpx;
+		margin: 0 20rpx 20rpx;
+	}
+
+	.goods-item {
+		display: flex;
+		align-items: center;
+		padding: 30rpx;
+		padding-bottom: 0;
+		.goods-cover {
+			width: 120rpx;
+			height: 120rpx;
+			border-radius: 8rpx;
+			margin-right: 20rpx;
+		}
+		.goods-info {
+			flex: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: space-between;
+			.goods-name {
+				font-size: 28rpx;
+				color: #333;
+				line-height: 1.4;
+			}
+			.price-box {
+				display: flex;
+				justify-content: space-between;
+				align-items: center;
+				.price {
+					font-size: 28rpx;
+					color: #333;
+					font-weight: 500;
+				}
+				.num {
+					font-size: 26rpx;
+					color: #999;
+				}
+			}
+		}
+	}
+
+	.mb-30 {
+		margin-bottom: 30rpx;
+	}
+	.mt-10 {
+		margin-top: 10rpx;
+	}
+	.ml-20 {
+		margin-left: 20rpx;
+	}
+
+	.money-cell {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		padding: 26rpx 32rpx;
+		background-color: #fff;
+		border-bottom: 1rpx solid #f5f5f5;
+
+		.cell-left {
+			.label {
+				font-size: 28rpx;
+				color: #333;
+			}
+		}
+
+		.cell-right {
+			display: flex;
+			align-items: center;
+
+			.edit-btn {
+				display: flex;
+				align-items: center;
+				color: #666;
+				font-size: 24rpx;
+				margin-right: 10rpx;
+				text {
+					margin-left: 4rpx;
+				}
+			}
+
+			.money-text {
+				font-size: 32rpx;
+				color: #38C148;
+				font-weight: bold;
+			}
+		}
+	}
+
+	.footer-bar {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		background-color: #fff;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 24rpx 30rpx;
+		padding-bottom: 0;
+		box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+		z-index: 100;
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+
+		.footer-left {
+			display: flex;
+			align-items: baseline;
+			.total-label {
+				font-size: 28rpx;
+				color: #38C148;
+				font-weight: bold;
+			}
+			.total-money {
+				font-size: 36rpx;
+				color: #38C148;
+				font-weight: bold;
+				margin: 0 10rpx;
+			}
+			.total-num {
+				font-size: 24rpx;
+				color: #999;
+			}
+		}
+
+		.footer-right {
+			display: flex;
+			align-items: center;
+			flex: 1;
+			justify-content: flex-end;
+			gap: 40rpx;
+			.btn-wrap {
+				flex: 1;
+				max-width: 200rpx;
+			}
+		}
+	}
+
+	.popup-container {
+		padding: 30rpx;
+		background-color: #fff;
+		display: flex;
+		flex-direction: column;
+
+		&.full-height {
+			height: 100%;
+		}
+
+		.popup-header {
+			display: flex;
+			justify-content: space-between;
+			align-items: center;
+			margin-bottom: 40rpx;
+			flex-shrink: 0;
+
+			.title {
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333;
+			}
+		}
+
+		.popup-content {
+			margin-bottom: 40rpx;
+			
+			&.flex-1 {
+				flex: 1;
+				overflow-y: auto;
+			}
+
+			.input-row {
+				display: flex;
+				align-items: center;
+				margin-bottom: 20rpx;
+				font-size: 30rpx;
+
+				.money-input {
+					flex: 1;
+					text-align: right;
+				}
+			}
+
+			.max-hint {
+				font-size: 24rpx;
+				color: #999;
+				text-align: right;
+			}
+
+			.upload-textarea-box {
+				background-color: #f9f9f9;
+				padding: 20rpx;
+				border-radius: 12rpx;
+			}
+		}
+
+		.popup-footer {
+			flex-shrink: 0;
+			padding-bottom: constant(safe-area-inset-bottom);
+			padding-bottom: env(safe-area-inset-bottom);
+		}
+	}
+	
+	.address-section {
+		padding: 26rpx 0;
+		border-top: 1rpx solid #f5f5f5;
+	}
+
+	.address-text {
+		color: #333333;
+		font-family: PingFang SC;
+		font-weight: 400;
+	}
+
+	.address-label {
+		font-size: 28rpx;
+		color: #909399;
+		margin-right: 20rpx;
+		width: 140rpx;
+	}
+	
+	.mt-20 {
+		margin-top: 20rpx;
+	}
+	.flex-1 {
+		flex: 1;
+	}
+</style>

+ 209 - 842
pages-car/pages/apply-refund.vue

@@ -1,416 +1,98 @@
 <template>
 	<view class="apply-refund-page">
-		<!-- 退款商品 -->
-		<view class="card">
-			<view class="type-section">
-				<view class="section-title">请选择售后类型</view>
-				<view class="type-btn-group">
-					<view class="type-btn" :class="{ active: refundType === '1' }"
-						@click="confirmType([{ value: '1', label: '退货退款' }])">
-						<text>退货退款</text>
-					</view>
-					<view class="type-btn" :class="{ active: refundType === '2' }"
-						@click="confirmType([{ value: '2', label: '仅退款' }])">
-						<text>仅退款</text>
-					</view>
-				</view>
-			</view>
-
-			<view class="card-header">
-				<text class="card-title">退款商品</text>
-			</view>
-			<view class="goods-list">
-				<view v-for="(item, index) in orderInfo.detailVoList" :key="index" class="goods-item">
-					<view class="checkbox-box" @click.stop="toggleCheck(item)">
-						<u-icon v-if="item.checked" name="checkmark-circle-fill" color="#38C148" size="44"></u-icon>
-						<u-icon v-else name="checkmark-circle" color="#ccc" size="44"></u-icon>
-					</view>
-					<image :src="item.cover" mode="aspectFill" class="goods-cover"></image>
-					<view class="goods-info">
-						<view class="goods-name u-line-2">{{ item.bookName }}</view>
-						<view class="goods-sku" v-if="item.isbn">ISBN: {{ item.isbn }}</view>
-						<view class="price-box">
-							<text class="price">¥{{ item.payPrice }}</text>
-							<u-number-box v-model="item.refundNum" :min="1" :max="item.num"
-								@change="calculateRefundMoney"></u-number-box>
-						</view>
-					</view>
-				</view>
-			</view>
-		</view>
-
-		<!-- 退款原因配置 -->
-		<view class="card no-padding">
-			<u-cell-group :border="false">
-				<!-- 退款原因 -->
-				<u-cell-item title="退款原因" :value="refundReasonText || '请选择'" @click="showReasonPicker = true" required
-					:border-bottom="refundType === '2' ? false : true"></u-cell-item>
-
-				<!-- 货物状态 (仅退款显示) -->
-				<u-cell-item v-if="refundType === '1'" title="货物状态" :value="shopStatusText"
-					@click="showStatusPicker = true" required :border-bottom="false"></u-cell-item>
-			</u-cell-group>
-		</view>
-
-		<!-- 退款金额与凭证 -->
-		<view class="card no-padding">
-			<view class="card-title" style="padding:30rpx 0 0 20rpx">退款金额:</view>
-			<u-cell-group :border="false">
-				<!-- 退回支付渠道 -->
-				<u-cell-item :title="refundChannelText" :arrow="false">
-					<view slot="right-icon" class="refund-money-box" @click="showRefundAmountPopup = true">
-						<text class="money">¥{{ refundMoney }}</text>
-						<view class="edit-tag" v-if="refundType === '2'">
-							<u-icon name="edit-pen" size="24"></u-icon>
-							<text>修改</text>
-						</view>
-					</view>
-				</u-cell-item>
-				<!-- 上传描述和凭证 -->
-				<u-cell-item title="上传描述和凭证" :value="uploadStatusText" @click="showUploadPopup = true" is-link
-					:border-bottom="false"></u-cell-item>
-			</u-cell-group>
-		</view>
-
-		<!-- 退货方式 (仅退货退款显示) - 单独分区 -->
-		<view class="card no-padding" v-if="refundType === '1'">
-			<u-cell-group :border="false">
-				<u-cell-item title="退货方式" :value="returnMethodText || '请选择'" @click="showReturnMethodPicker = true"
-					required></u-cell-item>
-
-				<!-- 上门取件地址 (仅上门取件显示) -->
-				<view class="address-section" @click="chooseAddress">
-					<view class="flex-a flex-j-b mb-20" v-if="address.name">
-						<view class="address-label">我的地址</view>
-						<image src="/pages-mine/static/adderss.png" style="width: 40rpx; height: 40rpx"></image>
-						<view class="flex-d flex-1 ml-24" style="margin-right: 20rpx">
-							<view class="flex-a flex-j-b mb-10">
-								<view class="address-text">{{ address.name }}</view>
-								<view class="address-text">{{ address.mobile }}</view>
-							</view>
-							<view class="address-text">{{ address.province || '' }}{{ address.city || '' }}{{
-								address.area || '' }}{{ address.fullAddress }}</view>
-						</view>
-						<u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
-					</view>
-
-					<view class="flex-a flex-j-b" v-else>
-						<view class="flex-a">
-							<u-icon name="plus-circle-fill" :size="48" color="#38C148" top="2"></u-icon>
-							<view class="ml-10 font-30 address-text">我的地址</view>
-							<text class="u-required">*</text>
-						</view>
-						<view class="flex-a">
-							<view class="ml-10 address-text">请添加</view>
-							<u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
-						</view>
-					</view>
-				</view>
-			</u-cell-group>
-		</view>
-
-
-		<!-- 我的服务 - 单独分区 -->
-		<view class="card no-padding">
-			<view class="card-title" style="padding:30rpx 0 0 20rpx">我的服务</view>
-			<u-cell-group :border="false">
-				<u-cell-item title="退换无忧" value="服务生效中" :arrow="true" :border-bottom="false">
-					<u-icon slot="icon" name="checkmark-circle-fill" color="#38C148" size="32"
-						style="margin-right: 10rpx;"></u-icon>
-				</u-cell-item>
-			</u-cell-group>
-		</view>
-
-		<!-- 底部占位 -->
-		<view style="height: 60rpx;"></view>
-
-		<!-- 底部固定栏 -->
-		<view class="footer-bar">
-			<view class="footer-left" @click="showRefundAmountPopup = true">
-				<text class="label">{{ refundChannelText }}:</text>
-				<text class="amount">¥{{ refundMoney }}</text>
-				<text class="detail-link">明细</text>
-			</view>
-			<view class="footer-right">
-				<u-button type="primary" shape="circle" :custom-style="submitBtnStyle" @click="submit">提交申请</u-button>
-			</view>
-		</view>
-
-		<!-- 弹窗: 退款原因 -->
-		<u-select v-model="showReasonPicker" :list="reasonList" @confirm="confirmReason"></u-select>
-		<!-- 弹窗: 货物状态 -->
-		<u-select v-model="showStatusPicker" :list="statusList" @confirm="confirmStatus"></u-select>
-		<!-- 弹窗: 退货方式 -->
-		<u-select v-model="showReturnMethodPicker" :list="returnMethodList" :default-value="returnMethodIndex"
-			@confirm="confirmReturnMethod"></u-select>
-
-		<!-- 弹窗: 上传描述和凭证 -->
-		<u-popup v-model="showUploadPopup" mode="bottom" border-radius="24" height="800">
-			<view class="popup-container full-height">
-				<view class="popup-header">
-					<text class="title">上传描述和凭证</text>
-					<u-icon name="close" size="32" color="#999" @click="showUploadPopup = false"></u-icon>
-				</view>
-				<view class="popup-content">
-					<view class="upload-textarea-box">
-						<u-input v-model="description" type="textarea" placeholder="补充描述,有助于平台更好的处理售后问题" :height="100"
-							maxlength="200" />
-					</view>
-					<view class="upload-area">
-						<common-image-upload v-model="fileList" :max-count="5" code="shopRefund" width="160"
-							height="160">
-						</common-image-upload>
-					</view>
-				</view>
-				<view class="popup-footer safe-area-bottom">
-					<u-button type="primary" shape="circle" :custom-style="submitBtnStyle"
-						@click="showUploadPopup = false">完成</u-button>
-				</view>
-			</view>
-		</u-popup>
-
-		<!-- 弹窗: 退款明细 -->
-		<u-popup v-model="showRefundAmountPopup" mode="bottom" border-radius="24">
-			<view class="popup-container">
-				<view class="popup-header">
-					<text class="title">{{ refundType === '2' ? '修改退款金额' : '退款金额明细' }}</text>
-					<u-icon name="close" size="32" color="#999" @click="showRefundAmountPopup = false"></u-icon>
-				</view>
-				<view class="popup-content">
-					<view class="detail-item">
-						<view class="item-row">
-							<u-icon name="rmb-circle" size="36" color="#666"></u-icon>
-							<text class="item-label">{{ refundChannelText }}</text>
-							<text v-if="refundType !== '2'" class="item-value">¥{{ refundMoney }}</text>
-							<u-input v-else v-model="customRefundMoney" type="number" placeholder="请输入退款金额" 
-								class="money-input" @blur="validateRefundMoney" />
-						</view>
-						<view v-if="refundType === '2'" class="max-money-hint">
-							<text>最大可退金额: ¥{{ maxRefundMoney }}</text>
-						</view>
-					</view>
-					<!-- 如果有红包/优惠,展示在这里 -->
-					<view class="detail-item" v-if="otherRefundAmount > 0">
-						<view class="item-row">
-							<u-icon name="red-packet" size="36" color="#666"></u-icon>
-							<text class="item-label">退回其他</text>
-						</view>
-						<view class="sub-list">
-							<view class="sub-item" v-if="orderInfo.discountMoney > 0">
-								<text class="sub-label">优惠金额</text>
-								<text class="sub-value">¥{{ orderInfo.discountMoney }}</text>
-							</view>
-							<!-- 预留红包字段 -->
-							<view class="sub-item" v-if="orderInfo.redPacketMoney > 0">
-								<text class="sub-label">红包</text>
-								<text class="sub-value">¥{{ orderInfo.redPacketMoney }}</text>
-							</view>
-						</view>
-					</view>
-				</view>
-				<view class="popup-footer safe-area-bottom">
-					<u-button v-if="refundType === '2'" type="primary" shape="circle" :custom-style="submitBtnStyle"
-						@click="confirmRefundMoney">确认修改</u-button>
-					<u-button v-else type="primary" shape="circle" :custom-style="submitBtnStyle"
-						@click="showRefundAmountPopup = false">我知道了</u-button>
-				</view>
-			</view>
-		</u-popup>
+		<refund-step-one 
+			v-if="currentStep === 1"
+			:goods-list="mergedGoods"
+			:refund-type.sync="refundType"
+			:reason-list="reasonList"
+			:status-list="statusList"
+			@next="handleStepOneNext"
+		/>
+		
+		<refund-step-two
+			v-if="currentStep === 2"
+			:selected-goods="selectedGoods"
+			:refund-type="refundType"
+			:default-address="address"
+			:reason-list="reasonList"
+			:status-list="statusList"
+			@back="currentStep = 1"
+			@submit="handleSubmit"
+		/>
 	</view>
 </template>
 
 <script>
-	import CommonImageUpload from "@/components/image-upload.vue";
+	import RefundStepOne from '../components/refund-step-one.vue';
+	import RefundStepTwo from '../components/refund-step-two.vue';
 
 	export default {
 		components: {
-			CommonImageUpload
+			RefundStepOne,
+			RefundStepTwo
 		},
 		data() {
 			return {
 				orderId: '',
-				isModify: false, // 是否是修改申请模式
-				refundOrderId: '', // 修改模式下的退款单ID
-				orderInfo: {
-					detailVoList: [],
-					discountMoney: 0,
-					redPacketMoney: 0 // 假设有
-				},
-
-				// 表单数据
+				targetDetailOrderId: '',
+				isModify: false,
+				refundOrderId: '',
+				
+				orderInfo: null,
+				mergedGoods: [], // 合并了 applyRefundPre 返回的字段
+				selectedGoods: [], // Step1 选中的商品及其状态/原因
+				
 				refundType: '1', // 1: 退货退款, 2: 仅退款
-				refundTypeText: '退货退款',
-
-				refundReason: '',
-				refundReasonText: '',
-
-				shopStatus: '2', // 1: 未收到货, 2: 已收到货
-				shopStatusText: '已收到货',
-
-				returnMethod: '3', // 1: 上门取件, 2: 寄件点自寄, 3: 自行寄回
-				returnMethodText: '自行寄回',
-
-				description: '',
-				fileList: [], // 用于回显和同步状态
-
-				address: {}, // 上门取件地址
-
-				// 退款金额相关
-				customRefundMoney: '', // 用户自定义的退款金额
-
-				// 辅助数据
-				showReasonPicker: false,
-				showStatusPicker: false,
-				showReturnMethodPicker: false,
-				showRefundAmountPopup: false,
-				showUploadPopup: false, // 上传弹窗
-
+				currentStep: 1,
+				
 				reasonList: [],
 				statusList: [
 					{ value: '1', label: '未收到货' },
 					{ value: '2', label: '已收到货' }
 				],
-				returnMethodList: [
-					{ value: '1', label: '上门取件' },
-					{ value: '2', label: '寄件点自寄' },
-					{ value: '3', label: '自行寄回' }
-				],
-
-				submitBtnStyle: {
-					width: '100%',
-					height: '80rpx',
-					fontSize: '30rpx',
-					backgroundColor: '#38C148',
-					color: '#ffffff'
-				}
+				
+				address: {} // 默认取件地址
 			};
 		},
-		computed: {
-			returnMethodIndex() {
-				const index = this.returnMethodList.findIndex(item => item.value === this.returnMethod);
-				return index > -1 ? [index] : [2];
-			},
-			selectedGoods() {
-				return this.orderInfo.detailVoList.filter(item => item.checked);
-			},
-			maxRefundMoney() {
-				if (!this.orderInfo.detailVoList || this.orderInfo.detailVoList.length === 0) return '0.00';
-
-				let totalOriginal = 0;
-				this.orderInfo.detailVoList.forEach(item => {
-					totalOriginal += Number(item.payPrice) * item.num;
-				});
-
-				if (totalOriginal === 0) return '0.00';
-
-				let refund = 0;
-				this.selectedGoods.forEach(item => {
-					refund += Number(item.payPrice) * item.refundNum;
-				});
-
-				return refund.toFixed(2);
-			},
-			refundMoney() {
-				if (this.customRefundMoney) {
-					return this.customRefundMoney;
-				}
-				return this.maxRefundMoney;
-			},
-			refundChannelText() {
-				const type = String(this.orderInfo.payType);
-				if (type === '1') return '退回余额';
-				if (type === '2') return '退回微信';
-				return '退回支付渠道';
-			},
-			otherRefundAmount() {
-				return (Number(this.orderInfo.discountMoney) || 0) + (Number(this.orderInfo.redPacketMoney) || 0);
-			},
-			uploadStatusText() {
-				if ((this.description && this.description.trim().length > 0) || this.fileList.length > 0) {
-					return '已补充';
-				}
-				return '上传有助于处理退款';
-			}
-		},
 		onLoad(options) {
 			this.getRefundReasons();
 			if (options.isModify == '1' && options.refundOrderId) {
 				this.isModify = true;
 				this.refundOrderId = options.refundOrderId;
+				// 修改模式暂且保留老逻辑的兼容,由于需求只给了正常流程,先重点处理正常流程
 				this.loadRefundDetail();
 			} else if (options.orderId) {
 				this.orderId = options.orderId;
+				if (options.detailOrderId) {
+					this.targetDetailOrderId = options.detailOrderId;
+				}
 				this.loadOrderDetail();
 			}
 		},
 		onShow() {
+			// 如果在第二步去了地址选择页,返回后更新地址
 			let selectAddr = uni.getStorageSync("selectAddr");
 			if (selectAddr) {
 				this.address = selectAddr;
 				uni.removeStorageSync("selectAddr");
 			}
 		},
-		onUnload() {
-
+		watch: {
+			refundType(newVal) {
+				// 切换退款类型时重新拉取预览数据
+				if (this.orderInfo && !this.isModify) {
+					this.fetchPreRefundData();
+				}
+			}
 		},
 		methods: {
-			loadRefundDetail() {
-				// 使用退款详情接口回填数据
-				this.$u.api.getNewRefundOrderDetailAjax({
-					refundOrderId: this.refundOrderId
-				}).then(res => {
+			getRefundReasons() {
+				uni.$u.http.get("/token/common/getDictOptions?type=shop_order_complaints_options").then(res => {
 					if (res.code === 200) {
-						const data = res.data;
-						this.orderId = data.originOrderId; // 确保orderId存在
-						
-						// 构造页面需要的 orderInfo 结构
-						// 注意:退款详情接口返回的 detailList 结构可能与订单详情的 detailVoList 略有不同,需要映射
-						this.orderInfo = {
-							...data,
-							detailVoList: data.detailList.map(item => ({
-								...item,
-								num: item.num, // 原购买数量
-								refundNum: item.refundNum // 之前申请的退款数量
-							}))
-						};
-						console.log(this.orderInfo, 'orderInfo');
-
-						// 回填表单数据
-						this.refundType = String(data.refundType);
-						// this.refundTypeText 需要根据 options 查找或直接设置,这里简单根据值设置
-						this.refundTypeText = this.refundType === '1' ? '退货退款' : '仅退款';
-						
-						this.shopStatus = String(data.shopStatus);
-						this.shopStatusText = this.shopStatus === '1' ? '未收到货' : '已收到货';
-						
-						this.refundReason = data.refundReason;
-						this.refundReasonText = data.refundReason; // 假设后端直接返回文本,或者需要遍历 reasonList 匹配
-						
-						this.returnMethod = String(data.sendType);
-						// 查找 returnMethodText
-						const methodObj = this.returnMethodList.find(m => m.value === this.returnMethod);
-						this.returnMethodText = methodObj ? methodObj.label : '';
-						
-						this.description = data.description || '';
-						
-						// 回填图片
-						if (data.fileUrlList) {
-							this.fileList = data.fileUrlList;
-						}
-
-						// 初始化选中状态
-						this.orderInfo.detailVoList.forEach(item => {
-							this.$set(item, 'checked', true);
-							// 修改模式下,默认显示上次申请的数量
-							this.$set(item, 'refundNum', item.refundNum || 1);
-						});
-						
-						this.address = {	
-							id: data.sendAddressId,
-							name: data.sendName,
-							mobile: data.sendMobile,
-							fullAddress: data.sendAddress,
-						}
+						this.reasonList = res.data.map(item => ({
+							value: item.dictValue || item.dictLabel,
+							label: item.dictLabel
+						}));
 					}
 				});
 			},
@@ -418,163 +100,190 @@
 				this.$u.api.getShopOrderDetailAjax({ orderId: this.orderId }).then(res => {
 					if (res.code === 200) {
 						this.orderInfo = res.data;
-						// 初始化商品选中状态和退款数量
-						this.orderInfo.detailVoList.forEach(item => {
-							this.$set(item, 'checked', true);
-							this.$set(item, 'refundNum', item.num);
-						});
-
-						// 根据状态默认选中类型
+						
+						// 根据订单状态设置默认售后类型
 						if (this.orderInfo.status == '3') { // 待收货
-							// 默认退货退款
 							this.refundType = '1';
-							this.refundTypeText = '退货退款';
-							this.shopStatus = '1';
-							this.shopStatusText = '未收到货';
 						} else {
 							this.refundType = '1';
-							this.refundTypeText = '退货退款';
-							this.shopStatus = '2';
-							this.shopStatusText = '已收到货';
 						}
 
-						// 默认使用订单收货地址作为取件地址
 						this.address = {
 							id: this.orderInfo.receiverAddressId,
 							name: this.orderInfo.receiverName,
 							mobile: this.orderInfo.receiverMobile,
 							fullAddress: this.orderInfo.receiverAddress,
 						};
+						
+						this.fetchPreRefundData();
 					}
 				});
 			},
-			getRefundReasons() {
-				uni.$u.http.get("/token/common/getDictOptions?type=shop_order_complaints_options").then(res => {
+			fetchPreRefundData() {
+				if (!this.orderInfo || !this.orderInfo.detailVoList) return;
+				
+				// 此时全量传入后端进行校验
+				const detailOrderIdList = this.orderInfo.detailVoList.map(item => item.detailOrderId || item.id);
+				
+				if (detailOrderIdList.length === 0) {
+					this.buildMergedGoods({});
+					return;
+				}
+
+				this.$u.api.applyRefundPreAjax({
+					orderId: this.orderId,
+					detailOrderIdList: detailOrderIdList,
+					refundType: this.refundType
+				}).then(res => {
 					if (res.code === 200) {
-						this.reasonList = res.data.map(item => ({
-							value: item.dictValue || item.dictLabel,
-							label: item.dictLabel
-						}));
+						const preDataMap = {};
+						res.data.forEach(item => {
+							preDataMap[item.detailOrderId] = item;
+						});
+						this.buildMergedGoods(preDataMap);
 					}
 				});
 			},
-			toggleCheck(item) {
-					item.checked = !item.checked;
-					// 当勾选状态变化时,重置自定义退款金额,让系统重新计算
-					this.customRefundMoney = '';
-				},
-			calculateRefundMoney() {
-					// 当商品数量变化时,重置自定义退款金额,让系统重新计算
-					this.customRefundMoney = '';
-				},
-			confirmType(e) {
-					this.refundType = e[0].value;
-					this.refundTypeText = e[0].label;
-
-					// 切换类型时重置一些状态
-					if (this.refundType === '2') { // 仅退款
-						this.returnMethod = '';
-						this.returnMethodText = '';
-					} else {
-						if (!this.returnMethod) {
-							this.returnMethod = '3';
-							this.returnMethodText = '自行寄回';
-						}
+			buildMergedGoods(preDataMap) {
+				// 保留已选中的状态,如果是第一次则按 targetDetailOrderId 选中
+				const isFirst = this.mergedGoods.length === 0;
+				
+				this.mergedGoods = this.orderInfo.detailVoList.map(item => {
+					const id = item.detailOrderId || item.id;
+					const preData = preDataMap[id] || {};
+					
+					// 查找是否已经存在
+					const existing = this.mergedGoods.find(m => (m.detailOrderId || m.id) === id);
+					
+					// 根据后端返回的字段和当前退款类型判断是否可选
+					let isDisabled = false;
+					if (this.refundType === '1') {
+						// 退货退款
+						isDisabled = preData.canRefund === 0;
+					} else if (this.refundType === '2') {
+						// 仅退款
+						isDisabled = preData.canRefundOnlyMoney === 0;
 					}
 					
-					// 切换退款类型时,重置自定义退款金额
-					this.customRefundMoney = '';
-				},
-			confirmReason(e) {
-				this.refundReason = e[0].value;
-				this.refundReasonText = e[0].label;
+					let checked = true;
+					if (isDisabled) {
+						checked = false;
+					} else if (isFirst && this.targetDetailOrderId) {
+						checked = (String(id) === String(this.targetDetailOrderId));
+					} else if (existing) {
+						checked = existing.checked;
+					}
+					
+					return {
+						...item,
+						...preData, // 合并新接口的字段:canRefundNum, canRefundMoney, canModifyNum, canModifyMoney
+						disabled: isDisabled,
+						checked: checked,
+						refundNum: preData.canRefundNum || item.num,
+						refundMoney: preData.canRefundMoney || 0,
+						shopStatus: existing ? existing.shopStatus : '',
+						shopStatusText: existing ? existing.shopStatusText : '',
+						refundReason: existing ? existing.refundReason : '',
+						refundReasonText: existing ? existing.refundReasonText : '',
+						description: existing ? existing.description : '',
+						fileUrlList: existing ? existing.fileUrlList : []
+					};
+				});
 			},
-			confirmStatus(e) {
-				this.shopStatus = e[0].value;
-				this.shopStatusText = e[0].label;
+			loadRefundDetail() {
+				// 修改模式的兼容逻辑(如果需要的话,按原有代码逻辑映射到新组件数据结构)
+				this.$u.api.getNewRefundOrderDetailAjax({
+					refundOrderId: this.refundOrderId
+				}).then(res => {
+					if (res.code === 200) {
+						const data = res.data;
+						this.orderId = data.originOrderId;
+						this.refundType = String(data.refundType);
+						
+						this.mergedGoods = [{
+							...data,
+							id: data.detailOrderId,
+							checked: true,
+							refundNum: data.refundNum || 1,
+							refundMoney: data.refundMoney || 0,
+							shopStatus: String(data.shopStatus),
+							shopStatusText: String(data.shopStatus) === '1' ? '未收到货' : '已收到货',
+							refundReason: data.refundReason,
+							refundReasonText: data.refundReason,
+							description: data.description || '',
+							fileUrlList: data.fileUrlList || [],
+							// 假装可以修改,修改模式后端未返回canModify,暂设1
+							canModifyMoney: 1, 
+							canModifyNum: 1,
+							canRefundMoney: data.refundMoney || 0,
+							canRefundNum: data.refundNum || 1
+						}];
+						
+						this.address = {
+							id: data.sendAddressId,
+							name: data.sendName,
+							mobile: data.sendMobile,
+							fullAddress: data.sendAddress,
+						};
+					}
+				});
 			},
-			confirmReturnMethod(e) {
-				this.returnMethod = e[0].value;
-				this.returnMethodText = e[0].label;
+			handleStepOneNext(payload) {
+				this.selectedGoods = payload.selectedGoods;
+				this.currentStep = 2;
 			},
-			chooseAddress() {
-					uni.navigateTo({
-						url: `/pages-mine/pages/address/list?id=${this.address.id || ''}&isSelect=1`
-					});
-				},
-				validateRefundMoney() {
-					if (!this.customRefundMoney) {
-						this.customRefundMoney = this.maxRefundMoney;
-						return;
-					}
-					
-					const refund = Number(this.customRefundMoney);
-					const max = Number(this.maxRefundMoney);
+			handleSubmit(payload) {
+				const { goodsList, returnMethod, address } = payload;
+				
+				let params = {};
+				
+				if (this.isModify) {
+					// 修改申请,只针对单件商品,取选中的第一件商品数据
+					const item = goodsList[0];
+					params = {
+						orderId: this.orderId,
+						refundOrderId: this.refundOrderId,
+						refundType: this.refundType,
+						sendType: returnMethod || '',
+						addressId: (address && address.id) ? address.id : '',
+						refundNum: item.refundNum,
+						refundMoney: item.refundMoney,
+						shopStatus: item.shopStatus,
+						refundReason: item.refundReason,
+						description: item.description,
+						fileUrlList: item.fileUrlList || []
+					};
+				} else {
+					// 正常申请售后,可能是多件商品
+					const refundDetailList = goodsList.map(item => ({
+						detailOrderId: item.detailOrderId || item.id,
+						num: item.refundNum,
+						refundMoney: item.refundMoney,
+						shopStatus: item.shopStatus,
+						refundReason: item.refundReason,
+						description: item.description,
+						fileUrlList: item.fileUrlList || []
+					}));
 					
-					if (refund > max) {
-						this.$u.toast(`退款金额不能超过最大可退金额 ¥${max}`);
-						this.customRefundMoney = max.toFixed(2);
-					} else if (refund < 0) {
-						this.$u.toast('退款金额不能为负数');
-						this.customRefundMoney = '0.00';
-					}
-				},
-				confirmRefundMoney() {
-					this.validateRefundMoney();
-					this.showRefundAmountPopup = false;
-				},
-			submit() {
-				if (this.selectedGoods.length === 0) {
-					this.$u.toast('请选择退款商品');
-					return;
-				}
-				if (!this.refundReason) {
-					this.$u.toast('请选择退款原因');
-					return;
-				}
-				if (this.refundType === '1' && !this.returnMethod) {
-					this.$u.toast('请选择退货方式');
-					return;
-				}
-				if (this.refundType === '1' && this.returnMethod === '1' && !this.address.name) {
-					this.$u.toast('请选择取件地址');
-					return;
+					params = {
+						orderId: this.orderId,
+						refundDetailList: refundDetailList,
+						refundType: this.refundType,
+						sendType: returnMethod || '',
+						addressId: (address && address.id) ? address.id : ''
+					};
 				}
-
-				// 处理图片
-				let files = this.fileList;
-
-				const params = {
-					orderId: this.orderId,
-					refundDetailList: this.selectedGoods.map(item => ({
-						detailOrderId: item.detailOrderId || item.id,
-						num: item.refundNum
-					})),
-					refundType: this.refundType,
-					shopStatus: this.shopStatus,
-					refundReason: this.refundReason,
-					refundMoney: this.refundMoney,
-					sendType: this.returnMethod,
-					addressId: this.address.id || '',
-					description: this.description,
-					fileUrlList: files
-				};
-
-				// 附加地址信息(如果是上门取件)
-				if (this.refundType === '1' && this.returnMethod === '1') {
-					// 这里的参数结构需根据后端实际要求调整,这里假设后端接收 address 对象
-					params.address = this.address;
+				
+				// 如果是退货退款(1) 并且是上门取件(1),传递地址对象(兼容旧逻辑)
+				if (this.refundType === '1' && returnMethod === '1' && address) {
+					params.address = address;
 				}
 
 				const api = this.isModify ? this.$u.api.refundApplyModifyAjax : this.$u.api.applyRefundAjax;
-				// 修改模式下可能需要传入 refundOrderId (虽然截图只有 orderId, 但为了保险起见或者后端根据 orderId 找)
-				// 截图显示 Request body 包含 orderId 和 refundDetailList 等。
-				// 如果是修改,通常 orderId 指的是原订单ID。后端可能通过 orderId 找到对应的 active refund application 或者需要 refundOrderId。
-				// 根据截图,只有 body,没有 path param。body 里有 orderId。
-				// 如果是修改,我们可能需要额外传 refundOrderId 吗? 截图里没有显示 refundOrderId 字段。
-				// 假设后端通过 orderId 识别当前正在进行的退款申请进行修改,或者复用 applyRefund 的逻辑只是接口不同。
 				
+				uni.showLoading({ title: '提交中' });
 				api(params).then(res => {
+					uni.hideLoading();
 					if (res.code === 200) {
 						this.$u.toast(this.isModify ? '修改成功' : '提交成功');
 						setTimeout(() => {
@@ -583,6 +292,8 @@
 					} else {
 						this.$u.toast(res.msg || (this.isModify ? '修改失败' : '提交失败'));
 					}
+				}).catch(() => {
+					uni.hideLoading();
 				});
 			}
 		}
@@ -593,349 +304,5 @@
 	.apply-refund-page {
 		min-height: 100vh;
 		background-color: #f5f5f5;
-		padding: 20rpx;
-		padding-bottom: 140rpx; // Space for fixed footer
-	}
-
-	.card {
-		background-color: #fff;
-		border-radius: 16rpx;
-		margin-bottom: 20rpx;
-		padding: 30rpx;
-		overflow: hidden;
-
-		&.no-padding {
-			padding: 0;
-		}
-
-		.type-section {
-			margin-bottom: 30rpx;
-
-			.section-title {
-				font-size: 30rpx;
-				font-weight: bold;
-				color: #333;
-				margin-bottom: 20rpx;
-			}
-
-			.type-btn-group {
-				display: flex;
-
-				.type-btn {
-					flex: 1;
-					height: 64rpx;
-					display: flex;
-					align-items: center;
-					justify-content: center;
-					border: 2rpx solid #e5e5e5;
-					border-radius: 8rpx;
-					margin-right: 20rpx;
-					font-size: 28rpx;
-					color: #333;
-					transition: all 0.3s;
-
-					&:last-child {
-						margin-right: 0;
-					}
-
-					&.active {
-						border-color: #38C148;
-						color: #38C148;
-						background-color: rgba(56, 193, 72, 0.05);
-						font-weight: bold;
-					}
-				}
-			}
-		}
-
-		.card-header {
-			padding: 10rpx 0 20rpx;
-			border-bottom: 1rpx solid #f5f5f5;
-			margin-bottom: 20rpx;
-
-			.card-title {
-				font-size: 30rpx;
-				font-weight: bold;
-				color: #333;
-			}
-		}
-
-		.card-title {
-			// For simple title without header line
-			font-size: 30rpx;
-			font-weight: bold;
-			color: #333;
-			margin-bottom: 10rpx;
-		}
-	}
-
-	.goods-list {
-		.goods-item {
-			display: flex;
-			align-items: center;
-			margin-bottom: 30rpx;
-
-			&:last-child {
-				margin-bottom: 0;
-			}
-
-			.checkbox-box {
-				margin-right: 20rpx;
-			}
-
-			.goods-cover {
-				width: 140rpx;
-				height: 150rpx;
-				border-radius: 8rpx;
-				margin-right: 20rpx;
-			}
-
-			.goods-info {
-				flex: 1;
-				height: 160rpx;
-				display: flex;
-				flex-direction: column;
-				justify-content: space-between;
-
-				.goods-name {
-					font-size: 28rpx;
-					color: #333;
-					line-height: 1.4;
-				}
-
-				.goods-sku {
-					font-size: 24rpx;
-					color: #999;
-				}
-
-				.price-box {
-					display: flex;
-					justify-content: space-between;
-					align-items: center;
-
-					.price {
-						font-size: 30rpx;
-						color: #333;
-						font-weight: 500;
-					}
-				}
-			}
-		}
-	}
-
-	.refund-money-box {
-		display: flex;
-		align-items: center;
-
-		.money {
-			font-size: 32rpx;
-			color: #38C148; // Theme color
-			font-weight: bold;
-		}
-
-		.edit-tag {
-			display: flex;
-			align-items: center;
-			background-color: #f0f0f0;
-			padding: 4rpx 12rpx;
-			border-radius: 20rpx;
-			margin-left: 16rpx;
-
-			text {
-				font-size: 22rpx;
-				color: #666;
-				margin-left: 4rpx;
-			}
-		}
-	}
-
-	.address-box {
-		flex: 1;
-		display: flex;
-		align-items: center;
-		justify-content: flex-end;
-		text-align: right;
-		color: #333;
-		font-size: 28rpx;
-
-		.addr-detail {
-			font-size: 24rpx;
-			color: #999;
-			max-width: 300rpx;
-		}
-	}
-
-	.footer-bar {
-		position: fixed;
-		bottom: 0;
-		left: 0;
-		right: 0;
-		height: 200rpx;
-		background-color: #fff;
-		display: flex;
-		align-items: center;
-		padding: 0 30rpx;
-		box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
-		z-index: 100;
-		padding-bottom: constant(safe-area-inset-bottom);
-		padding-bottom: env(safe-area-inset-bottom);
-
-		.footer-left {
-			flex: 1;
-			display: flex;
-			align-items: center;
-
-			.label {
-				font-size: 26rpx;
-				color: #333;
-			}
-
-			.amount {
-				font-size: 36rpx;
-				color: #38C148;
-				font-weight: bold;
-				margin: 0 10rpx;
-			}
-
-			.detail-link {
-				font-size: 24rpx;
-				color: #999;
-				text-decoration: underline;
-			}
-		}
-
-		.footer-right {
-			width: 240rpx;
-		}
-	}
-
-	.popup-container {
-		padding: 30rpx;
-		background-color: #fff;
-
-		&.full-height {
-			height: 100%;
-			display: flex;
-			flex-direction: column;
-		}
-
-		.popup-header {
-			display: flex;
-			justify-content: space-between;
-			align-items: center;
-			margin-bottom: 40rpx;
-			flex-shrink: 0;
-
-			.title {
-				font-size: 32rpx;
-				font-weight: bold;
-				color: #333;
-			}
-		}
-
-		.popup-content {
-			margin-bottom: 40rpx;
-			flex: 1;
-			overflow-y: auto;
-
-			.detail-item {
-				margin-bottom: 30rpx;
-
-				.item-row {
-					display: flex;
-					align-items: center;
-					margin-bottom: 10rpx;
-
-					.item-label {
-						flex: 1;
-						margin-left: 20rpx;
-						font-size: 30rpx;
-						color: #333;
-					}
-
-					.item-value {
-						font-size: 30rpx;
-						color: #333;
-						font-weight: 500;
-					}
-				}
-
-				.sub-list {
-					padding-left: 60rpx;
-
-					.sub-item {
-						display: flex;
-						justify-content: space-between;
-						margin-top: 10rpx;
-						font-size: 26rpx;
-						color: #999;
-					}
-				}
-				
-				.money-input {
-					width: 200rpx;
-					text-align: right;
-					font-size: 30rpx;
-					color: #333;
-					font-weight: 500;
-				}
-				
-				.max-money-hint {
-					margin-top: 10rpx;
-					padding-left: 60rpx;
-					font-size: 24rpx;
-					color: #999;
-				}
-			}
-
-			.upload-textarea-box {
-				background-color: #f9f9f9;
-				padding: 20rpx;
-				border-radius: 12rpx;
-
-				.counter {
-					text-align: right;
-					font-size: 24rpx;
-					color: #999;
-					margin: 10rpx 0 0;
-				}
-			}
-
-			.upload-area {
-				margin-top: 20rpx;
-			}
-		}
-
-		.popup-footer {
-			margin-top: 20rpx;
-			flex-shrink: 0;
-
-			&.safe-area-bottom {
-				padding-bottom: constant(safe-area-inset-bottom);
-				padding-bottom: env(safe-area-inset-bottom);
-			}
-		}
-	}
-
-	.address-section {
-		padding: 26rpx 32rpx;
-	}
-
-	.address-text {
-		color: #333333;
-		font-family: PingFang SC;
-		font-weight: 400;
-	}
-
-	.address-label {
-		font-size: 28rpx;
-		color: #909399;
-		margin-right: 20rpx;
-		width: 140rpx;
-	}
-
-	.u-required {
-		color: #ff0000;
-		margin-left: 8rpx;
 	}
-</style>
+</style>

+ 19 - 8
pages-car/pages/order-detail.vue

@@ -64,14 +64,14 @@
                         <text class="num">x{{ goods.num }}</text>
                     </view>
                 </view>
-                <!-- 商品级按钮,如退款 -->
-                <!-- <view class="goods-action" v-if="['3', '8'].includes(String(orderInfo.status))">
-                    <u-button size="mini" shape="circle" plain
-                        custom-style="margin-left: 20rpx; height: 50rpx; line-height: 50rpx;">退款</u-button>
-                </view> -->
-                <view class="goods-action" v-if="['10'].includes(String(orderInfo.status))">
-                    <u-button size="mini" shape="circle" plain disabled
-                        custom-style="margin-left: 20rpx; height: 50rpx; line-height: 50rpx; background: #f5f5f5; color: #999; border: none;">退款成功</u-button>
+                <!-- 商品级按钮 -->
+                <view class="goods-action">
+                    <u-button v-if="goods.refundOrderId" size="mini" shape="circle" plain
+                        @click="goToRefundDetail(goods.refundOrderId)"
+                        custom-style="margin-left: 20rpx; height: 50rpx; line-height: 50rpx; color: #333; border-color: #ccc;">查看详情</u-button>
+                    <u-button v-show="!goods.refundOrderId" size="mini" shape="circle" plain
+                        @click="applyRefund(goods)"
+                        custom-style="margin-left: 20rpx; height: 50rpx; line-height: 50rpx; color: #333; border-color: #ccc;">申请售后</u-button>
                 </view>
             </view>
 
@@ -225,6 +225,17 @@
                     url: `/pages-car/pages/logistics-detail?orderId=${this.orderInfo.orderId}`
                 });
             },
+            goToRefundDetail(refundOrderId) {
+                uni.navigateTo({
+                    url: `/pages-car/pages/refund-detail?refundOrderId=${refundOrderId}`
+                });
+            },
+            applyRefund(goods) {
+                // 跳转到申请售后页面,并传递 orderId 和对应的 detailOrderId
+                uni.navigateTo({
+                    url: `/pages-car/pages/apply-refund?orderId=${this.orderInfo.orderId}&detailOrderId=${goods.detailOrderId}`
+                });
+            },
             handleAddressClick() {
                 if (this.orderInfo.showModifyAddress == 1) {
                     this.handleAction('address');

+ 54 - 11
pages-car/pages/refund-detail.vue

@@ -88,17 +88,18 @@
 				@click="goToFillLogistics">填写单号</u-button>
 		</view>
 
-		<!-- 商品列表 -->
-		<view class="info-card goods-card">
-			<view class="goods-item" v-for="(goods, index) in orderInfo.detailList" :key="index">
-				<image :src="goods.cover" mode="aspectFill" class="goods-cover"></image>
+		<!-- 商品信息 -->
+		<view class="info-card goods-card" v-if="orderInfo.bookName">
+			<view class="goods-item">
+				<image :src="orderInfo.cover" mode="aspectFill" class="goods-cover"></image>
 				<view class="goods-info">
-					<view class="goods-title u-line-2">{{ goods.bookName }}</view>
-					<view class="goods-sku" v-if="goods.isbn">品相:{{ goods.conditionType | conditionText }}
+					<view class="goods-title u-line-2">{{ orderInfo.bookName }}</view>
+					<view class="goods-sku" v-if="orderInfo.isbn">品相:{{ orderInfo.conditionType | conditionText }}
 					</view>
 					<view class="price-box">
-						<text class="price">¥{{ goods.price }}</text>
-						<text class="num">x{{ goods.refundNum }}</text>
+						<text class="price" v-if="orderInfo.price">¥{{ orderInfo.price }}</text>
+						<text class="price" v-else></text>
+						<text class="num">x{{ orderInfo.refundNum || 1 }}</text>
 					</view>
 					<!-- 状态Badge -->
 					<view class="refund-status-tag red-tag" v-if="orderInfo.status == '1'">
@@ -123,6 +124,14 @@
 					<u-icon name="lightning-fill" color="#38C148" size="28" style="margin-left: 6rpx;"></u-icon>
 				</view>
 			</view>
+			
+			<view class="row history-row" @click="openRefundHistory">
+				<text class="label">历史退款记录</text>
+				<view class="value flex-row link-text">
+					<text>查看</text>
+					<u-icon name="lightning-fill" color="#38C148" size="28" style="margin-left: 6rpx;"></u-icon>
+				</view>
+			</view>
 
 			<view class="row">
 				<text class="label">退款原因</text>
@@ -180,14 +189,18 @@
 			</button>
 			<!-- #endif -->
 		</FloatingDrag>
+		
+		<refund-history-popup ref="historyPopup" @view="handleViewHistoryRefund"></refund-history-popup>
 	</view>
 </template>
 
 <script>
 	import FloatingDrag from "@/components/floating-drag.vue";
+	import RefundHistoryPopup from "../components/refund-history-popup.vue";
 	export default {
 		components: {
-			FloatingDrag
+			FloatingDrag,
+			RefundHistoryPopup
 		},
 		data() {
 			return {
@@ -201,7 +214,13 @@
 				orderInfo: {
 					status: '',
 					restAuditSecond: 0,
-					detailList: [],
+					detailOrderId: '',
+					bookName: '',
+					cover: '',
+					isbn: '',
+					conditionType: '',
+					refundNum: 0,
+					price: 0,
 					refundMoney: 0,
 					refundReason: '',
 					createTime: '',
@@ -332,6 +351,30 @@
 					url: `/pages-car/pages/negotiation-history?refundOrderId=${this.refundOrderId}`
 				});
 			},
+			openRefundHistory() {
+				const orderDetailId = this.orderInfo.detailOrderId || this.orderInfo.orderDetailId || this.orderInfo.detailId;
+				if (!orderDetailId) {
+					uni.showToast({ title: '缺少订单详情ID', icon: 'none' });
+					return;
+				}
+				uni.showLoading({ title: '加载中' });
+				this.$u.api.getRefundOrderDetailListAjax({ orderDetailId }).then(res => {
+					uni.hideLoading();
+					if (res.code == 200) {
+						this.$refs.historyPopup && this.$refs.historyPopup.open(res.data || []);
+					} else {
+						uni.showToast({ title: res.msg || '获取失败', icon: 'none' });
+					}
+				}).catch(() => {
+					uni.hideLoading();
+				});
+			},
+			handleViewHistoryRefund(item) {
+				if (!item || !item.refundOrderId) return;
+				uni.redirectTo({
+					url: `/pages-car/pages/refund-detail?refundOrderId=${item.refundOrderId}`
+				});
+			},
 			navigateToCustomerService() {
 				// 联系客服
 				uni.navigateTo({
@@ -767,4 +810,4 @@
 			}
 		}
 	}
-</style>
+</style>