Merge branch 'fix-109' of https://git.nianxx.cn/zoujing/YGChatCS
This commit is contained in:
102
src/components/DetailPopup/index.vue
Normal file
102
src/components/DetailPopup/index.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="popupRef"
|
||||
type="bottom"
|
||||
:safe-area="false"
|
||||
@maskClick="handleClose"
|
||||
>
|
||||
<view class="refund-popup bg-F5F7FA border-box">
|
||||
<view
|
||||
class="border-box flex flex-items-center justify-between pt-12 pb-12 relative"
|
||||
>
|
||||
<view
|
||||
class="flex-full font-size-16 color-171717 line-height-24 text-center"
|
||||
>明细详情</view
|
||||
>
|
||||
<!-- 关闭按钮 -->
|
||||
<uni-icons
|
||||
class="close absolute"
|
||||
type="close"
|
||||
size="20"
|
||||
color="#CACFD8"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</view>
|
||||
<!-- 内容区域 -->
|
||||
<view class="rounded-12 bg-white ml-12 mr-12 mb-40">
|
||||
<view
|
||||
class="border-box border-bottom flex flex-items-center flex-justify-between pt-12 pb-12 ml-12 mr-12"
|
||||
>
|
||||
<text class="font-size-16 font-500 color-171717">在线支付</text>
|
||||
<text class="font-size-14 color-171717">239</text>
|
||||
</view>
|
||||
<view
|
||||
class="border-box flex flex-items-center flex-justify-between pt-12 pb-12 ml-12 mr-12"
|
||||
>
|
||||
<text class="font-size-16 font-500 color-171717">房费</text>
|
||||
<text class="font-size-14 color-171717">239</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from "vue";
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
// 弹窗显示状态
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 订单数据
|
||||
orderData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
// Events定义
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
// 弹窗引用
|
||||
const popupRef = ref(null);
|
||||
|
||||
// 获取退款模板
|
||||
const commodityPurchaseInstruction = computed(() => {
|
||||
if (props.orderData.commodityPurchaseInstruction) {
|
||||
return props.orderData.commodityPurchaseInstruction;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
// 方法定义
|
||||
const show = () => popupRef.value && popupRef.value.open();
|
||||
|
||||
const hide = () => popupRef.value && popupRef.value.close();
|
||||
|
||||
// 监听modelValue变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
show();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
emit("update:modelValue", false);
|
||||
emit("close");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
9
src/components/DetailPopup/styles/index.scss
Normal file
9
src/components/DetailPopup/styles/index.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.refund-popup {
|
||||
border-radius: 15px 15px 0 0;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 917 B |
@@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<view class="service-order-item">
|
||||
<view class="order-header">
|
||||
<image class="order-icon" src="./images/icon_order.png"></image>
|
||||
<text class="order-title">温泉早鸟票</text>
|
||||
<text :class="['order-status', `status-${orderStatus}`]">{{
|
||||
orderStatusText
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="order-details">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">订单编号:</text>
|
||||
<text class="detail-value">{{ orderId }}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">游客人数:</text>
|
||||
<text class="detail-value">{{ touristCount }}人</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
// Sample data
|
||||
const orderId = ref("7378400483776544");
|
||||
const touristCount = ref(7);
|
||||
const orderStatus = ref("pending"); // Options: 'canceled', 'pending', 'refundProcessing', 'refunded', 'completed'
|
||||
|
||||
// Computed property for order status text
|
||||
const orderStatusText = computed(() => {
|
||||
switch (orderStatus.value) {
|
||||
case "canceled":
|
||||
return "已取消";
|
||||
case "pending":
|
||||
return "待确认";
|
||||
case "refundProcessing":
|
||||
return "退款中";
|
||||
case "refunded":
|
||||
return "已退款";
|
||||
case "completed":
|
||||
return "已完成";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -1,16 +0,0 @@
|
||||
## 订单 Item 组件
|
||||
|
||||
组件名称:订单卡片组件
|
||||
|
||||
## 提示词:
|
||||
|
||||
使用 uniapp + vue3 组合式 api 开发微信小程序,要求如下:
|
||||
1、按照提供的图片高度还原交互设计
|
||||
2、要求布局样式结构简洁明了,class 命名请按照模块名称来命名,例如:.service-order-item
|
||||
3、可以使用 uniapp 内置的组件
|
||||
4、订单状态有已取消、待确认、退款中、已退款、已完成状态,用颜色区分
|
||||
5、订单卡片有点击跳转订单详情交互
|
||||
|
||||
## 备注
|
||||
|
||||
仅供学习、交流使用,请勿用于商业用途。
|
||||
@@ -1,81 +0,0 @@
|
||||
.service-order-item {
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.order-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.order-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: $uni-border-radius-circle;
|
||||
background-color: #ffa500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.order-title {
|
||||
font-size: $uni-font-size-lg;
|
||||
color: $uni-text-color;
|
||||
}
|
||||
|
||||
.order-status {
|
||||
font-size: $uni-font-size-base;
|
||||
padding: 5px 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.status-canceled {
|
||||
color: $uni-text-color-grey;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
color: #ff4d94;
|
||||
border: 1px solid #ff4d94;
|
||||
}
|
||||
|
||||
.status-refundProcessing {
|
||||
color: #ff9900;
|
||||
border: 1px solid #ff9900;
|
||||
}
|
||||
|
||||
.status-refunded {
|
||||
color: #ff4d4f;
|
||||
border: 1px solid #ff4d4f;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
color: #28a745;
|
||||
border: 1px solid #28a745;
|
||||
}
|
||||
|
||||
.order-details {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: $uni-font-size-base;
|
||||
color: #666;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: $uni-font-size-base;
|
||||
color: $uni-text-color;
|
||||
}
|
||||
101
src/components/RefundPopup/index.vue
Normal file
101
src/components/RefundPopup/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<uni-popup
|
||||
ref="popupRef"
|
||||
type="bottom"
|
||||
:safe-area="false"
|
||||
@maskClick="handleClose"
|
||||
>
|
||||
<view class="refund-popup bg-F5F7FA border-box">
|
||||
<view
|
||||
class="border-box flex flex-items-center justify-between pt-12 pb-12 relative"
|
||||
>
|
||||
<view
|
||||
class="flex-full font-size-16 color-171717 line-height-24 text-center"
|
||||
>取消政策</view
|
||||
>
|
||||
<!-- 关闭按钮 -->
|
||||
<uni-icons
|
||||
class="close absolute"
|
||||
type="close"
|
||||
size="20"
|
||||
color="#CACFD8"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</view>
|
||||
<!-- 内容区域 -->
|
||||
<view class="border-box rounded-12 bg-white p-12 ml-12 mr-12 mb-40">
|
||||
<view class="flex flex-items-center mb-8">
|
||||
<uni-icons fontFamily="znicons" size="20" color="#333">
|
||||
{{ zniconsMap["zn-refund"] }}
|
||||
</uni-icons>
|
||||
<text class="font-size-14 font-600 color-171717 ml-8">退订规则</text>
|
||||
</view>
|
||||
<view class="font-size-14 color-525866 line-height-16">
|
||||
<!-- {{ commodityPurchaseInstruction.refundContent }} -->
|
||||
·不予退款:12小时以内取消或未入住,不予退款。
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from "vue";
|
||||
import { zniconsMap } from "@/static/fonts/znicons";
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
// 弹窗显示状态
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 订单数据
|
||||
orderData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
// Events定义
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
// 弹窗引用
|
||||
const popupRef = ref(null);
|
||||
|
||||
// 获取退款模板
|
||||
const commodityPurchaseInstruction = computed(() => {
|
||||
if (props.orderData.commodityPurchaseInstruction) {
|
||||
return props.orderData.commodityPurchaseInstruction;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
// 方法定义
|
||||
const show = () => popupRef.value && popupRef.value.open();
|
||||
|
||||
const hide = () => popupRef.value && popupRef.value.close();
|
||||
|
||||
// 监听modelValue变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
show();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
emit("update:modelValue", false);
|
||||
emit("close");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
9
src/components/RefundPopup/styles/index.scss
Normal file
9
src/components/RefundPopup/styles/index.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
.refund-popup {
|
||||
border-radius: 15px 15px 0 0;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.close {
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
// Emit
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const emit = defineEmits(["update:modelValue", "decrease", "increase"]);
|
||||
|
||||
// Local state
|
||||
const value = ref(props.modelValue);
|
||||
@@ -53,6 +53,7 @@ const decrease = () => {
|
||||
if (value.value > props.min) {
|
||||
value.value--;
|
||||
emit("update:modelValue", value.value);
|
||||
emit("decrease");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,6 +61,7 @@ const increase = () => {
|
||||
if (value.value < props.max) {
|
||||
value.value++;
|
||||
emit("update:modelValue", value.value);
|
||||
emit("increase");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
73
src/pages-booking/components/DateRangeSection/index.vue
Normal file
73
src/pages-booking/components/DateRangeSection/index.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<view class="border-box flex flex-items-center flex-justify-between mb-12">
|
||||
<view class="left flex flex-items-center">
|
||||
<text class="font-size-12 color-99A0AE mr-4">入住</text>
|
||||
<text class="font-size-12 color-171717 mr-16">
|
||||
{{ selectedDate.startDate }}
|
||||
</text>
|
||||
<text
|
||||
class="total border-box rounded-50 flex flex-items-center font-size-11 color-43669A relative"
|
||||
>1晚</text
|
||||
>
|
||||
<text class="font-size-12 color-99A0AE ml-16">离店</text>
|
||||
<text class="font-size-12 color-171717 ml-4">
|
||||
{{ selectedDate.endDate }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="flex flex-items-center" @click="emit('click')">
|
||||
<text class="font-size-12 color-2D91FF line-height-16">房间详情</text>
|
||||
<uni-icons type="right" size="15" color="#99A0AE" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, defineEmits } from "vue";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
selectedDate: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["click"]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.total {
|
||||
border: 1px solid #43669a;
|
||||
padding: 3px 6px;
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
content: "";
|
||||
width: 8px;
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&::before {
|
||||
background: linear-gradient(
|
||||
270deg,
|
||||
rgba(67, 102, 154, 1),
|
||||
rgba(67, 102, 154, 0)
|
||||
);
|
||||
left: -9px;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: linear-gradient(
|
||||
270deg,
|
||||
rgba(67, 102, 154, 0),
|
||||
rgba(67, 102, 154, 1)
|
||||
);
|
||||
right: -9px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
57
src/pages-booking/components/FooterSection/index.vue
Normal file
57
src/pages-booking/components/FooterSection/index.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<view
|
||||
class="booking-footer border-box bg-white flex flex-items-center font-family-misans-vf"
|
||||
>
|
||||
<text class="font-size-14 font-500 color-525866 mr-4"> 在线付 </text>
|
||||
<text
|
||||
class="amt font-size-20 font-bold color-FF3D60 line-height-28 flex flex-items-center mr-8"
|
||||
>88</text
|
||||
>
|
||||
<!-- <view class="flex flex-items-center" @click="emit('detailClick')">
|
||||
<text class="font-size-12 color-A3A3A3 mr-4">明细</text>
|
||||
<uni-icons type="up" size="16" color="#A3A3A3" />
|
||||
</view> -->
|
||||
<view
|
||||
class="btn border-box rounded-10 flex flex-items-center ml-auto pl-8"
|
||||
@click="handleBooking"
|
||||
>
|
||||
<image
|
||||
class="icon"
|
||||
src="https://oss.nianxx.cn/mp/static/version_101/common/btn.png"
|
||||
/>
|
||||
<text class="font-size-16 font-500 color-white">立即预定</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits } from "vue";
|
||||
|
||||
const emit = defineEmits(["detailClick"]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.booking-footer {
|
||||
border-radius: 15px 15px 0 0;
|
||||
padding: 12px 12px 42px;
|
||||
}
|
||||
|
||||
.amt {
|
||||
&::before {
|
||||
content: "¥";
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 120px;
|
||||
height: 48px;
|
||||
background: linear-gradient(90deg, #ff3d60 57%, #ff990c 100%);
|
||||
}
|
||||
|
||||
.icon {
|
||||
height: 48px;
|
||||
width: 34px;
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,11 @@
|
||||
</view>
|
||||
|
||||
<view class="border-box pl-12 pr-12">
|
||||
<view class="border-box border-bottom pt-12 pb-12 flex flex-items-center">
|
||||
<view
|
||||
class="border-box border-bottom pt-12 pb-12 flex flex-items-center"
|
||||
v-for="item in quantity"
|
||||
:key="item"
|
||||
>
|
||||
<view class="font-size-14 font-500 color-525866 mr-12"> 住客姓名 </view>
|
||||
<view class="right">
|
||||
<input
|
||||
|
||||
@@ -14,14 +14,33 @@
|
||||
<view class="booking-content flex-full border-box p-12">
|
||||
<!-- 预约内容 -->
|
||||
<view class="border-box bg-white p-12 rounded-12 mb-12">
|
||||
<view class="font-size-16 font-500 color-000 line-height-24"
|
||||
>【温泉季】戏水单人票 +单人简餐</view
|
||||
>
|
||||
<!-- 酒店类型入住离店日期部分 -->
|
||||
<DateRangeSection
|
||||
v-if="orderData.commodityTypeCode === '0'"
|
||||
:selectedDate="selectedDate"
|
||||
@click="navigateToDetail(orderData)"
|
||||
/>
|
||||
|
||||
<view
|
||||
class="border-box border-bottom font-size-12 color-99A0AE line-height-16 pb-12"
|
||||
>温泉早鸟票2张 黄南武辣子鸡2人套餐1份</view
|
||||
>
|
||||
<view class="font-size-16 font-500 color-000 line-height-24 ellipsis-1">
|
||||
{{ orderData.commodityName }}
|
||||
</view>
|
||||
|
||||
<view class="border-box border-bottom">
|
||||
<view class="font-size-12 color-99A0AE line-height-16 pb-12">
|
||||
{{ orderData.commodityDescription }}
|
||||
</view>
|
||||
|
||||
<!-- 权益部分 -->
|
||||
<view class="flex flex-items-center mb-8">
|
||||
<text
|
||||
class="bg-F7F7F7 border-box rounded-4 font-size-11 color-525866 mr-4 pt-4 pb-4 pl-6 pr-6"
|
||||
v-for="(item, index) in orderData.commodityFacilityList"
|
||||
:key="index"
|
||||
>
|
||||
{{ item }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="border-box flex flex-items-center flex-justify-between pt-12"
|
||||
@@ -30,7 +49,9 @@
|
||||
>使用时间:周一至周日9:00-22:00</text
|
||||
>
|
||||
<view class="flex flex-items-center">
|
||||
<text class="font-size-12 color-2D91FF line-height-16"
|
||||
<text
|
||||
class="font-size-12 color-2D91FF line-height-16"
|
||||
@click="refundVisible = true"
|
||||
>取消政策</text
|
||||
>
|
||||
<uni-icons type="right" size="15" color="#99A0AE" />
|
||||
@@ -39,21 +60,73 @@
|
||||
</view>
|
||||
|
||||
<!-- 非酒店类型 -->
|
||||
<ContactSection v-if="false" />
|
||||
<ContactSection v-if="orderData.commodityTypeCode !== '0'" />
|
||||
|
||||
<!-- 酒店类型 -->
|
||||
<UserSection />
|
||||
<UserSection v-if="orderData.commodityTypeCode === '0'" />
|
||||
</view>
|
||||
|
||||
<!-- 底部 -->
|
||||
<FooterSection @detailClick="detailVisible = true" />
|
||||
|
||||
<!-- 取消政策弹窗 -->
|
||||
<RefundPopup v-model="refundVisible" :orderData="orderData" />
|
||||
|
||||
<!-- 明细弹窗 -->
|
||||
<DetailPopup v-model="detailVisible" :orderData="orderData" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import TopNavBar from "@/components/TopNavBar/index.vue";
|
||||
import DateRangeSection from "./components/DateRangeSection/index.vue";
|
||||
import ContactSection from "./components/ConactSection/index.vue";
|
||||
import UserSection from "./components/UserSection/index.vue";
|
||||
import RefundPopup from "@/components/RefundPopup/index.vue";
|
||||
import DetailPopup from "@/components/DetailPopup/index.vue";
|
||||
import FooterSection from "./components/FooterSection/index.vue";
|
||||
import { goodsDetail } from "@/request/api/GoodsApi";
|
||||
|
||||
const title = ref("预约");
|
||||
const refundVisible = ref(false);
|
||||
const detailVisible = ref(false);
|
||||
const orderData = ref({});
|
||||
const selectedDate = ref({
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
totalDays: 1,
|
||||
});
|
||||
|
||||
onLoad((options) => {
|
||||
const { commodityId, startDate, endDate, totalDays } = options;
|
||||
selectedDate.value.startDate = startDate;
|
||||
selectedDate.value.endDate = endDate;
|
||||
selectedDate.value.totalDays = totalDays;
|
||||
|
||||
getGoodsDetail(commodityId);
|
||||
});
|
||||
|
||||
const getGoodsDetail = async (commodityId) => {
|
||||
const res = await goodsDetail({ commodityId });
|
||||
|
||||
console.log("获取商品详情", res);
|
||||
|
||||
orderData.value = res.data;
|
||||
// 取commodityFacilityList前3个
|
||||
orderData.value.commodityFacilityList = res.data.commodityFacilityList.slice(
|
||||
0,
|
||||
3
|
||||
);
|
||||
};
|
||||
|
||||
// 跳转商品详情
|
||||
const navigateToDetail = ({ commodityId }) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${commodityId}`,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
# 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. 确保组件的可复用性和可扩展性
|
||||
@@ -1,389 +0,0 @@
|
||||
<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>
|
||||
@@ -1,274 +0,0 @@
|
||||
<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>
|
||||
@@ -1,154 +0,0 @@
|
||||
<template>
|
||||
<uni-popup ref="popupRef" type="center" @maskClick="handleClose">
|
||||
<view class="refund-popup">
|
||||
<!-- 头部卡通形象 -->
|
||||
<image
|
||||
:src="commodityPurchaseInstruction.refundLogo"
|
||||
class="refund-popup__avatar"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view class="refund-popup__content">
|
||||
<!-- 主标题 -->
|
||||
<view class="refund-popup__title">
|
||||
{{ commodityPurchaseInstruction.refundTitle }}
|
||||
</view>
|
||||
|
||||
<!-- 金额显示 -->
|
||||
<view class="refund-popup__amount" v-if="isRefundable">
|
||||
<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="isRefundable"
|
||||
>可退金额</view
|
||||
>
|
||||
|
||||
<!-- 退款政策详情 -->
|
||||
<view class="refund-popup__policy" v-if="showRefundPolicy">
|
||||
<view class="policy-title">退订政策:</view>
|
||||
<view class="policy-content">
|
||||
{{ commodityPurchaseInstruction.refundContent }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 按钮区域 -->
|
||||
<view class="refund-popup__actions">
|
||||
<view
|
||||
v-if="!showRefundPolicy"
|
||||
class="action-btn secondary-btn"
|
||||
@click="handlePolicyClick"
|
||||
>
|
||||
退订政策
|
||||
</view>
|
||||
<view
|
||||
class="action-btn primary-btn"
|
||||
@click="handleConfirmClick(btnText)"
|
||||
>
|
||||
{{ btnText }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 关闭按钮 -->
|
||||
<uni-icons
|
||||
class="refund-popup__close"
|
||||
type="close"
|
||||
size="40"
|
||||
color="#fff"
|
||||
@click="handleClose"
|
||||
/>
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from "vue";
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
// 弹窗显示状态
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 订单数据
|
||||
orderData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
// Events定义
|
||||
const emit = defineEmits(["update:modelValue", "confirm", "close"]);
|
||||
|
||||
// 弹窗引用
|
||||
const popupRef = ref(null);
|
||||
// 退款政策是否显示
|
||||
const showRefundPolicy = ref(false);
|
||||
|
||||
// 退款金额
|
||||
const refundAmount = computed(() => {
|
||||
if (props.orderData.payAmt) {
|
||||
return parseFloat(Number(props.orderData.payAmt) || 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// 是否可退款
|
||||
const isRefundable = computed(() => props.orderData.refundable);
|
||||
|
||||
// 按钮文件
|
||||
const btnText = computed(() => (isRefundable.value ? "点击退款" : "我知道了"));
|
||||
|
||||
// 获取退款模板
|
||||
const commodityPurchaseInstruction = computed(() => {
|
||||
if (props.orderData.commodityPurchaseInstruction) {
|
||||
return props.orderData.commodityPurchaseInstruction;
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
// 方法定义
|
||||
const show = () => popupRef.value && popupRef.value.open();
|
||||
|
||||
const hide = () => popupRef.value && popupRef.value.close();
|
||||
|
||||
// 监听modelValue变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
show();
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleClose = () => {
|
||||
showRefundPolicy.value = false;
|
||||
emit("update:modelValue", false);
|
||||
emit("close");
|
||||
};
|
||||
|
||||
const handlePolicyClick = () => {
|
||||
showRefundPolicy.value = true;
|
||||
};
|
||||
|
||||
const handleConfirmClick = (text) => {
|
||||
if (text === "点击退款") {
|
||||
emit("confirm", props.orderData);
|
||||
}
|
||||
|
||||
handleClose();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -1,164 +0,0 @@
|
||||
// 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: -75px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
// 内容区域
|
||||
&__content {
|
||||
padding: 12px 20px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: $uni-font-size-lg;
|
||||
font-weight: 500;
|
||||
color: $uni-text-color;
|
||||
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: $uni-font-size-sm;
|
||||
color: #ff6a00;
|
||||
}
|
||||
|
||||
.amount-value {
|
||||
font-size: 24px;
|
||||
color: #ff6a00;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.amount-unit {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: #ff6a00;
|
||||
}
|
||||
}
|
||||
|
||||
&__amount-label {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&__policy {
|
||||
text-align: left;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.policy-title {
|
||||
font-size: $uni-font-size-base;
|
||||
color: #007aff;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.policy-content {
|
||||
font-size: $uni-font-size-sm;
|
||||
color: $uni-text-color;
|
||||
line-height: 22px;
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮区域
|
||||
&__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: $uni-font-size-lg;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭按钮
|
||||
&__close {
|
||||
position: absolute;
|
||||
bottom: -60px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
// 动画效果
|
||||
@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;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ import GoodsInfo from "./components/GoodsInfo/index.vue";
|
||||
import UserInfo from "./components/UserInfo/index.vue";
|
||||
import NoticeInfo from "./components/NoticeInfo/index.vue";
|
||||
import OrderInfo from "./components/OrderInfo/index.vue";
|
||||
import RefundPopup from "./components/RefundPopup/index.vue";
|
||||
import RefundPopup from "@/components/RefundPopup/index.vue";
|
||||
|
||||
const refundVisible = ref(false);
|
||||
const orderData = ref({});
|
||||
|
||||
@@ -61,6 +61,10 @@ const props = defineProps({
|
||||
stockUnitLabel: "", // 库存单位
|
||||
}),
|
||||
},
|
||||
selectedDate: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const handleClick = ({ commodityId }) => {
|
||||
@@ -68,7 +72,10 @@ const handleClick = ({ commodityId }) => {
|
||||
};
|
||||
|
||||
const handleBooking = ({ commodityId }) => {
|
||||
uni.navigateTo({ url: `/pages/booking/index?commodityId=${commodityId}` });
|
||||
const { startDate, endDate, totalDays } = props.selectedDate;
|
||||
uni.navigateTo({
|
||||
url: `/pages-booking/index?commodityId=${commodityId}&startDate=${startDate}&endDate=${endDate}&totalDays=${totalDays}`,
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -50,7 +50,12 @@
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<Card v-for="(item, index) in dataList" :key="index" :item="item" />
|
||||
<Card
|
||||
v-for="(item, index) in dataList"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:selectedDate="selectedDate"
|
||||
/>
|
||||
</z-paging>
|
||||
|
||||
<!-- 日历组件 -->
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.bg-F7F7F7 {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
.bg-17294E {
|
||||
background-color: #17294e;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
// 内边距
|
||||
.pt-4 {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.pb-4 {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.p-6 {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user