2026-07-11 16:14:36 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view v-if="step" class="door-overlay">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 开门中 -->
|
|
|
|
|
|
<view v-if="step === 'opening'" class="door-overlay-content">
|
|
|
|
|
|
<view class="door-spinner">
|
|
|
|
|
|
<view class="spinner-dot" v-for="i in 3" :key="i"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="door-overlay-text">开门中...</text>
|
|
|
|
|
|
<text class="door-overlay-tip">请稍候,正在为您打开柜门</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 已开门 -->
|
|
|
|
|
|
<view v-if="step === 'opened'" class="door-state-bg">
|
|
|
|
|
|
<view class="door-state-content">
|
|
|
|
|
|
<view class="door-state-icon-wrap">
|
|
|
|
|
|
<view class="door-state-ring"></view>
|
|
|
|
|
|
<view class="door-state-icon">
|
|
|
|
|
|
<view class="icon-door-left"></view>
|
|
|
|
|
|
<view class="icon-door-right"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-07-13 17:27:06 +08:00
|
|
|
|
<text class="door-state-title">门锁已打开</text>
|
|
|
|
|
|
<text class="door-state-desc">请先拉开柜门,取走商品后随手关门</text>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
<view class="door-state-divider"></view>
|
|
|
|
|
|
<view class="door-steps">
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot active">1</view>
|
|
|
|
|
|
<text class="step-text">取走商品</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="step-line"></view>
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot">2</view>
|
|
|
|
|
|
<text class="step-text">关闭柜门</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="step-line"></view>
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot">3</view>
|
|
|
|
|
|
<text class="step-text">完成订单</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:56:15 +08:00
|
|
|
|
<view class="door-btn-area">
|
|
|
|
|
|
<view class="door-btn-close" @click="handleClose">返回首页</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 已关门 -->
|
|
|
|
|
|
<view v-if="step === 'closed'" class="door-state-bg">
|
|
|
|
|
|
<view class="door-state-content">
|
|
|
|
|
|
<view class="door-state-icon-wrap">
|
|
|
|
|
|
<view class="door-state-ring warning-ring"></view>
|
|
|
|
|
|
<view class="door-state-icon">
|
|
|
|
|
|
<view class="icon-lock">
|
|
|
|
|
|
<view class="lock-body"></view>
|
|
|
|
|
|
<view class="lock-hole"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="door-state-title">柜门已关闭</text>
|
|
|
|
|
|
<text class="door-state-desc">订单处理中,请稍候...</text>
|
|
|
|
|
|
<view class="door-state-divider"></view>
|
|
|
|
|
|
<view class="door-steps">
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot done">✓</view>
|
|
|
|
|
|
<text class="step-text">取走商品</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="step-line done"></view>
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot active">2</view>
|
|
|
|
|
|
<text class="step-text">已关柜门</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="step-line"></view>
|
|
|
|
|
|
<view class="step-item">
|
|
|
|
|
|
<view class="step-dot">3</view>
|
|
|
|
|
|
<text class="step-text">完成订单</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:56:15 +08:00
|
|
|
|
<view class="door-btn-area">
|
|
|
|
|
|
<view class="door-btn-close" @click="handleClose">返回首页</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 订单完成 -->
|
|
|
|
|
|
<view v-if="step === 'complete'" class="door-state-bg">
|
|
|
|
|
|
<view class="door-state-content door-complete-content">
|
|
|
|
|
|
<view class="door-state-icon-wrap">
|
|
|
|
|
|
<view class="door-state-ring success-ring"></view>
|
|
|
|
|
|
<view class="door-state-icon">
|
|
|
|
|
|
<view class="icon-check"></view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<text class="door-state-title">订单已完成</text>
|
|
|
|
|
|
<text class="door-state-desc">感谢您的购买</text>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 订单信息 -->
|
|
|
|
|
|
<view class="door-order-card" v-if="orderInfo">
|
2026-07-11 17:17:34 +08:00
|
|
|
|
<!-- 商品列表 -->
|
|
|
|
|
|
<view class="door-product-list" v-if="orderInfo.products && orderInfo.products.length">
|
|
|
|
|
|
<view class="door-product-item" v-for="(p, idx) in orderInfo.products" :key="idx">
|
|
|
|
|
|
<image class="door-product-img" :src="p.image" mode="aspectFill" />
|
|
|
|
|
|
<view class="door-product-info">
|
|
|
|
|
|
<text class="door-product-name">{{ p.name }}</text>
|
|
|
|
|
|
<text class="door-product-price">¥{{ getDecimal(p.unit_price) }} x {{ p.qty }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
<view class="door-order-row">
|
|
|
|
|
|
<text class="door-order-label">订单编号</text>
|
|
|
|
|
|
<text class="door-order-value">{{ orderInfo.order_no }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="door-order-row">
|
|
|
|
|
|
<text class="door-order-label">商品数量</text>
|
2026-07-11 17:17:34 +08:00
|
|
|
|
<text class="door-order-value">{{ orderInfo.total_qty || '-' }} 件</text>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="door-order-row total">
|
|
|
|
|
|
<text class="door-order-label">订单金额</text>
|
2026-07-11 17:17:34 +08:00
|
|
|
|
<text class="door-order-value door-price">¥{{ getDecimal(orderInfo.total_amount) }}</text>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-07-11 16:56:15 +08:00
|
|
|
|
<view class="door-btn-area">
|
|
|
|
|
|
<view class="door-btn-detail" @click="goOrderDetail">查看订单详情</view>
|
|
|
|
|
|
<view class="door-btn-close" @click="handleClose">返回首页</view>
|
|
|
|
|
|
</view>
|
2026-07-11 16:14:36 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import { get, post } from '@/utils/request.js';
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
props: {
|
|
|
|
|
|
deviceId: { type: String, default: '' },
|
|
|
|
|
|
userId: { type: String, default: '' }
|
|
|
|
|
|
},
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
step: '', // '' | 'opening' | 'opened' | 'closed' | 'complete'
|
|
|
|
|
|
flowId: '',
|
|
|
|
|
|
orderInfo: null,
|
|
|
|
|
|
orderLoading: false,
|
|
|
|
|
|
cancelled: false
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
beforeDestroy() {
|
|
|
|
|
|
this.cancelled = true;
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
// 外部调用:启动开门流程
|
2026-07-11 20:43:56 +08:00
|
|
|
|
async open(userId) {
|
2026-07-11 16:14:36 +08:00
|
|
|
|
if (this.step) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.cancelled = false;
|
|
|
|
|
|
this.step = 'opening';
|
|
|
|
|
|
|
2026-07-11 20:43:56 +08:00
|
|
|
|
const uid = userId || this.userId;
|
|
|
|
|
|
|
2026-07-11 16:14:36 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const res = await post('/device-scan/open', {
|
|
|
|
|
|
device_id: this.deviceId,
|
|
|
|
|
|
door_index: 0,
|
2026-07-11 20:43:56 +08:00
|
|
|
|
user_id: uid
|
2026-07-11 16:14:36 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const ret = res.data || {};
|
|
|
|
|
|
if (ret.code !== 0) {
|
|
|
|
|
|
this.step = '';
|
|
|
|
|
|
uni.showToast({ title: ret.message || '开门失败', icon: 'none' });
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const flowId = ret.data && ret.data.flow_id;
|
|
|
|
|
|
if (!flowId) {
|
|
|
|
|
|
this.step = '';
|
|
|
|
|
|
uni.showToast({ title: '未获取到开门流水号', icon: 'none' });
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.flowId = flowId;
|
|
|
|
|
|
this.pollStatus(flowId);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
this.step = '';
|
|
|
|
|
|
uni.showToast({ title: '网络请求失败', icon: 'none' });
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 轮询状态
|
|
|
|
|
|
async pollStatus(flowId) {
|
|
|
|
|
|
const maxRetries = 30;
|
|
|
|
|
|
const interval = 2000;
|
|
|
|
|
|
let remaining = maxRetries;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
let lastStep = '';
|
2026-07-11 16:14:36 +08:00
|
|
|
|
|
|
|
|
|
|
while (remaining > 0) {
|
|
|
|
|
|
if (this.step === 'complete' || this.cancelled) return;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await get('/flow/getStatusLog', {
|
|
|
|
|
|
flow_id: flowId,
|
|
|
|
|
|
status: '101,103,201'
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const ret = res.data || {};
|
|
|
|
|
|
if (ret.code === 0 && Array.isArray(ret.data) && ret.data.length > 0) {
|
|
|
|
|
|
const latestStatus = ret.data[0].status;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
let newStep = '';
|
2026-07-11 16:14:36 +08:00
|
|
|
|
|
|
|
|
|
|
if (latestStatus === 101) {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
newStep = 'opened';
|
2026-07-11 16:14:36 +08:00
|
|
|
|
} else if (latestStatus === 103) {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
newStep = 'closed';
|
2026-07-11 16:14:36 +08:00
|
|
|
|
} else if (latestStatus === 201) {
|
|
|
|
|
|
this.step = 'complete';
|
2026-07-11 17:22:41 +08:00
|
|
|
|
this.fetchOrder(flowId);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-13 17:27:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 状态真正变化时才重置计数器
|
|
|
|
|
|
if (newStep && newStep !== lastStep) {
|
|
|
|
|
|
this.step = newStep;
|
|
|
|
|
|
lastStep = newStep;
|
|
|
|
|
|
remaining = maxRetries;
|
|
|
|
|
|
}
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('[door-panel] 轮询失败:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
remaining--;
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, interval));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-13 17:37:51 +08:00
|
|
|
|
// 轮询结束
|
|
|
|
|
|
if (this.step === 'opening') {
|
|
|
|
|
|
this.step = '';
|
|
|
|
|
|
uni.showToast({ title: '开门超时,请重试', icon: 'none' });
|
|
|
|
|
|
}
|
2026-07-11 16:14:36 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 查询订单信息
|
2026-07-11 17:17:34 +08:00
|
|
|
|
async fetchOrder(orderId) {
|
|
|
|
|
|
if (!orderId || !this.userId) {
|
|
|
|
|
|
this.orderLoading = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-11 16:14:36 +08:00
|
|
|
|
this.orderLoading = true;
|
|
|
|
|
|
try {
|
2026-07-11 17:17:34 +08:00
|
|
|
|
const res = await get('/user/orders', {
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
limit: 1,
|
|
|
|
|
|
filter: JSON.stringify({ user_id: this.userId, order_id: orderId })
|
|
|
|
|
|
});
|
2026-07-11 16:14:36 +08:00
|
|
|
|
const ret = res.data || {};
|
2026-07-11 17:17:34 +08:00
|
|
|
|
if (ret.code === 0 && Array.isArray(ret.data) && ret.data.length > 0) {
|
|
|
|
|
|
this.orderInfo = ret.data[0];
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('[door-panel] 获取订单信息失败:', e);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.orderLoading = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-07-11 17:17:34 +08:00
|
|
|
|
// MongoDB Decimal128 转换
|
|
|
|
|
|
getDecimal(val) {
|
|
|
|
|
|
if (val == null) return '0.00';
|
|
|
|
|
|
if (typeof val === 'object' && val.$numberDecimal) return val.$numberDecimal;
|
|
|
|
|
|
return String(val);
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-07-11 16:41:57 +08:00
|
|
|
|
// 跳转订单详情
|
|
|
|
|
|
goOrderDetail() {
|
2026-07-11 16:56:15 +08:00
|
|
|
|
if (this.orderInfo) {
|
2026-07-11 17:17:34 +08:00
|
|
|
|
const orderId = this.orderInfo.order_id || '';
|
|
|
|
|
|
uni.setStorageSync('__order_detail_cache', this.orderInfo);
|
2026-07-11 16:56:15 +08:00
|
|
|
|
uni.navigateTo({ url: `/pages/order/detail?order_id=${orderId}` });
|
|
|
|
|
|
} else if (this.flowId) {
|
|
|
|
|
|
uni.navigateTo({ url: `/pages/order/detail?flow_id=${this.flowId}` });
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({ title: '无法获取订单信息', icon: 'none' });
|
|
|
|
|
|
}
|
2026-07-11 16:41:57 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-07-11 16:14:36 +08:00
|
|
|
|
// 关闭遮罩
|
|
|
|
|
|
handleClose() {
|
|
|
|
|
|
this.step = '';
|
|
|
|
|
|
this.flowId = '';
|
|
|
|
|
|
this.orderInfo = null;
|
|
|
|
|
|
this.orderLoading = false;
|
|
|
|
|
|
this.$emit('close');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
/* ====== 开门中遮罩 ====== */
|
|
|
|
|
|
.door-overlay {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
|
|
|
z-index: 3000;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-overlay-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 60rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-spinner {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
|
margin-bottom: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.spinner-dot {
|
|
|
|
|
|
width: 20rpx;
|
|
|
|
|
|
height: 20rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: #fff;
|
|
|
|
|
|
animation: spinner-bounce 1.2s ease-in-out infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
.spinner-dot:nth-child(2) { animation-delay: 0.2s; }
|
|
|
|
|
|
.spinner-dot:nth-child(3) { animation-delay: 0.4s; }
|
|
|
|
|
|
@keyframes spinner-bounce {
|
|
|
|
|
|
0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
|
|
|
|
30% { transform: translateY(-20rpx); opacity: 1; }
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-overlay-text {
|
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-overlay-tip {
|
|
|
|
|
|
font-size: 26rpx;
|
|
|
|
|
|
color: rgba(255, 255, 255, 0.7);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 通用状态页背景 ====== */
|
|
|
|
|
|
.door-state-bg {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
right: 0;
|
|
|
|
|
|
bottom: 0;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #fff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
z-index: 3000;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-state-content {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0 60rpx;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-complete-content {
|
|
|
|
|
|
max-height: 85vh;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 图标区域 ====== */
|
|
|
|
|
|
.door-state-icon-wrap {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 180rpx;
|
|
|
|
|
|
height: 180rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
margin-bottom: 48rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
/* 动效环 scale(1.6) 会超出容器,预留空间避免被裁剪 */
|
|
|
|
|
|
margin-top: 60rpx;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-state-ring {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
width: 180rpx;
|
|
|
|
|
|
height: 180rpx;
|
|
|
|
|
|
border-radius: 50%;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border: 4rpx solid rgba(41, 121, 255, 0.3);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
animation: ring-pulse 2s ease-out infinite;
|
|
|
|
|
|
}
|
|
|
|
|
|
.warning-ring {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-color: rgba(250, 173, 20, 0.3);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.success-ring {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-color: rgba(82, 196, 26, 0.3);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
@keyframes ring-pulse {
|
|
|
|
|
|
0% { transform: scale(0.8); opacity: 1; }
|
|
|
|
|
|
100% { transform: scale(1.6); opacity: 0; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.door-state-icon {
|
|
|
|
|
|
width: 80rpx;
|
|
|
|
|
|
height: 80rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
z-index: 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 柜门图标(已开门) */
|
|
|
|
|
|
.icon-door-left {
|
|
|
|
|
|
width: 28rpx;
|
|
|
|
|
|
height: 56rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 6rpx;
|
|
|
|
|
|
transform: perspective(200rpx) rotateY(25deg);
|
|
|
|
|
|
animation: door-left-open 0.8s ease-out forwards;
|
|
|
|
|
|
}
|
|
|
|
|
|
.icon-door-right {
|
|
|
|
|
|
width: 28rpx;
|
|
|
|
|
|
height: 56rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 6rpx;
|
|
|
|
|
|
transform: perspective(200rpx) rotateY(-25deg);
|
|
|
|
|
|
animation: door-right-open 0.8s ease-out forwards;
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes door-left-open {
|
|
|
|
|
|
0% { transform: perspective(200rpx) rotateY(0deg); }
|
|
|
|
|
|
100% { transform: perspective(200rpx) rotateY(35deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes door-right-open {
|
|
|
|
|
|
0% { transform: perspective(200rpx) rotateY(0deg); }
|
|
|
|
|
|
100% { transform: perspective(200rpx) rotateY(-35deg); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 锁图标(已关门) */
|
|
|
|
|
|
.icon-lock {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
width: 48rpx;
|
|
|
|
|
|
height: 56rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.lock-body {
|
|
|
|
|
|
width: 48rpx;
|
|
|
|
|
|
height: 36rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #faad14;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 6rpx;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.lock-hole {
|
|
|
|
|
|
width: 28rpx;
|
|
|
|
|
|
height: 24rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border: 5rpx solid #faad14;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 14rpx 14rpx 0 0;
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 50%;
|
|
|
|
|
|
transform: translateX(-50%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 勾图标(订单完成) */
|
|
|
|
|
|
.icon-check {
|
|
|
|
|
|
width: 40rpx;
|
|
|
|
|
|
height: 24rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-left: 5rpx solid #52c41a;
|
|
|
|
|
|
border-bottom: 5rpx solid #52c41a;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
transform: rotate(-45deg);
|
|
|
|
|
|
margin-top: -8rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 标题 ====== */
|
|
|
|
|
|
.door-state-title {
|
|
|
|
|
|
font-size: 44rpx;
|
|
|
|
|
|
font-weight: bold;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #333;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
margin-bottom: 16rpx;
|
|
|
|
|
|
letter-spacing: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-state-desc {
|
|
|
|
|
|
font-size: 28rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
margin-bottom: 48rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 分割线 ====== */
|
|
|
|
|
|
.door-state-divider {
|
|
|
|
|
|
width: 80rpx;
|
|
|
|
|
|
height: 4rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: linear-gradient(90deg, transparent, #2979ff, transparent);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 2rpx;
|
|
|
|
|
|
margin-bottom: 48rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 步骤条 ====== */
|
|
|
|
|
|
.door-steps {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-dot {
|
|
|
|
|
|
width: 44rpx;
|
|
|
|
|
|
height: 44rpx;
|
|
|
|
|
|
border-radius: 50%;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
|
border: 2rpx solid #e0e0e0;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 22rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.step-dot.active {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: rgba(41, 121, 255, 0.1);
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-color: #2979ff;
|
|
|
|
|
|
color: #2979ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-dot.done {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: rgba(41, 121, 255, 0.1);
|
|
|
|
|
|
border-color: #2979ff;
|
|
|
|
|
|
color: #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-text {
|
|
|
|
|
|
font-size: 22rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-line {
|
|
|
|
|
|
width: 60rpx;
|
|
|
|
|
|
height: 2rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #e0e0e0;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
margin-bottom: 36rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.step-line.done {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 订单卡片 ====== */
|
|
|
|
|
|
.door-order-card {
|
|
|
|
|
|
width: 100%;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #f8f8f8;
|
|
|
|
|
|
border: 1rpx solid #f0f0f0;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 20rpx;
|
|
|
|
|
|
padding: 24rpx 30rpx;
|
|
|
|
|
|
margin-top: 16rpx;
|
|
|
|
|
|
}
|
2026-07-11 17:17:34 +08:00
|
|
|
|
|
|
|
|
|
|
/* 商品列表 */
|
|
|
|
|
|
.door-product-list {
|
|
|
|
|
|
margin-bottom: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-product-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 12rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-product-item + .door-product-item {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-top: 1rpx solid #f0f0f0;
|
2026-07-11 17:17:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-product-img {
|
|
|
|
|
|
width: 80rpx;
|
|
|
|
|
|
height: 80rpx;
|
|
|
|
|
|
border-radius: 10rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #f0f0f0;
|
2026-07-11 17:17:34 +08:00
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-product-info {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
margin-left: 16rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 6rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-product-name {
|
|
|
|
|
|
font-size: 26rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #333;
|
2026-07-11 17:17:34 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-product-price {
|
|
|
|
|
|
font-size: 24rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 17:17:34 +08:00
|
|
|
|
}
|
2026-07-11 16:14:36 +08:00
|
|
|
|
.door-order-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 16rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-order-row + .door-order-row {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-top: 1rpx solid #f0f0f0;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-order-row.total {
|
2026-07-13 17:27:06 +08:00
|
|
|
|
border-top: 1rpx solid #e0e0e0;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
margin-top: 8rpx;
|
|
|
|
|
|
padding-top: 20rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.door-order-label {
|
|
|
|
|
|
font-size: 26rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-order-value {
|
|
|
|
|
|
font-size: 26rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #333;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-price {
|
|
|
|
|
|
font-size: 36rpx;
|
|
|
|
|
|
font-weight: bold;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #e4393c;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
.door-order-loading {
|
|
|
|
|
|
font-size: 26rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #999;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding: 16rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-11 16:56:15 +08:00
|
|
|
|
/* ====== 按钮区域 ====== */
|
|
|
|
|
|
.door-btn-area {
|
|
|
|
|
|
margin-top: 64rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-11 16:41:57 +08:00
|
|
|
|
/* ====== 查看订单详情按钮 ====== */
|
|
|
|
|
|
.door-btn-detail {
|
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
padding: 24rpx 80rpx;
|
|
|
|
|
|
background: linear-gradient(135deg, #2979ff, #1e60e0);
|
|
|
|
|
|
border-radius: 48rpx;
|
|
|
|
|
|
letter-spacing: 2rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-11 16:14:36 +08:00
|
|
|
|
/* ====== 关闭按钮 ====== */
|
|
|
|
|
|
.door-btn-close {
|
|
|
|
|
|
font-size: 30rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
color: #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
padding: 24rpx 100rpx;
|
2026-07-13 17:27:06 +08:00
|
|
|
|
background: #fff;
|
|
|
|
|
|
border: 2rpx solid #2979ff;
|
2026-07-11 16:14:36 +08:00
|
|
|
|
border-radius: 48rpx;
|
|
|
|
|
|
letter-spacing: 2rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|