Compare commits
4 Commits
e6615da586
...
8b4926eb2b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b4926eb2b | ||
|
|
4b066626cf | ||
|
|
d71e63b666 | ||
|
|
29522fb4bc |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"appid": "wx0be424e1d22065a9",
|
||||
"appid": "wx5e79df5996572539",
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "3.8.10",
|
||||
"packOptions": {
|
||||
|
||||
28
src/components/CustomEmpty/index.vue
Normal file
28
src/components/CustomEmpty/index.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<view class="empty-container flex flex-col items-center justify-center">
|
||||
<image class="empty-image" :src="emptyIcon" mode="aspectFit" />
|
||||
<text class="font-size-12 text-center color-99A0AE mt-8">
|
||||
{{ statusText }}
|
||||
</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
statusText: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
emptyIcon: {
|
||||
type: String,
|
||||
default:
|
||||
"https://oss.nianxx.cn/mp/static/version_101/order/order_empty.png",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
4
src/components/CustomEmpty/styles/index.scss
Normal file
4
src/components/CustomEmpty/styles/index.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.empty-image {
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
}
|
||||
@@ -1,50 +1,55 @@
|
||||
<template>
|
||||
<view class="create-service-order">
|
||||
<view class="create-service-wrapper">
|
||||
<view class="order-header">
|
||||
<text>反馈意见</text>
|
||||
</view>
|
||||
<view class="order-content">
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">联系电话:</text>
|
||||
<view
|
||||
class="w-full bg-white border-box border-ff overflow-hidden rounded-20"
|
||||
>
|
||||
<image
|
||||
class="order-header w-full"
|
||||
src="https://oss.nianxx.cn/mp/static/version_101/home/feedback_1.png"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="order-content border-box p-12">
|
||||
<view
|
||||
class="bg-F5F7FA border-box flex flex-items-center p-12 rounded-10 font-size-14 color-171717 mb-12"
|
||||
>
|
||||
<text class="font-500 line-height-22 mr-20">联系电话</text>
|
||||
<input
|
||||
v-if="!isCallSuccess"
|
||||
class="detail-input"
|
||||
placeholder="请填写联系电话"
|
||||
v-model="contactPhone"
|
||||
/>
|
||||
<text v-else class="detail-value">{{ contactPhone }}</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<text class="detail-label">意见内容:</text>
|
||||
<view
|
||||
class="bg-F5F7FA border-box p-12 rounded-10 font-size-14 font-500 color-171717 mb-12"
|
||||
>
|
||||
<view class="font-500 line-height-22 mb-12">意见内容</view>
|
||||
<textarea
|
||||
v-if="!isCallSuccess"
|
||||
class="detail-textarea"
|
||||
placeholder="请输入反馈意见"
|
||||
maxlength="100"
|
||||
v-model="contactText"
|
||||
/>
|
||||
<text v-else class="detail-value">{{ contactText }}</text>
|
||||
</view>
|
||||
|
||||
<button
|
||||
<view
|
||||
v-if="!isCallSuccess"
|
||||
class="order-button submit-button"
|
||||
class="btn rounded-50 color-white bg-button flex flex-items-center flex-justify-center"
|
||||
@click="handleCall"
|
||||
>
|
||||
立即提交
|
||||
</button>
|
||||
|
||||
<button v-else class="order-button look-button" @click="viewWorkOrder">
|
||||
查看意见
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer-help">
|
||||
<image src="./images/icon_volume.png" class="help-icon"></image>
|
||||
<text class="help-text">
|
||||
{{ appName }}收到您的意见将第一时间为您处理!
|
||||
</text>
|
||||
<view
|
||||
class="footer-help flex flex-items-center flex-justify-center mb-12"
|
||||
>
|
||||
<image class="help-icon mr-4" src="./images/icon_volume.png" />
|
||||
<text class="font-size-12 font-500 color-FA7319">
|
||||
{{ appName }}收到您的意见将第一时间为您处理!
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -1,95 +1,16 @@
|
||||
.create-service-wrapper {
|
||||
padding: 6px 0 12px;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
font-size: $uni-font-size-base;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
color: $uni-text-color;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.order-content {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
margin-bottom: 12px;
|
||||
font-size: $uni-font-size-base;
|
||||
color: $uni-text-color;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
width: 70px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: $uni-font-size-base;
|
||||
color: $uni-text-color;
|
||||
}
|
||||
|
||||
.detail-textarea {
|
||||
width: 100%;
|
||||
font-size: $uni-font-size-base;
|
||||
color: $uni-text-color;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.detail-input {
|
||||
border: none;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
font-size: $uni-font-size-base;
|
||||
color: $uni-text-color;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.order-button {
|
||||
width: 300px;
|
||||
height: 42px;
|
||||
color: #fff;
|
||||
font-size: $uni-font-size-base;
|
||||
font-weight: 600;
|
||||
border-radius: 21px;
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
background: linear-gradient(179deg, #00a6ff 0%, #0256ff 100%);
|
||||
}
|
||||
|
||||
.look-button {
|
||||
background: linear-gradient(90deg, #0256ff, #00a6ff);
|
||||
}
|
||||
|
||||
.footer-help {
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: $uni-font-size-base;
|
||||
color: #ed6a0c;
|
||||
width: 335px;
|
||||
}
|
||||
|
||||
.help-icon {
|
||||
width: 16px;
|
||||
height: 14px;
|
||||
margin-right: 5px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
margin-right: 5px;
|
||||
.btn {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ const processConfigs = (configs) => {
|
||||
export const CLIENT_CONFIGS = processConfigs(rawConfigs);
|
||||
|
||||
// 获取当前用户端配置
|
||||
export const getCurrentConfig = () => CLIENT_CONFIGS.tianmu;
|
||||
export const getCurrentConfig = () => CLIENT_CONFIGS.zhinian;
|
||||
export const clientId = getCurrentConfig().clientId;
|
||||
export const appId = getCurrentConfig().appId;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
朵花:wx23f86d809ae80259
|
||||
*/
|
||||
"mp-weixin": {
|
||||
"appid": "wx0be424e1d22065a9",
|
||||
"appid": "wx5e79df5996572539",
|
||||
"setting": {
|
||||
"urlCheck": false
|
||||
},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,26 +0,0 @@
|
||||
|
||||
<template>
|
||||
<view class="empty-container">
|
||||
<image
|
||||
class="empty-image"
|
||||
mode="aspectFit"
|
||||
src="./images/empty.png"
|
||||
></image>
|
||||
<text class="empty-text">{{ statusText }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
statusText: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -1,19 +0,0 @@
|
||||
.empty-container {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.empty-image {
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
margin-top: 10px;
|
||||
font-size: $uni-font-size-base;
|
||||
color: #666666;
|
||||
}
|
||||
@@ -62,6 +62,7 @@ const ICON_MAP = {
|
||||
0: "https://oss.nianxx.cn/mp/static/version_101/order/room.png", // 酒店订单
|
||||
1: "https://oss.nianxx.cn/mp/static/version_101/order/ticket.png", // 门票订单
|
||||
2: "https://oss.nianxx.cn/mp/static/version_101/order/food.png", // 餐饮
|
||||
3: "https://oss.nianxx.cn/mp/static/version_101/order/package.png", // 套餐
|
||||
};
|
||||
|
||||
// 订单类型映射
|
||||
@@ -69,6 +70,7 @@ const ORDER_NAME_MAP = {
|
||||
0: "房间",
|
||||
1: "门票",
|
||||
2: "餐饮",
|
||||
3: "套餐",
|
||||
};
|
||||
|
||||
// 获取状态图标
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import TopNavBar from "@/components/TopNavBar/index.vue";
|
||||
import CustomEmpty from "@/components/CustomEmpty/index.vue";
|
||||
import OrderCard from "./components/OrderCard/index.vue";
|
||||
import CustomEmpty from "./components/CustomEmpty/index.vue";
|
||||
import { userOrderList } from "@/request/api/OrderApi";
|
||||
|
||||
const dataList = ref([]);
|
||||
|
||||
66
src/pages-quick/components/Card/index.vue
Normal file
66
src/pages-quick/components/Card/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<view
|
||||
class="card bg-white border-box p-8 rounded-12 flex flex-items-start m-12"
|
||||
@click="handleClick(item)"
|
||||
>
|
||||
<image
|
||||
class="left rounded-10"
|
||||
:src="item.commodityIcon"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="right border-box flex-full pl-12">
|
||||
<view class="font-size-16 line-height-24 color-171717 mb-4">
|
||||
{{ item.commodityName }}
|
||||
</view>
|
||||
<view class="font-size-12 line-height-16 color-99A0AE mb-4">
|
||||
1 张 1.8 米大床
|
||||
</view>
|
||||
<view class="font-size-12 line-height-18 color-43669A">
|
||||
{{ item.commodityTradeRuleList.join(" / ") }}
|
||||
</view>
|
||||
|
||||
<view class="flex flex-items-center flex-justify-end">
|
||||
<text
|
||||
class="amt font-size-18 font-500 font-family-misans-vf line-height-24 color-FF3D60 mr-4"
|
||||
>
|
||||
{{ item.commodityPrice }}
|
||||
</text>
|
||||
<text class="font-size-12 line-height-16 color-99A0AE">
|
||||
/{{ item.stockUnitLabel }}
|
||||
</text>
|
||||
<text class="btn border-box rounded-10 color-white ml-16">订</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
commodityIcon: "",
|
||||
commodityId: "",
|
||||
commodityName: "",
|
||||
commodityPrice: "",
|
||||
commodityServices: [],
|
||||
commodityTags: [], // 商品标签
|
||||
commodityTradeRuleList: [], // 交易规则列表
|
||||
specificationId: "", // 规格ID
|
||||
stockUnitLabel: "", // 库存单位
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const handleClick = ({ commodityId }) => {
|
||||
uni.navigateTo({ url: `/pages/goods/index?commodityId=${commodityId}` });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
21
src/pages-quick/components/Card/styles/index.scss
Normal file
21
src/pages-quick/components/Card/styles/index.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
.left {
|
||||
height: 107px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.right {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.amt {
|
||||
&::before {
|
||||
content: "¥";
|
||||
font-size: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
background: linear-gradient(90deg, #ff3d60 57%, #ff990c 100%);
|
||||
padding: 4px 8px;
|
||||
}
|
||||
276
src/pages-quick/components/Tabs/index.vue
Normal file
276
src/pages-quick/components/Tabs/index.vue
Normal file
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<view class="tab-container relative">
|
||||
<view class="tab-wrapper flex flex-items-center flex-justify-center">
|
||||
<view
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
:class="[
|
||||
'tab-item flex flex-full flex-items-center flex-justify-center relative',
|
||||
activeIndex === index && 'tab-item-active',
|
||||
]"
|
||||
@click="handleTabClick(index)"
|
||||
>
|
||||
<text
|
||||
:class="[
|
||||
'font-size-16 font-500 color-525866 absolute',
|
||||
activeIndex === index && 'tab-text-active',
|
||||
]"
|
||||
>
|
||||
{{ item.label }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 下划线指示器 -->
|
||||
<view
|
||||
:class="[
|
||||
'tab-indicator absolute',
|
||||
indicatorAnimating && 'animating',
|
||||
indicatorInitialized && 'initialized',
|
||||
]"
|
||||
:style="{
|
||||
left: indicatorLeft + 'px',
|
||||
width: indicatorWidth + 'px',
|
||||
}"
|
||||
></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
onMounted,
|
||||
nextTick,
|
||||
watch,
|
||||
getCurrentInstance,
|
||||
} from "vue";
|
||||
|
||||
// 获取组件实例
|
||||
const instance = getCurrentInstance();
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
tabs: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ label: "客房", value: "0" },
|
||||
{ label: "门票", value: "1" },
|
||||
{ label: "餐食", value: "2" },
|
||||
{ label: "套餐", value: "3" },
|
||||
],
|
||||
},
|
||||
defaultActive: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
indicatorColor: {
|
||||
type: String,
|
||||
default: "#007AFF",
|
||||
},
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(["change", "update:modelValue"]);
|
||||
|
||||
// 响应式数据
|
||||
const activeIndex = ref(props.defaultActive);
|
||||
const tabList = ref(props.tabs);
|
||||
const indicatorLeft = ref(0);
|
||||
const indicatorWidth = ref(0);
|
||||
const tabItemRects = reactive([]);
|
||||
const isUpdating = ref(false);
|
||||
const indicatorAnimating = ref(false);
|
||||
const indicatorInitialized = ref(false);
|
||||
|
||||
// 处理Tab点击
|
||||
const handleTabClick = (index) => {
|
||||
if (activeIndex.value === index) return;
|
||||
|
||||
activeIndex.value = index;
|
||||
indicatorAnimating.value = true;
|
||||
updateIndicator();
|
||||
|
||||
// 动画结束后移除动画类
|
||||
setTimeout(() => {
|
||||
indicatorAnimating.value = false;
|
||||
}, 300);
|
||||
|
||||
emit("change", {
|
||||
index,
|
||||
item: tabList.value[index],
|
||||
});
|
||||
emit("update:modelValue", index);
|
||||
};
|
||||
|
||||
// 更新指示器位置
|
||||
const updateIndicator = async () => {
|
||||
if (isUpdating.value) return;
|
||||
isUpdating.value = true;
|
||||
|
||||
await nextTick();
|
||||
|
||||
// 检查实例是否存在
|
||||
if (!instance) {
|
||||
console.warn("Component instance not available");
|
||||
isUpdating.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查 uni.createSelectorQuery 是否可用
|
||||
if (!uni || !uni.createSelectorQuery) {
|
||||
console.warn("uni.createSelectorQuery not available");
|
||||
isUpdating.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
|
||||
// 同时获取tab项和容器的位置信息
|
||||
query.selectAll(".tab-item").boundingClientRect();
|
||||
query.select(".tab-wrapper").boundingClientRect();
|
||||
|
||||
query.exec((res) => {
|
||||
try {
|
||||
const [tabRects, wrapperRect] = res || [];
|
||||
|
||||
if (tabRects && tabRects.length > 0 && wrapperRect) {
|
||||
tabItemRects.splice(0, tabItemRects.length, ...tabRects);
|
||||
|
||||
const activeRect = tabRects[activeIndex.value];
|
||||
if (activeRect) {
|
||||
// 计算相对于容器的位置,居中显示
|
||||
const tabCenter =
|
||||
activeRect.left - wrapperRect.left + activeRect.width / 2;
|
||||
indicatorLeft.value = tabCenter - 10; // 15px宽度的一半
|
||||
// 固定宽度15px,不动态计算宽度
|
||||
indicatorWidth.value = 20;
|
||||
|
||||
// 标记为已初始化
|
||||
if (!indicatorInitialized.value) {
|
||||
indicatorInitialized.value = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn("Failed to get tab rects or wrapper rect");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in updateIndicator exec:", error);
|
||||
} finally {
|
||||
isUpdating.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听activeIndex变化
|
||||
watch(
|
||||
() => activeIndex.value,
|
||||
() => {
|
||||
// 如果是初始化阶段,使用initIndicator
|
||||
if (indicatorLeft.value === 0 && indicatorWidth.value === 0) {
|
||||
initIndicator();
|
||||
} else {
|
||||
updateIndicator();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听tabs变化
|
||||
watch(
|
||||
() => props.tabs,
|
||||
(newTabs) => {
|
||||
tabList.value = newTabs;
|
||||
// 重置初始化状态
|
||||
indicatorInitialized.value = false;
|
||||
// 重新初始化指示器
|
||||
initIndicator();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听defaultActive变化
|
||||
watch(
|
||||
() => props.defaultActive,
|
||||
(newActive) => {
|
||||
if (newActive !== activeIndex.value) {
|
||||
activeIndex.value = newActive;
|
||||
// 重置初始化状态
|
||||
indicatorInitialized.value = false;
|
||||
initIndicator();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 初始化指示器
|
||||
const initIndicator = async (retryCount = 0) => {
|
||||
// 等待DOM完全渲染
|
||||
await nextTick();
|
||||
|
||||
// 延迟一帧确保布局完成
|
||||
setTimeout(() => {
|
||||
// 检查实例是否存在
|
||||
if (!instance) {
|
||||
console.warn("Component instance not available in initIndicator");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查 uni.createSelectorQuery 是否可用
|
||||
if (!uni || !uni.createSelectorQuery) {
|
||||
console.warn("uni.createSelectorQuery not available in initIndicator");
|
||||
return;
|
||||
}
|
||||
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
query.selectAll(".tab-item").boundingClientRect();
|
||||
query.select(".tab-wrapper").boundingClientRect();
|
||||
|
||||
query.exec((res) => {
|
||||
try {
|
||||
const [tabRects, wrapperRect] = res || [];
|
||||
|
||||
// 如果DOM元素还未准备好,重试
|
||||
if (
|
||||
(!tabRects || tabRects.length === 0 || !wrapperRect) &&
|
||||
retryCount < 3
|
||||
) {
|
||||
setTimeout(() => {
|
||||
initIndicator(retryCount + 1);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行正常的更新逻辑
|
||||
updateIndicator();
|
||||
} catch (error) {
|
||||
console.error("Error in initIndicator exec:", error);
|
||||
// 如果出错且还有重试次数,尝试重试
|
||||
if (retryCount < 3) {
|
||||
setTimeout(() => {
|
||||
initIndicator(retryCount + 1);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
};
|
||||
|
||||
// 组件挂载后初始化指示器
|
||||
onMounted(() => {
|
||||
initIndicator();
|
||||
});
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
setActiveIndex: (index) => {
|
||||
if (index >= 0 && index < tabList.value.length) {
|
||||
handleTabClick(index);
|
||||
}
|
||||
},
|
||||
getActiveIndex: () => activeIndex.value,
|
||||
getActiveItem: () => tabList.value[activeIndex.value],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
67
src/pages-quick/components/Tabs/styles/index.scss
Normal file
67
src/pages-quick/components/Tabs/styles/index.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
.tab-wrapper {
|
||||
background-color: #d9eeff;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tab-item-active {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 20px 20px 0 0;
|
||||
transform: perspective(40px) rotateX(6deg) translate(0, -1px);
|
||||
transform-origin: bottom bottom;
|
||||
box-shadow: 0 -0.5px 0 #2d91ff;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-text-active {
|
||||
color: #2d91ff;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
bottom: 0;
|
||||
height: 3px;
|
||||
background-color: #2d91ff;
|
||||
border-radius: 4px 4px 0 0;
|
||||
transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 3;
|
||||
transform: translateZ(0); /* 启用硬件加速 */
|
||||
will-change: left, width; /* 优化动画性能 */
|
||||
|
||||
/* 初始状态:未初始化时隐藏 */
|
||||
opacity: 0;
|
||||
width: 20px; /* 默认宽度15px */
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* 已初始化状态 */
|
||||
.tab-indicator.initialized {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 动画增强 */
|
||||
@keyframes tabSwitch {
|
||||
0% {
|
||||
transform: translateZ(0) scaleX(0.8);
|
||||
opacity: 0.6;
|
||||
}
|
||||
100% {
|
||||
transform: translateZ(0) scaleX(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-indicator.animating {
|
||||
animation: tabSwitch 0.3s ease-out;
|
||||
}
|
||||
118
src/pages-quick/list.vue
Normal file
118
src/pages-quick/list.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<z-paging
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
use-virtual-list
|
||||
:force-close-inner-list="true"
|
||||
cell-height-mode="dynamic"
|
||||
safe-area-inset-bottom
|
||||
@query="queryList"
|
||||
>
|
||||
<template #top>
|
||||
<TopNavBar title="快速预定" />
|
||||
<header>
|
||||
<Tabs @change="handleTabChange" />
|
||||
|
||||
<!-- 选择入住、离店日期 -->
|
||||
<view class="bg-white border-box flex flex-items-center p-12">
|
||||
<view class="in flex flex-items-center">
|
||||
<text class="font-size-11 font-500 color-99A0AE mr-4">入住</text>
|
||||
<text class="font-size-14 font-500 color-171717">
|
||||
{{ DateUtils.formatDate() }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 几晚 -->
|
||||
<text
|
||||
class="nights bg-E5E8EE border-box font-size-11 font-500 color-525866 rounded-50 ml-8 mr-8"
|
||||
>
|
||||
{{ 1 }}晚
|
||||
</text>
|
||||
|
||||
<view class="out flex flex-items-center">
|
||||
<text class="font-size-11 font-500 color-99A0AE mr-4">离店</text>
|
||||
<text class="font-size-14 font-500 color-171717">
|
||||
{{ DateUtils.formatDate() }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 日期图标 -->
|
||||
<uni-icons
|
||||
class="ml-auto"
|
||||
type="calendar"
|
||||
size="24"
|
||||
color="#2D91FF"
|
||||
@click="calendarVisible = true"
|
||||
/>
|
||||
</view>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<Card v-for="(item, index) in dataList" :key="index" :item="item" />
|
||||
</z-paging>
|
||||
|
||||
<!-- 日历组件 -->
|
||||
<Calender
|
||||
:visible="calendarVisible"
|
||||
mode="single"
|
||||
:default-value="selectedDate"
|
||||
@close="handleCalendarClose"
|
||||
@select="handleDateSelect"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import TopNavBar from "@/components/TopNavBar/index.vue";
|
||||
import Calender from "@/components/Calender/index.vue";
|
||||
import Tabs from "./components/Tabs/index.vue";
|
||||
import Card from "./components/Card/index.vue";
|
||||
import { quickBookingComponent } from "@/request/api/MainPageDataApi";
|
||||
import { DateUtils } from "@/utils";
|
||||
|
||||
const calendarVisible = ref(false);
|
||||
const selectedDate = ref("");
|
||||
const dataList = ref([]);
|
||||
const paging = ref(null);
|
||||
|
||||
const queryList = async (pageNum = 1, pageSize = 10) => {
|
||||
try {
|
||||
const res = await quickBookingComponent(DateUtils.formatDate());
|
||||
console.log("API响应:", res.data.commodityGroupDTOList);
|
||||
|
||||
if (res && res.data && res.data.commodityGroupDTOList) {
|
||||
const records = res.data.commodityGroupDTOList[0].commodityList;
|
||||
|
||||
// 完成数据加载,第二个参数表示是否还有更多数据
|
||||
paging.value.complete(records);
|
||||
} else {
|
||||
// 没有数据
|
||||
paging.value.complete([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("查询列表失败:", error);
|
||||
// 加载失败
|
||||
paging.value.complete(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTabChange = ({ index, item }) => {};
|
||||
|
||||
// 处理日历关闭
|
||||
const handleCalendarClose = () => {
|
||||
calendarVisible.value = false;
|
||||
};
|
||||
|
||||
// 处理日期选择
|
||||
const handleDateSelect = (data) => {
|
||||
selectedDate.value = data.date;
|
||||
calendarVisible.value = false;
|
||||
console.log("选择的日期:", data.date);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nights {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
</style>
|
||||
26
src/pages-service/order/components/OrderCard/InfoRow.vue
Normal file
26
src/pages-service/order/components/OrderCard/InfoRow.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<view class="info-row flex items-center mt-8">
|
||||
<text class="label font-size-12 color-99A0AE">{{ label }}:</text>
|
||||
<text class="value flex-full font-size-12 color-99A0AE">{{ value }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<view class="card-content border-box pt-12">
|
||||
<view class="flex items-center justify-between">
|
||||
<view
|
||||
class="left flex-full font-size-14 line-height-20 color-171717 mr-12"
|
||||
>
|
||||
{{ orderData.commodityName }}
|
||||
</view>
|
||||
<view class="right font-size-18 font-bold line-height-20 color-525866">
|
||||
{{ orderData.orderAmt }}
|
||||
</view>
|
||||
</view>
|
||||
<!-- 动态渲染信息行 -->
|
||||
<InfoRow
|
||||
v-for="item in displayItems"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed } from "vue";
|
||||
import InfoRow from "./InfoRow.vue";
|
||||
|
||||
// 订单类型常量
|
||||
const ORDER_TYPES = {
|
||||
HOTEL: "0", // 酒店订单
|
||||
TICKET: "1", // 门票订单
|
||||
OTHER: "2", // 其他订单
|
||||
};
|
||||
|
||||
// 标签常量
|
||||
const LABELS = {
|
||||
CHECK_IN_TIME: "入住时间",
|
||||
VISITOR_NAME: "游客姓名",
|
||||
CONTACT_PHONE: "联系电话",
|
||||
QUANTITY: "份数",
|
||||
CREATE_TIME: "创建时间",
|
||||
CONTACT_GUEST: "联系房客",
|
||||
};
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
orderType: undefined,
|
||||
orderId: "",
|
||||
checkInTime: "",
|
||||
visitorName: "",
|
||||
contactPhone: "",
|
||||
userName: "",
|
||||
userPhone: "",
|
||||
commodityAmount: 0,
|
||||
createTime: "",
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
// 格式化份数
|
||||
const formatQuantity = (amount) => {
|
||||
return `${Math.floor(amount || 0)}份`;
|
||||
};
|
||||
|
||||
// 计算显示项目
|
||||
const displayItems = computed(() => {
|
||||
const { orderData } = props;
|
||||
const { orderType } = orderData;
|
||||
|
||||
// 工单情况:orderType 为 undefined
|
||||
if (orderType === undefined) {
|
||||
return [
|
||||
{ label: LABELS.CREATE_TIME, value: orderData.createTime },
|
||||
{ label: LABELS.CONTACT_GUEST, value: orderData.userName },
|
||||
{ label: LABELS.CONTACT_PHONE, value: orderData.userPhone },
|
||||
];
|
||||
}
|
||||
|
||||
// 订单情况:根据 orderType 返回不同的显示项
|
||||
switch (orderType) {
|
||||
case ORDER_TYPES.HOTEL:
|
||||
return [
|
||||
{
|
||||
label: LABELS.CHECK_IN_TIME,
|
||||
value:
|
||||
orderData.checkInData?.replace(
|
||||
/(\d{4})-(\d{1,2})-(\d{1,2})/,
|
||||
"$2月$3日"
|
||||
) +
|
||||
"-" +
|
||||
orderData.checkOutData?.replace(
|
||||
/(\d{4})-(\d{1,2})-(\d{1,2})/,
|
||||
"$2月$3日"
|
||||
),
|
||||
},
|
||||
{ label: LABELS.VISITOR_NAME, value: orderData.visitorName },
|
||||
{ label: LABELS.CONTACT_PHONE, value: orderData.contactPhone },
|
||||
];
|
||||
|
||||
case ORDER_TYPES.TICKET:
|
||||
case ORDER_TYPES.OTHER:
|
||||
return [
|
||||
{
|
||||
label: LABELS.QUANTITY,
|
||||
value: formatQuantity(orderData.commodityAmount),
|
||||
},
|
||||
];
|
||||
|
||||
default:
|
||||
// 兜底情况,返回空数组
|
||||
return [];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.right {
|
||||
&::before {
|
||||
content: "¥";
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
// 订单类型
|
||||
export const ORDER_TYPE_MAP = {
|
||||
0: [
|
||||
{
|
||||
label: '订单编号',
|
||||
key: 'orderId'
|
||||
},
|
||||
{
|
||||
label: '入住时间',
|
||||
key: 'checkInData'
|
||||
},
|
||||
{
|
||||
label: '游客姓名',
|
||||
key: 'visitorName'
|
||||
},
|
||||
{
|
||||
label: '联系电话',
|
||||
key: 'contactPhone'
|
||||
}
|
||||
],
|
||||
1: [
|
||||
{
|
||||
label: '订单编号',
|
||||
key: 'orderId'
|
||||
},
|
||||
{
|
||||
label: '份数',
|
||||
key: 'commodityAmount'
|
||||
}
|
||||
],
|
||||
2: [
|
||||
{
|
||||
label: '订单编号',
|
||||
key: 'orderId'
|
||||
},
|
||||
{
|
||||
label: '份数',
|
||||
key: 'commodityAmount'
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
// 工单类型
|
||||
export const SERVICE_TYPE_MAP = {
|
||||
0: [
|
||||
{
|
||||
label: '创建时间',
|
||||
key: 'createTime'
|
||||
},
|
||||
{
|
||||
label: '联系房客',
|
||||
key: 'userName'
|
||||
},
|
||||
{
|
||||
label: '联系电话',
|
||||
key: 'userPhone'
|
||||
}
|
||||
],
|
||||
}
|
||||
119
src/pages-service/order/components/OrderCard/index.vue
Normal file
119
src/pages-service/order/components/OrderCard/index.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<view
|
||||
class="order-card bg-white border-box p-12 rounded-12 m-12"
|
||||
@click="handleCardClick"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header flex items-center">
|
||||
<view class="status-info flex items-center flex-full">
|
||||
<image class="status-icon mr-4" :src="getStatusIcon()" />
|
||||
<view class="order-title font-size-14 line-height-20 color-525866">
|
||||
{{ getOrderTypeName() }}
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
v-if="orderData.status !== 'pending'"
|
||||
:class="[
|
||||
'status-tag font-size-12',
|
||||
`tag-${orderData.orderStatus || orderData.workOrderStatus}`,
|
||||
]"
|
||||
>
|
||||
{{ getStatusText(orderData.orderStatus || orderData.workOrderStatus) }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<OrderCardContent :order-data="orderData" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from "vue";
|
||||
import OrderCardContent from "./OrderCardContent.vue";
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
id: "",
|
||||
workOrderTypeName: "",
|
||||
createTime: "",
|
||||
contactName: "",
|
||||
contactPhone: "",
|
||||
orderStatus: "0", // pending-待处理, completed-已完成, cancelled-已取消
|
||||
orderType: undefined, // 0-酒店订单, 1-门票订单, 2-其他订单, undefined-工单
|
||||
orderNumber: "", // 订单编号
|
||||
checkInTime: "", // 入住时间
|
||||
visitorName: "", // 游客姓名
|
||||
commodityAmount: 0, // 份数
|
||||
workOrderStatus: 0, // 工单状态:0-待处理, 1-已完成
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(["click", "call"]);
|
||||
|
||||
// 图标映射
|
||||
const ICON_MAP = {
|
||||
0: "https://oss.nianxx.cn/mp/static/version_101/order/room.png", // 酒店订单
|
||||
1: "https://oss.nianxx.cn/mp/static/version_101/order/ticket.png", // 门票订单
|
||||
2: "https://oss.nianxx.cn/mp/static/version_101/order/food.png", // 餐饮
|
||||
};
|
||||
|
||||
// 订单类型映射
|
||||
const ORDER_NAME_MAP = {
|
||||
0: "房间",
|
||||
1: "门票",
|
||||
2: "餐饮",
|
||||
};
|
||||
|
||||
// 获取状态图标
|
||||
const getStatusIcon = () => {
|
||||
// 订单情况:根据 orderType 返回对应图标
|
||||
return ICON_MAP[props.orderData.orderType];
|
||||
};
|
||||
|
||||
// 获取订单类型名称
|
||||
const getOrderTypeName = () => {
|
||||
return ORDER_NAME_MAP[props.orderData.orderType] || "其他订单";
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status) => {
|
||||
// 工单情况:orderType 为 undefined
|
||||
if (props.orderData.orderType === undefined) {
|
||||
const workOrderStatusMap = {
|
||||
0: "待接单",
|
||||
1: "处理中",
|
||||
2: "已完成",
|
||||
3: "已关闭",
|
||||
};
|
||||
return workOrderStatusMap[status] || "未知状态";
|
||||
}
|
||||
|
||||
// 订单情况:orderType 有值
|
||||
const orderStatusMap = {
|
||||
0: "待支付",
|
||||
1: "待确认",
|
||||
2: "待使用",
|
||||
3: "已取消",
|
||||
4: "退款中",
|
||||
5: "已退款",
|
||||
6: "已完成",
|
||||
};
|
||||
return orderStatusMap[status] || "未知状态";
|
||||
};
|
||||
|
||||
// 处理卡片点击
|
||||
const handleCardClick = () => {
|
||||
if (props.orderData.orderType === undefined) return;
|
||||
emit("click", props.orderData);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,43 @@
|
||||
.order-card {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
&.tag-0 {
|
||||
color: #ff3d60;
|
||||
}
|
||||
|
||||
&.tag-1 {
|
||||
color: #f00044;
|
||||
}
|
||||
|
||||
&.tag-2 {
|
||||
color: #40ae36;
|
||||
}
|
||||
|
||||
&.tag-3 {
|
||||
color: #808389;
|
||||
}
|
||||
|
||||
&.tag-4 {
|
||||
color: #2d91ff;
|
||||
}
|
||||
|
||||
&.tag-5 {
|
||||
color: #808389;
|
||||
}
|
||||
|
||||
&.tag-6 {
|
||||
color: #fd8702;
|
||||
}
|
||||
}
|
||||
208
src/pages-service/order/components/OrderInfo/index.vue
Normal file
208
src/pages-service/order/components/OrderInfo/index.vue
Normal file
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<view class="order-info">
|
||||
<view class="order-item">
|
||||
<text class="label">订单号</text>
|
||||
<text class="value">{{ orderData.orderId }}</text>
|
||||
</view>
|
||||
<view class="order-item">
|
||||
<text class="label">流水号</text>
|
||||
<text class="value">{{ orderData.paySerialNumber }}</text>
|
||||
</view>
|
||||
<view class="order-item">
|
||||
<text class="label">支付方式</text>
|
||||
<text class="value">{{ payWayText }}</text>
|
||||
</view>
|
||||
<!-- 在已退款状态显示 -->
|
||||
<view v-if="orderData.orderStatus === '4'" class="order-item">
|
||||
<text class="label">退款单号</text>
|
||||
<text class="value">{{ orderData.refundOrderNo }}</text>
|
||||
</view>
|
||||
<view class="line"></view>
|
||||
<view class="order-item amount">
|
||||
<text class="label">实际支付金额</text>
|
||||
<text class="value">{{ formattedAmount }}</text>
|
||||
</view>
|
||||
<!-- 根据订单状态动态显示按钮 -->
|
||||
<button
|
||||
v-if="shouldShowButton"
|
||||
:class="['reserve-button', { loading: isLoading }]"
|
||||
:disabled="isLoading"
|
||||
@click="handleButtonClick(orderData)"
|
||||
>
|
||||
{{ isLoading ? "处理中..." : buttonText }}
|
||||
</button>
|
||||
<view class="feedback">
|
||||
<text @click="openFeedback">投诉反馈</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed, ref, defineEmits } from "vue";
|
||||
import { orderPayNow } from "@/request/api/OrderApi";
|
||||
|
||||
// 支付方式映射常量
|
||||
const PAY_WAY_MAP = {
|
||||
0: "微信",
|
||||
1: "支付宝",
|
||||
2: "云闪付",
|
||||
};
|
||||
|
||||
// 加载状态
|
||||
const isLoading = ref(false);
|
||||
|
||||
// 定义事件发射器
|
||||
const emit = defineEmits(["show-refund-popup", "pay-success"]);
|
||||
|
||||
const props = defineProps({
|
||||
orderData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({
|
||||
orderId: "",
|
||||
paySerialNumber: "",
|
||||
payWay: "", // 支付方式 0-微信 1-支付宝 2-云闪付
|
||||
payAmt: "",
|
||||
orderStatus: "0", // 订单状态 0-待支付 1-待确认 2-待使用 3-已取消 4-退款中 5-已关闭 6-已完成
|
||||
orderType: "0", // 0-酒店订单, 1-门票订单, 2-餐饮
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
// 使用计算属性缓存支付方式文本
|
||||
const payWayText = computed(() => {
|
||||
return PAY_WAY_MAP[props.orderData.payWay] || "未知支付方式";
|
||||
});
|
||||
|
||||
// 格式化金额显示
|
||||
const formattedAmount = computed(() => {
|
||||
const amount = props.orderData.payAmt;
|
||||
return amount ? `${parseFloat(amount).toFixed(2)}` : "0.00";
|
||||
});
|
||||
|
||||
// 按钮文案逻辑,订单状态 0-待支付 1-待确认 2-待使用 3-已取消 4-退款中 5-已关闭 6-已完成
|
||||
const buttonText = computed(() => {
|
||||
const status = props.orderData.orderStatus;
|
||||
switch (status) {
|
||||
case "0": // 待支付状态
|
||||
return "立即支付";
|
||||
case "2": // 待使用状态
|
||||
return "申请退款";
|
||||
case "3": // 已取消状态
|
||||
case "5": // 已关闭状态
|
||||
case "6": // 已完成状态
|
||||
return "再次预定";
|
||||
}
|
||||
});
|
||||
|
||||
// 是否显示按钮(待支付、待使用、已取消、已关闭、已完成)
|
||||
const shouldShowButton = computed(() => {
|
||||
const status = props.orderData.orderStatus;
|
||||
return ["0", "2", "3", "5", "6"].includes(status);
|
||||
});
|
||||
|
||||
// 处理按钮点击事件
|
||||
const handleButtonClick = async (orderData) => {
|
||||
if (isLoading.value) return; // 防止重复点击
|
||||
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
const status = orderData.orderStatus;
|
||||
|
||||
if (status === "2") {
|
||||
// 情况2:待使用状态,显示退款弹窗
|
||||
emit("show-refund-popup");
|
||||
return; // 直接返回,不执行后续代码
|
||||
}
|
||||
|
||||
// 再次预定跳转商品详情
|
||||
if (["3", "5", "6"].includes(status)) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/goods/index?commodityId=${orderData.commodityId}`,
|
||||
});
|
||||
}
|
||||
|
||||
// 待支付状态,调用支付接口
|
||||
if (status === "0") {
|
||||
const orderId = orderData.orderId;
|
||||
const payWay = orderData.payWay;
|
||||
const paySource = orderData.paySource;
|
||||
|
||||
const res = await orderPayNow({ orderId, payWay, paySource });
|
||||
console.log("确认订单---2:", res);
|
||||
|
||||
// 检查接口返回数据
|
||||
if (!res || !res.data) {
|
||||
uni.showToast({
|
||||
title: "订单创建失败,请重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const { data } = res;
|
||||
const { nonceStr, packageVal, paySign, signType, timeStamp } = data;
|
||||
|
||||
// 验证支付参数是否完整
|
||||
if (!nonceStr || !packageVal || !paySign || !signType || !timeStamp) {
|
||||
console.error("支付参数不完整:", {
|
||||
nonceStr: !!nonceStr,
|
||||
packageVal: !!packageVal,
|
||||
paySign: !!paySign,
|
||||
signType: !!signType,
|
||||
timeStamp: !!timeStamp,
|
||||
});
|
||||
uni.showToast({
|
||||
title: "支付参数错误,请重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用微信支付
|
||||
uni.requestPayment({
|
||||
provider: "wxpay",
|
||||
timeStamp: String(timeStamp), // 确保为字符串类型
|
||||
nonceStr: String(nonceStr),
|
||||
package: String(packageVal), // 确保为字符串类型
|
||||
signType: String(signType),
|
||||
paySign: String(paySign),
|
||||
success: () => {
|
||||
uni.showToast({
|
||||
title: "支付成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
success: () => {
|
||||
emit("pay-success");
|
||||
},
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
uni.showToast({
|
||||
title: "支付失败,请重试",
|
||||
icon: "none",
|
||||
duration: 2000,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("操作失败:", error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 投诉电话
|
||||
const openFeedback = () => {
|
||||
const phoneNumber = props.orderData.complaintHotline;
|
||||
uni.makePhoneCall({ phoneNumber });
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
156
src/pages-service/order/components/OrderInfo/styles/index.scss
Normal file
156
src/pages-service/order/components/OrderInfo/styles/index.scss
Normal file
@@ -0,0 +1,156 @@
|
||||
// 颜色系统
|
||||
$order-bg-color: #fff;
|
||||
$text-color-primary: #333;
|
||||
$text-color-secondary: #666;
|
||||
$text-color-accent: #ff5722;
|
||||
$button-color: #00a6ff;
|
||||
$button-disabled-color: #ccc;
|
||||
$border-color: #ececec;
|
||||
$shadow-color: rgba(0, 0, 0, 0.08);
|
||||
|
||||
// 尺寸和间距
|
||||
$order-border-radius: 10px;
|
||||
$order-padding: 16px 18px;
|
||||
$spacing-small: 8px;
|
||||
$spacing-medium: 10px;
|
||||
$spacing-large: 20px;
|
||||
$button-height: 42px;
|
||||
|
||||
// 字体系统
|
||||
$font-size-small: 12px;
|
||||
$font-size-medium: 14px;
|
||||
$font-size-large: 18px;
|
||||
$font-weight-normal: 400;
|
||||
$font-weight-medium: 500;
|
||||
$font-weight-semibold: 600;
|
||||
|
||||
// 过渡动画
|
||||
$transition-fast: 0.2s ease;
|
||||
$transition-normal: 0.3s ease;
|
||||
|
||||
.order-info {
|
||||
background-color: $order-bg-color;
|
||||
border-radius: $order-border-radius;
|
||||
padding: $order-padding;
|
||||
box-shadow: 0 2px 8px $shadow-color;
|
||||
transition: box-shadow $transition-normal;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
// 订单项样式,优化布局和视觉层次
|
||||
.order-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: $spacing-small;
|
||||
padding: 4px 0;
|
||||
transition: background-color $transition-fast;
|
||||
|
||||
.label {
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-secondary;
|
||||
font-weight: $font-weight-normal;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: $font-size-small;
|
||||
color: $text-color-primary;
|
||||
font-weight: $font-weight-normal;
|
||||
text-align: right;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
line-height: 1.4;
|
||||
max-width: 60%;
|
||||
}
|
||||
|
||||
// 金额特殊样式,增强视觉重点
|
||||
&.amount {
|
||||
.label {
|
||||
color: $text-color-primary;
|
||||
font-weight: $font-weight-medium;
|
||||
font-size: $font-size-medium;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: $text-color-accent;
|
||||
font-size: $font-size-large;
|
||||
font-weight: $font-weight-semibold;
|
||||
max-width: none;
|
||||
|
||||
// 货币符号样式
|
||||
&::before {
|
||||
content: "¥";
|
||||
margin-right: 2px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
border-bottom: 1px solid $border-color;
|
||||
margin: $spacing-medium 0;
|
||||
height: 0;
|
||||
opacity: 0.6;
|
||||
transition: opacity $transition-fast;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.reserve-button {
|
||||
width: 100%;
|
||||
background: linear-gradient(179deg, #00a6ff 0%, #0256ff 100%);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: $uni-border-radius-50px;
|
||||
height: $button-height;
|
||||
font-size: $font-size-medium;
|
||||
font-weight: $font-weight-medium;
|
||||
margin-top: $spacing-large;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
&:disabled {
|
||||
background: $button-disabled-color;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态样式
|
||||
&.loading {
|
||||
background: $button-disabled-color;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.feedback {
|
||||
text-align: center;
|
||||
font-size: $font-size-medium;
|
||||
color: $text-color-primary;
|
||||
font-weight: $font-weight-normal;
|
||||
margin-top: $spacing-medium;
|
||||
}
|
||||
}
|
||||
63
src/pages-service/order/list.vue
Normal file
63
src/pages-service/order/list.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<z-paging
|
||||
bg-color="linear-gradient(180deg, #D9EEFF 0%, #F5F7FA 100%) 0 86px / 100% 100px no-repeat"
|
||||
ref="paging"
|
||||
v-model="dataList"
|
||||
use-virtual-list
|
||||
:force-close-inner-list="true"
|
||||
cell-height-mode="dynamic"
|
||||
safe-area-inset-bottom
|
||||
@query="queryList"
|
||||
>
|
||||
<template #top>
|
||||
<TopNavBar title="全部服务工单" />
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<CustomEmpty
|
||||
emptyIcon="https://oss.nianxx.cn/mp/static/version_101/order/service_empty.png"
|
||||
statusText="您暂无服务工单"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<OrderCard
|
||||
v-for="(item, index) in dataList"
|
||||
:key="item.id || index"
|
||||
:orderData="item"
|
||||
/>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import TopNavBar from "@/components/TopNavBar/index.vue";
|
||||
import CustomEmpty from "@/components/CustomEmpty/index.vue";
|
||||
import OrderCard from "./components/OrderCard/index.vue";
|
||||
import { userWorkOrderList } from "@/request/api/OrderApi";
|
||||
|
||||
const dataList = ref([]);
|
||||
const paging = ref(null);
|
||||
|
||||
const queryList = async (pageNum, pageSize) => {
|
||||
try {
|
||||
const res = await userWorkOrderList({ pageNum, pageSize });
|
||||
console.log("API响应:", res);
|
||||
|
||||
if (res && res.data && res.data.records) {
|
||||
const records = res.data.records;
|
||||
|
||||
// 完成数据加载,第二个参数表示是否还有更多数据
|
||||
paging.value.complete(records);
|
||||
} else {
|
||||
// 没有数据
|
||||
paging.value.complete([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("查询列表失败:", error);
|
||||
// 加载失败
|
||||
paging.value.complete(false);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -36,6 +36,28 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages-service",
|
||||
"pages": [
|
||||
{
|
||||
"path": "order/list",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages-quick",
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
||||
@@ -52,6 +52,13 @@ const sendReply = (item) => {
|
||||
uni.$emit("SHOW_MORE_POPUP");
|
||||
return;
|
||||
}
|
||||
|
||||
// 快速预定
|
||||
if (item.type === Command.quickBooking) {
|
||||
uni.navigateTo({ url: "/pages-quick/list" });
|
||||
return;
|
||||
}
|
||||
|
||||
uni.$emit(SEND_MESSAGE_COMMAND_TYPE, item);
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -52,7 +52,7 @@ const list = ref([
|
||||
content: "预定门票、房间、餐食",
|
||||
btnText: "去预定",
|
||||
type: Command.quickBooking,
|
||||
path: "",
|
||||
path: "/pages-quick/list",
|
||||
},
|
||||
{
|
||||
icon: "https://oss.nianxx.cn/mp/static/version_101/home/tsfx.png",
|
||||
@@ -76,7 +76,7 @@ const list = ref([
|
||||
content: "查看服务工单、进度与处理情况",
|
||||
btnText: "去查看",
|
||||
type: Command.myWorkOrder,
|
||||
path: "/pages/myWorkOrder/index",
|
||||
path: "/pages-service/order/list",
|
||||
},
|
||||
{
|
||||
icon: "https://oss.nianxx.cn/mp/static/version_101/home/fkyj.png",
|
||||
@@ -98,10 +98,12 @@ const close = () => {
|
||||
|
||||
const handleClick = (item) => {
|
||||
close();
|
||||
|
||||
if (item.path) {
|
||||
uni.navigateTo({ url: item.path });
|
||||
return;
|
||||
}
|
||||
|
||||
uni.$emit(SEND_MESSAGE_COMMAND_TYPE, item);
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@
|
||||
background-color: #17294e;
|
||||
}
|
||||
|
||||
.bg-E5E8EE {
|
||||
background-color: #e5e8ee;
|
||||
}
|
||||
|
||||
.bg-F5F7FA {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.bg-liner {
|
||||
background: linear-gradient(205deg, #8ae3fc 0%, rgba(138, 227, 252, 0) 20%),
|
||||
linear-gradient(155deg, #fef7e1 0%, rgba(254, 247, 225, 0) 20%),
|
||||
@@ -21,3 +29,7 @@
|
||||
),
|
||||
linear-gradient(180deg, rgba(238, 248, 255, 0) 0%, #eef8ff 50%), #eef8ff;
|
||||
}
|
||||
|
||||
.bg-button {
|
||||
background: linear-gradient(90deg, #2d91ff 0%, #4de4ff 100%);
|
||||
}
|
||||
|
||||
@@ -6,3 +6,11 @@
|
||||
.border-bottom {
|
||||
border-bottom: 1px solid #e5e8ee;
|
||||
}
|
||||
|
||||
.border-ff {
|
||||
border: 1px solid #fff;
|
||||
}
|
||||
|
||||
.border-none {
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -51,3 +51,7 @@
|
||||
.color-21B466 {
|
||||
color: #21b466;
|
||||
}
|
||||
|
||||
.color-FA7319 {
|
||||
color: #fa7319;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// 外边距-bottom
|
||||
.ml-auto {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.m-4 {
|
||||
margin: 4px;
|
||||
}
|
||||
@@ -7,6 +11,18 @@
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.m-8 {
|
||||
margin: 8px;
|
||||
}
|
||||
@@ -15,6 +31,10 @@
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.ml-8 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mr-8 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -39,18 +59,18 @@
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mt-6 {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.ml-12 {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.ml-16 {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.ml-20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.rounded-10 {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.rounded-12 {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
@@ -299,12 +299,33 @@ export class ThrottleUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期工具类
|
||||
* 提供日期格式化功能
|
||||
* */
|
||||
export class DateUtils {
|
||||
/**
|
||||
* 格式化日期为指定格式
|
||||
* @param {Date} date - 日期对象
|
||||
* @param {string} format - 格式化字符串,默认值为 "yyyy-MM-dd"
|
||||
* @returns {string} 格式化后的日期字符串
|
||||
*/
|
||||
static formatDate(date = new Date(), format = "yyyy-MM-dd") {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
|
||||
return format.replace("yyyy", year).replace("MM", month).replace("dd", day);
|
||||
}
|
||||
}
|
||||
|
||||
// 默认导出所有工具类
|
||||
export default {
|
||||
IdUtils,
|
||||
CallbackUtils,
|
||||
MessageUtils,
|
||||
TimerUtils,
|
||||
DateUtils,
|
||||
DebounceUtils,
|
||||
ThrottleUtils,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user