feat: 新增订单详情退款弹窗
This commit is contained in:
parent
a8a5a90d5e
commit
72c761428b
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="login-wrapper">
|
<view class="login-wrapper" :style="{ backgroundImage: `url(${loginBg})` }">
|
||||||
<image class="bg" src="./images/bg.png"></image>
|
|
||||||
<!-- 头部内容 -->
|
<!-- 头部内容 -->
|
||||||
<view class="login-header">
|
<view class="login-header">
|
||||||
<!-- 卡通形象 -->
|
<!-- 卡通形象 -->
|
||||||
@ -66,6 +65,7 @@ import CheckBox from "@/components/CheckBox/index.vue";
|
|||||||
import AgreePopup from "./components/AgreePopup/index.vue";
|
import AgreePopup from "./components/AgreePopup/index.vue";
|
||||||
import { loginAuth, bindPhone, checkPhone } from "@/manager/LoginManager";
|
import { loginAuth, bindPhone, checkPhone } from "@/manager/LoginManager";
|
||||||
import { goHome } from "@/hooks/useGoHome";
|
import { goHome } from "@/hooks/useGoHome";
|
||||||
|
import loginBg from "./images/bg.png";
|
||||||
|
|
||||||
const isAgree = ref(false);
|
const isAgree = ref(false);
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
|
|||||||
@ -7,21 +7,13 @@
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
padding-top: 168px;
|
padding-top: 168px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
background-position: 0 0;
|
||||||
.bg {
|
background-size: 100% 100%;
|
||||||
position: absolute;
|
background-repeat: no-repeat;
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: -1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.login-header {
|
.login-header {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
max-height: 223px;
|
||||||
|
|
||||||
.login-avatar {
|
.login-avatar {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
@ -35,7 +27,7 @@
|
|||||||
|
|
||||||
.login-desc {
|
.login-desc {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #1E4C69;
|
color: #1e4c69;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +37,7 @@
|
|||||||
width: 297px;
|
width: 297px;
|
||||||
|
|
||||||
.login-btn {
|
.login-btn {
|
||||||
background: linear-gradient( 246deg, #22A7FF 0%, #2567FF 100%);
|
background: linear-gradient(246deg, #22a7ff 0%, #2567ff 100%);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 50px;
|
border-radius: 50px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, computed, ref } from "vue";
|
import { defineProps, computed, ref, defineEmits } from "vue";
|
||||||
import {
|
import {
|
||||||
preOrder,
|
preOrder,
|
||||||
orderPayNow,
|
orderPayNow,
|
||||||
@ -56,6 +56,9 @@ const PAY_WAY_MAP = {
|
|||||||
// 加载状态
|
// 加载状态
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
|
||||||
|
// 定义事件发射器
|
||||||
|
const emit = defineEmits(['show-refund-popup']);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
orderData: {
|
orderData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -111,18 +114,15 @@ const handleButtonClick = async () => {
|
|||||||
// 支付渠道
|
// 支付渠道
|
||||||
const paySource = "1";
|
const paySource = "1";
|
||||||
|
|
||||||
|
if (status === "2") {
|
||||||
|
// 情况2:待使用状态,显示退款弹窗
|
||||||
|
emit('show-refund-popup');
|
||||||
|
return; // 直接返回,不执行后续代码
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
|
||||||
if (status === "2") {
|
|
||||||
// 情况2:待使用状态,直接申请退款
|
|
||||||
await orderRefund({ orderId });
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: "退款申请已提交",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 情况1:待支付状态或其他状态,先预下单再支付
|
// 情况1:待支付状态或其他状态,先预下单再支付
|
||||||
// 第一步:预下单
|
// 第一步:预下单
|
||||||
const res = await orderPayNow({ orderId, payWay, paySource });
|
const res = await orderPayNow({ orderId, payWay, paySource });
|
||||||
@ -143,7 +143,6 @@ const handleButtonClick = async () => {
|
|||||||
console.log("fail:" + JSON.stringify(err));
|
console.log("fail:" + JSON.stringify(err));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("操作失败:", error);
|
console.error("操作失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
|
|||||||
158
pages/order/components/RefundPopup/README.md
Normal file
158
pages/order/components/RefundPopup/README.md
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
# RefundPopup 退款弹窗组件
|
||||||
|
|
||||||
|
## 组件概述
|
||||||
|
`RefundPopup` 是一个用于处理订单退款相关操作的弹窗组件,支持多种退款场景和状态展示。
|
||||||
|
|
||||||
|
## 功能需求分析
|
||||||
|
|
||||||
|
### 界面设计规范
|
||||||
|
1. **弹窗容器**
|
||||||
|
- 使用圆角矩形容器,背景色为白色
|
||||||
|
- 弹窗宽度适中,居中显示
|
||||||
|
- 支持遮罩层,点击遮罩可关闭弹窗
|
||||||
|
|
||||||
|
2. **头部区域**
|
||||||
|
- 显示可爱的花朵卡通形象作为视觉元素
|
||||||
|
- 卡通形象位于弹窗顶部中央位置
|
||||||
|
|
||||||
|
3. **内容区域**
|
||||||
|
- 主标题:根据不同场景显示相应提示文字
|
||||||
|
- 副标题:显示详细的退款规则和说明
|
||||||
|
- 金额显示:突出显示可退金额(橙色字体)
|
||||||
|
- 退款政策:详细列出各种退款条件和规则
|
||||||
|
|
||||||
|
4. **按钮区域**
|
||||||
|
- 双按钮布局:左侧为次要操作,右侧为主要操作
|
||||||
|
- 按钮样式:圆角矩形,左侧蓝色,右侧橙色
|
||||||
|
- 按钮文字:根据场景显示不同的操作文字
|
||||||
|
|
||||||
|
### 交互功能
|
||||||
|
1. **弹窗显示/隐藏**
|
||||||
|
- 支持通过方法调用显示弹窗
|
||||||
|
- 支持点击遮罩层关闭弹窗
|
||||||
|
- 支持点击按钮后关闭弹窗
|
||||||
|
|
||||||
|
2. **退款场景处理**
|
||||||
|
- **不予退款场景**:显示"不予退款:12小时以内取消或未入住,不予退款"
|
||||||
|
- **免费取消场景**:显示"免费取消:提前48小时以上,全额退还房费"
|
||||||
|
- **部分退款场景**:显示退款政策详情和可退金额
|
||||||
|
- **商品限制场景**:显示"该商品未使用随时可退"
|
||||||
|
|
||||||
|
3. **按钮交互**
|
||||||
|
- 左侧按钮:"退订政策"或"退订政策"(查看详情)
|
||||||
|
- 右侧按钮:"我知道了"或"点击退款"(确认操作)
|
||||||
|
- 支持按钮点击事件回调
|
||||||
|
|
||||||
|
4. **数据展示**
|
||||||
|
- 动态显示可退金额
|
||||||
|
- 展示详细的退款规则列表
|
||||||
|
- 支持不同退款类型的文案切换
|
||||||
|
|
||||||
|
### 技术要求
|
||||||
|
1. **组件架构**
|
||||||
|
- 使用 Vue 3 Composition API
|
||||||
|
- 支持 TypeScript(可选)
|
||||||
|
- 使用 uni-popup 作为弹窗基础组件
|
||||||
|
|
||||||
|
2. **样式处理**
|
||||||
|
- 使用 SASS 预处理器
|
||||||
|
- 支持响应式设计
|
||||||
|
- 遵循设计规范的颜色和字体
|
||||||
|
|
||||||
|
3. **性能优化**
|
||||||
|
- 懒加载弹窗内容
|
||||||
|
- 合理使用计算属性
|
||||||
|
- 避免不必要的重渲染
|
||||||
|
|
||||||
|
4. **代码规范**
|
||||||
|
- 清晰的组件结构
|
||||||
|
- 详细的注释说明
|
||||||
|
- 统一的命名规范
|
||||||
|
|
||||||
|
### 样式规范
|
||||||
|
1. **颜色规范**
|
||||||
|
- 主色调:蓝色 #007AFF(左侧按钮)
|
||||||
|
- 强调色:橙色 #FF9500(右侧按钮、金额)
|
||||||
|
- 文字色:黑色 #000000(主要文字)
|
||||||
|
- 辅助色:灰色 #666666(辅助文字)
|
||||||
|
- 背景色:白色 #FFFFFF
|
||||||
|
|
||||||
|
2. **字体规范**
|
||||||
|
- 主标题:16px,加粗
|
||||||
|
- 副标题:14px,常规
|
||||||
|
- 金额:18px,加粗,橙色
|
||||||
|
- 按钮文字:16px,加粗
|
||||||
|
- 说明文字:12px,常规
|
||||||
|
|
||||||
|
3. **间距规范**
|
||||||
|
- 弹窗内边距:20px
|
||||||
|
- 元素间距:12px
|
||||||
|
- 按钮间距:12px
|
||||||
|
- 按钮高度:44px
|
||||||
|
|
||||||
|
### 组件接口
|
||||||
|
|
||||||
|
#### Props
|
||||||
|
```typescript
|
||||||
|
interface RefundPopupProps {
|
||||||
|
// 弹窗显示状态
|
||||||
|
visible: boolean
|
||||||
|
// 退款类型:'no_refund' | 'free_cancel' | 'partial_refund' | 'anytime_refund'
|
||||||
|
refundType: string
|
||||||
|
// 可退金额
|
||||||
|
refundAmount?: number
|
||||||
|
// 退款规则列表
|
||||||
|
refundRules?: string[]
|
||||||
|
// 自定义标题
|
||||||
|
title?: string
|
||||||
|
// 自定义描述
|
||||||
|
description?: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Events
|
||||||
|
```typescript
|
||||||
|
interface RefundPopupEvents {
|
||||||
|
// 弹窗关闭事件
|
||||||
|
'update:visible': (visible: boolean) => void
|
||||||
|
// 查看政策按钮点击
|
||||||
|
'policy-click': () => void
|
||||||
|
// 确认按钮点击
|
||||||
|
'confirm-click': () => void
|
||||||
|
// 弹窗关闭事件
|
||||||
|
'close': () => void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Methods
|
||||||
|
```typescript
|
||||||
|
interface RefundPopupMethods {
|
||||||
|
// 显示弹窗
|
||||||
|
show(): void
|
||||||
|
// 隐藏弹窗
|
||||||
|
hide(): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用场景
|
||||||
|
1. **订单详情页面**:用户查看退款政策
|
||||||
|
2. **退款申请流程**:确认退款操作
|
||||||
|
3. **客服咨询场景**:展示退款规则
|
||||||
|
4. **订单管理后台**:处理退款申请
|
||||||
|
|
||||||
|
### 文件结构
|
||||||
|
```
|
||||||
|
RefundPopup/
|
||||||
|
├── index.vue # 主组件文件
|
||||||
|
├── styles/
|
||||||
|
│ └── index.scss # 样式文件
|
||||||
|
├── demo.vue # 演示页面
|
||||||
|
└── README.md # 组件文档
|
||||||
|
```
|
||||||
|
|
||||||
|
### 开发注意事项
|
||||||
|
1. 确保弹窗在不同屏幕尺寸下的适配
|
||||||
|
2. 处理长文本的换行和显示
|
||||||
|
3. 考虑无障碍访问支持
|
||||||
|
4. 添加适当的动画效果
|
||||||
|
5. 确保组件的可复用性和可扩展性
|
||||||
389
pages/order/components/RefundPopup/demo.vue
Normal file
389
pages/order/components/RefundPopup/demo.vue
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
<template>
|
||||||
|
<view class="demo-container">
|
||||||
|
<view class="demo-header">
|
||||||
|
<text class="demo-title">RefundPopup 退款弹窗组件演示</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="demo-content">
|
||||||
|
<!-- 场景选择 -->
|
||||||
|
<view class="demo-section">
|
||||||
|
<text class="section-title">退款场景</text>
|
||||||
|
<view class="scenario-buttons">
|
||||||
|
<button
|
||||||
|
class="scenario-btn"
|
||||||
|
:class="{ active: currentScenario === 'no_refund' }"
|
||||||
|
@click="setScenario('no_refund')"
|
||||||
|
>
|
||||||
|
不予退款
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="scenario-btn"
|
||||||
|
:class="{ active: currentScenario === 'free_cancel' }"
|
||||||
|
@click="setScenario('free_cancel')"
|
||||||
|
>
|
||||||
|
免费取消
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="scenario-btn"
|
||||||
|
:class="{ active: currentScenario === 'partial_refund' }"
|
||||||
|
@click="setScenario('partial_refund')"
|
||||||
|
>
|
||||||
|
部分退款
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="scenario-btn"
|
||||||
|
:class="{ active: currentScenario === 'anytime_refund' }"
|
||||||
|
@click="setScenario('anytime_refund')"
|
||||||
|
>
|
||||||
|
随时可退
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 金额设置 -->
|
||||||
|
<view class="demo-section">
|
||||||
|
<text class="section-title">退款金额</text>
|
||||||
|
<view class="amount-input">
|
||||||
|
<input
|
||||||
|
class="amount-field"
|
||||||
|
type="number"
|
||||||
|
v-model="refundAmount"
|
||||||
|
placeholder="请输入退款金额"
|
||||||
|
/>
|
||||||
|
<text class="amount-unit">元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<view class="demo-section">
|
||||||
|
<button class="demo-btn primary" @click="showPopup">
|
||||||
|
显示退款弹窗
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 事件日志 -->
|
||||||
|
<view class="demo-section">
|
||||||
|
<text class="section-title">事件日志</text>
|
||||||
|
<view class="event-log">
|
||||||
|
<view
|
||||||
|
class="log-item"
|
||||||
|
v-for="(log, index) in eventLogs"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<text class="log-time">{{ log.time }}</text>
|
||||||
|
<text class="log-event">{{ log.event }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="log-empty" v-if="eventLogs.length === 0">
|
||||||
|
暂无事件日志
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<button class="demo-btn secondary" @click="clearLogs">
|
||||||
|
清空日志
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- RefundPopup 组件 -->
|
||||||
|
<RefundPopup
|
||||||
|
v-model="popupVisible"
|
||||||
|
:refund-type="currentScenario"
|
||||||
|
:refund-amount="refundAmount"
|
||||||
|
:refund-rules="customRules"
|
||||||
|
@policy-click="handlePolicyClick"
|
||||||
|
@confirm-click="handleConfirmClick"
|
||||||
|
@close="handleClose"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import RefundPopup from './index.vue'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const popupVisible = ref(false)
|
||||||
|
const currentScenario = ref('no_refund')
|
||||||
|
const refundAmount = ref(399)
|
||||||
|
const eventLogs = ref([])
|
||||||
|
|
||||||
|
// 自定义退款规则(可选)
|
||||||
|
const customRules = ref([])
|
||||||
|
|
||||||
|
// 方法定义
|
||||||
|
const setScenario = (scenario) => {
|
||||||
|
currentScenario.value = scenario
|
||||||
|
addLog(`切换到场景: ${getScenarioName(scenario)}`)
|
||||||
|
|
||||||
|
// 根据场景设置默认金额
|
||||||
|
switch (scenario) {
|
||||||
|
case 'no_refund':
|
||||||
|
refundAmount.value = 0
|
||||||
|
break
|
||||||
|
case 'free_cancel':
|
||||||
|
refundAmount.value = 399
|
||||||
|
break
|
||||||
|
case 'partial_refund':
|
||||||
|
refundAmount.value = 199
|
||||||
|
break
|
||||||
|
case 'anytime_refund':
|
||||||
|
refundAmount.value = 399
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getScenarioName = (scenario) => {
|
||||||
|
const names = {
|
||||||
|
'no_refund': '不予退款',
|
||||||
|
'free_cancel': '免费取消',
|
||||||
|
'partial_refund': '部分退款',
|
||||||
|
'anytime_refund': '随时可退'
|
||||||
|
}
|
||||||
|
return names[scenario] || scenario
|
||||||
|
}
|
||||||
|
|
||||||
|
const showPopup = () => {
|
||||||
|
popupVisible.value = true
|
||||||
|
addLog('显示退款弹窗')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePolicyClick = () => {
|
||||||
|
addLog('点击了退订政策按钮')
|
||||||
|
// 这里可以跳转到政策详情页面
|
||||||
|
uni.showToast({
|
||||||
|
title: '查看退订政策',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConfirmClick = () => {
|
||||||
|
addLog(`确认操作 - 场景: ${getScenarioName(currentScenario.value)}, 金额: ¥${refundAmount.value}`)
|
||||||
|
|
||||||
|
// 根据不同场景执行不同操作
|
||||||
|
if (currentScenario.value === 'no_refund') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '已知晓退款政策',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '退款申请已提交',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
addLog('关闭退款弹窗')
|
||||||
|
}
|
||||||
|
|
||||||
|
const addLog = (event) => {
|
||||||
|
const now = new Date()
|
||||||
|
const time = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
|
||||||
|
|
||||||
|
eventLogs.value.unshift({
|
||||||
|
time,
|
||||||
|
event
|
||||||
|
})
|
||||||
|
|
||||||
|
// 限制日志数量
|
||||||
|
if (eventLogs.value.length > 10) {
|
||||||
|
eventLogs.value = eventLogs.value.slice(0, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearLogs = () => {
|
||||||
|
eventLogs.value = []
|
||||||
|
addLog('清空事件日志')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
addLog('演示页面已加载')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.demo-container {
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
.demo-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-content {
|
||||||
|
.demo-section {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.scenario-btn {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 80px;
|
||||||
|
height: 40px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #666666;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: #007aff;
|
||||||
|
background: #007aff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-input {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.amount-field {
|
||||||
|
flex: 1;
|
||||||
|
height: 44px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0 16px;
|
||||||
|
font-size: 16px;
|
||||||
|
background: #ffffff;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #007aff;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-unit {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666666;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: #007aff;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #0056cc;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.secondary {
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: #666666;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e0e0e0;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-log {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f9f9f9;
|
||||||
|
|
||||||
|
.log-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-time {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999999;
|
||||||
|
font-family: monospace;
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-event {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-empty {
|
||||||
|
text-align: center;
|
||||||
|
color: #999999;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式适配
|
||||||
|
@media screen and (max-width: 375px) {
|
||||||
|
.demo-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-content {
|
||||||
|
.demo-section {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-buttons {
|
||||||
|
.scenario-btn {
|
||||||
|
min-width: 70px;
|
||||||
|
height: 36px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
274
pages/order/components/RefundPopup/example.vue
Normal file
274
pages/order/components/RefundPopup/example.vue
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
<template>
|
||||||
|
<view class="example-page">
|
||||||
|
<view class="page-header">
|
||||||
|
<text class="page-title">订单详情</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="order-info">
|
||||||
|
<view class="order-item">
|
||||||
|
<text class="item-name">鲜牛肉红烧牛肉面二两+例汤1份</text>
|
||||||
|
<text class="item-price">¥128</text>
|
||||||
|
</view>
|
||||||
|
<view class="order-item">
|
||||||
|
<text class="item-name">红油干拌鲜肉云吞+例汤1份</text>
|
||||||
|
<text class="item-price">¥50</text>
|
||||||
|
</view>
|
||||||
|
<view class="order-total">
|
||||||
|
<text class="total-label">总计:</text>
|
||||||
|
<text class="total-price">¥178</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="order-actions">
|
||||||
|
<button class="action-button refund-btn" @click="showRefundPopup">
|
||||||
|
申请退款
|
||||||
|
</button>
|
||||||
|
<button class="action-button contact-btn" @click="contactService">
|
||||||
|
联系客服
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 退款弹窗组件 -->
|
||||||
|
<RefundPopup
|
||||||
|
v-model="refundVisible"
|
||||||
|
:refund-type="refundType"
|
||||||
|
:refund-amount="refundAmount"
|
||||||
|
@policy-click="viewRefundPolicy"
|
||||||
|
@confirm-click="handleRefundConfirm"
|
||||||
|
@close="handleRefundClose"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import RefundPopup from './index.vue'
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const refundVisible = ref(false)
|
||||||
|
const refundType = ref('partial_refund')
|
||||||
|
const refundAmount = ref(89) // 50% 退款
|
||||||
|
|
||||||
|
// 方法定义
|
||||||
|
const showRefundPopup = () => {
|
||||||
|
// 根据订单状态和时间判断退款类型
|
||||||
|
const orderTime = new Date('2024-01-15 10:00:00')
|
||||||
|
const currentTime = new Date()
|
||||||
|
const hoursDiff = (currentTime - orderTime) / (1000 * 60 * 60)
|
||||||
|
|
||||||
|
if (hoursDiff < 12) {
|
||||||
|
refundType.value = 'no_refund'
|
||||||
|
refundAmount.value = 0
|
||||||
|
} else if (hoursDiff < 24) {
|
||||||
|
refundType.value = 'partial_refund'
|
||||||
|
refundAmount.value = 89 // 50% 退款
|
||||||
|
} else if (hoursDiff < 48) {
|
||||||
|
refundType.value = 'free_cancel'
|
||||||
|
refundAmount.value = 178 // 全额退款
|
||||||
|
} else {
|
||||||
|
refundType.value = 'anytime_refund'
|
||||||
|
refundAmount.value = 178
|
||||||
|
}
|
||||||
|
|
||||||
|
refundVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewRefundPolicy = () => {
|
||||||
|
// 跳转到退款政策页面
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/policy/refund'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRefundConfirm = () => {
|
||||||
|
if (refundType.value === 'no_refund') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '已了解退款政策',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交退款申请
|
||||||
|
uni.showLoading({
|
||||||
|
title: '提交中...'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 模拟API调用
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.hideLoading()
|
||||||
|
uni.showToast({
|
||||||
|
title: '退款申请已提交',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 可以跳转到退款状态页面
|
||||||
|
// uni.navigateTo({
|
||||||
|
// url: '/pages/refund/status'
|
||||||
|
// })
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRefundClose = () => {
|
||||||
|
console.log('退款弹窗已关闭')
|
||||||
|
}
|
||||||
|
|
||||||
|
const contactService = () => {
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['在线客服', '电话客服', '意见反馈'],
|
||||||
|
success: (res) => {
|
||||||
|
switch (res.tapIndex) {
|
||||||
|
case 0:
|
||||||
|
// 打开在线客服
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/service/chat'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
// 拨打客服电话
|
||||||
|
uni.makePhoneCall({
|
||||||
|
phoneNumber: '400-123-4567'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
// 打开意见反馈
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/feedback/index'
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.example-page {
|
||||||
|
padding: 20px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.order-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-price {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #ff6b35;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-total {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 12px;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-top: 2px solid #f0f0f0;
|
||||||
|
|
||||||
|
.total-label {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-price {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #ff6b35;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.refund-btn {
|
||||||
|
background: #ff6b35;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e55a2b;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.contact-btn {
|
||||||
|
background: #007aff;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #0056cc;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式适配
|
||||||
|
@media screen and (max-width: 375px) {
|
||||||
|
.example-page {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-info {
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-actions {
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
height: 44px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
217
pages/order/components/RefundPopup/index.vue
Normal file
217
pages/order/components/RefundPopup/index.vue
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<template>
|
||||||
|
<uni-popup ref="popupRef" type="center" @maskClick="handleClose">
|
||||||
|
<view class="refund-popup">
|
||||||
|
<!-- 头部卡通形象 -->
|
||||||
|
<image
|
||||||
|
src="@/static/dh.png"
|
||||||
|
class="refund-popup__avatar"
|
||||||
|
mode="widthFix"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="refund-popup__content">
|
||||||
|
<!-- 主标题 -->
|
||||||
|
<view class="refund-popup__title">{{ currentTitle }}</view>
|
||||||
|
|
||||||
|
<!-- 金额显示 -->
|
||||||
|
<view class="refund-popup__amount" v-if="showAmount">
|
||||||
|
<text class="amount-symbol">¥</text>
|
||||||
|
<text class="amount-value">{{ refundAmount }}</text>
|
||||||
|
<text class="amount-unit">元</text>
|
||||||
|
</view>
|
||||||
|
<view class="refund-popup__amount-label" v-if="showAmount"
|
||||||
|
>可退金额</view
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- 描述信息 -->
|
||||||
|
<view class="refund-popup__description" v-if="currentDescription">
|
||||||
|
{{ currentDescription }}
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 退款政策详情 -->
|
||||||
|
<view class="refund-popup__policy" v-if="showPolicy">
|
||||||
|
<view class="policy-title">退订政策:</view>
|
||||||
|
<view class="policy-list">
|
||||||
|
<view
|
||||||
|
class="policy-item"
|
||||||
|
v-for="(rule, index) in currentRules"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ rule }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 按钮区域 -->
|
||||||
|
<view class="refund-popup__actions">
|
||||||
|
<view class="action-btn secondary-btn" @click="handlePolicyClick">
|
||||||
|
{{ leftButtonText }}
|
||||||
|
</view>
|
||||||
|
<view class="action-btn primary-btn" @click="handleConfirmClick">
|
||||||
|
{{ rightButtonText }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, shallowRef } from "vue";
|
||||||
|
|
||||||
|
// Props定义
|
||||||
|
const props = defineProps({
|
||||||
|
// 弹窗显示状态
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
// 退款类型
|
||||||
|
refundType: {
|
||||||
|
type: String,
|
||||||
|
default: "no_refund",
|
||||||
|
validator: (value) =>
|
||||||
|
["no_refund", "all_refund", "anytime_refund"].includes(value),
|
||||||
|
},
|
||||||
|
// 可退金额
|
||||||
|
refundAmount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
// 退款规则列表
|
||||||
|
refundRules: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
// 自定义标题
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
// 自定义描述
|
||||||
|
description: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Events定义
|
||||||
|
const emit = defineEmits([
|
||||||
|
"update:modelValue",
|
||||||
|
"policy-click",
|
||||||
|
"confirm-click",
|
||||||
|
"close",
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 弹窗引用
|
||||||
|
const popupRef = ref(null);
|
||||||
|
|
||||||
|
// 退款场景配置(使用shallowRef优化性能)
|
||||||
|
const refundScenarios = shallowRef({
|
||||||
|
no_refund: {
|
||||||
|
title: "您在入住日期12小时以内申请退款,不可退款,如有疑问请咨询客服",
|
||||||
|
description: "",
|
||||||
|
showAmount: false,
|
||||||
|
showPolicy: false,
|
||||||
|
leftButton: "退订政策",
|
||||||
|
rightButton: "我知道了",
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
all_refund: {
|
||||||
|
title: "您在入住日期24小时内申请退款,可退还100%金额",
|
||||||
|
description: "",
|
||||||
|
showAmount: true,
|
||||||
|
showPolicy: true,
|
||||||
|
leftButton: "退订政策",
|
||||||
|
rightButton: "点击退款",
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
anytime_refund: {
|
||||||
|
title: "该商品未使用随时可退",
|
||||||
|
description: "",
|
||||||
|
showAmount: true,
|
||||||
|
showPolicy: false,
|
||||||
|
leftButton: "退订政策",
|
||||||
|
rightButton: "点击退款",
|
||||||
|
rules: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
const currentScenario = computed(
|
||||||
|
() =>
|
||||||
|
refundScenarios.value[props.refundType] || refundScenarios.value.no_refund
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentTitle = computed(() => props.title || currentScenario.value.title);
|
||||||
|
|
||||||
|
const currentDescription = computed(
|
||||||
|
() => props.description || currentScenario.value.description
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRules = computed(() => {
|
||||||
|
if (props.refundRules.length) {
|
||||||
|
return props.refundRules;
|
||||||
|
}
|
||||||
|
return currentScenario.value.rules;
|
||||||
|
});
|
||||||
|
|
||||||
|
const showAmount = computed(
|
||||||
|
() => currentScenario.value.showAmount && props.refundAmount > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const showPolicy = computed(
|
||||||
|
() => currentScenario.value.showPolicy && currentRules.value.length > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
const leftButtonText = computed(() => currentScenario.value.leftButton);
|
||||||
|
|
||||||
|
const rightButtonText = computed(() => currentScenario.value.rightButton);
|
||||||
|
|
||||||
|
// 方法定义
|
||||||
|
const show = () => popupRef.value.open();
|
||||||
|
|
||||||
|
const hide = () => popupRef.value.close();
|
||||||
|
|
||||||
|
// 监听modelValue变化
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
show();
|
||||||
|
} else {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听退款金额变化,进行数据验证
|
||||||
|
watch(
|
||||||
|
() => props.refundAmount,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal < 0) {
|
||||||
|
console.warn("RefundPopup: 退款金额不能为负数");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
emit("update:modelValue", false);
|
||||||
|
emit("close");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePolicyClick = () => {
|
||||||
|
emit("policy-click");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmClick = () => {
|
||||||
|
emit("confirm-click");
|
||||||
|
handleClose();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "./styles/index.scss";
|
||||||
|
</style>
|
||||||
168
pages/order/components/RefundPopup/styles/index.scss
Normal file
168
pages/order/components/RefundPopup/styles/index.scss
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// RefundPopup 退款弹窗样式
|
||||||
|
.refund-popup {
|
||||||
|
width: 320px;
|
||||||
|
background: linear-gradient(173deg, #cbf6ff 3%, #ffffff 32%);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 64px;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
// 头部区域
|
||||||
|
&__avatar {
|
||||||
|
width: 132px;
|
||||||
|
height: 132px;
|
||||||
|
position: absolute;
|
||||||
|
top: -45px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容区域
|
||||||
|
&__content {
|
||||||
|
padding: 12px 20px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
line-height: 22px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
|
||||||
|
.amount-symbol {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ff6a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-value {
|
||||||
|
font-size: 24px;
|
||||||
|
color: #ff6a00;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount-unit {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ff6a00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__policy {
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.policy-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #007aff;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.policy-list {
|
||||||
|
.policy-item {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: justify;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮区域
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 0 20px 20px;
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&.secondary-btn {
|
||||||
|
background: #007aff;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #0056cc;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary-btn {
|
||||||
|
background: #ff9500;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e6850e;
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动画效果
|
||||||
|
@keyframes popupFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flowerBounce {
|
||||||
|
0%,
|
||||||
|
20%,
|
||||||
|
50%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.refund-popup {
|
||||||
|
animation: popupFadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
@ -12,26 +12,41 @@
|
|||||||
<GoodsInfo :orderData="orderData" />
|
<GoodsInfo :orderData="orderData" />
|
||||||
<UserInfo :orderData="orderData" />
|
<UserInfo :orderData="orderData" />
|
||||||
<NoticeInfo :orderData="orderData" />
|
<NoticeInfo :orderData="orderData" />
|
||||||
<OrderInfo :orderData="orderData" />
|
<OrderInfo :orderData="orderData" @show-refund-popup="showRefundPopup" />
|
||||||
|
|
||||||
|
<!-- 退款状态显示 -->
|
||||||
|
<RefundPopup
|
||||||
|
v-model="refundVisible"
|
||||||
|
:refund-type="refundType"
|
||||||
|
:refund-amount="refundAmount"
|
||||||
|
@policy-click="viewRefundPolicy"
|
||||||
|
@confirm-click="handleRefundConfirm"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { userOrderDetail } from "@/request/api/OrderApi";
|
import { userOrderDetail, orderRefund } from "@/request/api/OrderApi";
|
||||||
import OrderQrcode from "./components/OrderQrcode/index.vue";
|
import OrderQrcode from "./components/OrderQrcode/index.vue";
|
||||||
import OrderStatusInfo from "./components/OrderStatusInfo/index.vue";
|
import OrderStatusInfo from "./components/OrderStatusInfo/index.vue";
|
||||||
import GoodsInfo from "./components/GoodsInfo/index.vue";
|
import GoodsInfo from "./components/GoodsInfo/index.vue";
|
||||||
import UserInfo from "./components/UserInfo/index.vue";
|
import UserInfo from "./components/UserInfo/index.vue";
|
||||||
import NoticeInfo from "./components/NoticeInfo/index.vue";
|
import NoticeInfo from "./components/NoticeInfo/index.vue";
|
||||||
import OrderInfo from "./components/OrderInfo/index.vue";
|
import OrderInfo from "./components/OrderInfo/index.vue";
|
||||||
|
import RefundPopup from "./components/RefundPopup/index.vue";
|
||||||
|
|
||||||
|
const refundVisible = ref(false);
|
||||||
|
const refundType = ref("free_cancel"); // 默认退款类型
|
||||||
|
const refundAmount = ref(0); // 退款金额
|
||||||
const orderData = ref({});
|
const orderData = ref({});
|
||||||
onLoad(async ({ orderId }) => {
|
onLoad(async ({ orderId }) => {
|
||||||
const res = await userOrderDetail({ orderId });
|
const res = await userOrderDetail({ orderId });
|
||||||
|
|
||||||
orderData.value = res.data;
|
orderData.value = res.data;
|
||||||
|
// 设置退款金额为订单支付金额
|
||||||
|
refundAmount.value = parseFloat(res.data.payAmt || 0);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -41,6 +56,42 @@ const goBack = () => {
|
|||||||
delta: 1,
|
delta: 1,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 显示退款弹窗
|
||||||
|
const showRefundPopup = () => {
|
||||||
|
refundVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看退款政策
|
||||||
|
const viewRefundPolicy = () => {
|
||||||
|
console.log("查看退款政策");
|
||||||
|
// 这里可以跳转到退款政策页面或显示详细政策
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认退款
|
||||||
|
const handleRefundConfirm = async () => {
|
||||||
|
try {
|
||||||
|
// 调用退款API
|
||||||
|
await orderRefund({ orderId: orderData.value.orderId });
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "退款申请已提交",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新订单状态
|
||||||
|
const res = await userOrderDetail({ orderId: orderData.value.orderId });
|
||||||
|
orderData.value = res.data;
|
||||||
|
// 更新退款金额
|
||||||
|
refundAmount.value = parseFloat(res.data.payAmt || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("退款失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "退款申请失败,请重试",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
BIN
static/dh.png
Normal file
BIN
static/dh.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
Loading…
Reference in New Issue
Block a user