fix: 优化订单列表、新增物流,订单详情(半成品)
29
README.md
@@ -10,32 +10,3 @@
|
||||
- Tailwind Css
|
||||
- Element Plus
|
||||
|
||||
# 运行截图
|
||||
|
||||
## 首页
|
||||

|
||||
|
||||
## 商品模态框
|
||||

|
||||
|
||||
## 商品详情
|
||||

|
||||
|
||||
## 商品介绍图(长图片)
|
||||

|
||||
|
||||
## 购物车
|
||||

|
||||
|
||||
## 个人中心
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
@@ -14,6 +14,7 @@
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@vueuse/head": "^2.0.0",
|
||||
"animate": "^1.0.0",
|
||||
"ant-design-vue": "^4.1.1",
|
||||
"axios": "^1.6.2",
|
||||
"element-plus": "^2.9.11",
|
||||
|
||||
43
pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
'@vueuse/head':
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0(vue@3.5.13)
|
||||
animate:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
ant-design-vue:
|
||||
specifier: ^4.1.1
|
||||
version: 4.2.6(vue@3.5.13)
|
||||
@@ -643,6 +646,9 @@ packages:
|
||||
'@vueuse/shared@9.13.0':
|
||||
resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
|
||||
|
||||
animate@1.0.0:
|
||||
resolution: {integrity: sha512-6QCwoJWDiE2Lm/jZAybrobHOHQgzhieBv9NSfUmErgk//FlC3pmIeqP27EXM+bSdiDizVDLckipwICpnC5Jwdw==}
|
||||
|
||||
ant-design-vue@4.2.6:
|
||||
resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
@@ -708,6 +714,9 @@ packages:
|
||||
dom-scroll-into-view@2.0.1:
|
||||
resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
|
||||
|
||||
dom-walk@0.1.2:
|
||||
resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -793,6 +802,9 @@ packages:
|
||||
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
global@4.3.2:
|
||||
resolution: {integrity: sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==}
|
||||
|
||||
gopd@1.2.0:
|
||||
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -949,6 +961,9 @@ packages:
|
||||
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
min-document@2.19.0:
|
||||
resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -993,9 +1008,16 @@ packages:
|
||||
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
process@0.5.2:
|
||||
resolution: {integrity: sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
rafl@1.2.2:
|
||||
resolution: {integrity: sha512-07d7yVpYE23jwMnJDrU5j/Ra2HzDUgyioKpdwzNVpfK0XN66zbjvny7EjiB/MFzPWk7tyq140mnXufpow4I42A==}
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
@@ -1587,6 +1609,10 @@ snapshots:
|
||||
- '@vue/composition-api'
|
||||
- vue
|
||||
|
||||
animate@1.0.0:
|
||||
dependencies:
|
||||
rafl: 1.2.2
|
||||
|
||||
ant-design-vue@4.2.6(vue@3.5.13):
|
||||
dependencies:
|
||||
'@ant-design/colors': 6.0.0
|
||||
@@ -1664,6 +1690,8 @@ snapshots:
|
||||
|
||||
dom-scroll-into-view@2.0.1: {}
|
||||
|
||||
dom-walk@0.1.2: {}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@@ -1786,6 +1814,11 @@ snapshots:
|
||||
dunder-proto: 1.0.1
|
||||
es-object-atoms: 1.1.1
|
||||
|
||||
global@4.3.2:
|
||||
dependencies:
|
||||
min-document: 2.19.0
|
||||
process: 0.5.2
|
||||
|
||||
gopd@1.2.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
@@ -1904,6 +1937,10 @@ snapshots:
|
||||
dependencies:
|
||||
mime-db: 1.52.0
|
||||
|
||||
min-document@2.19.0:
|
||||
dependencies:
|
||||
dom-walk: 0.1.2
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
nanopop@2.4.2: {}
|
||||
@@ -1938,8 +1975,14 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
process@0.5.2: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
rafl@1.2.2:
|
||||
dependencies:
|
||||
global: 4.3.2
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.0 MiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 911 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 6.8 MiB |
|
Before Width: | Height: | Size: 518 KiB |
|
Before Width: | Height: | Size: 816 KiB |
|
Before Width: | Height: | Size: 536 KiB |
194
src/App.vue
@@ -41,19 +41,31 @@
|
||||
v-model:visible="globalState.registerModalVisible"
|
||||
@confirm="handleRegisterConfirm"
|
||||
/>
|
||||
|
||||
<LogisticsComponent
|
||||
v-model:visible="globalState.logisticsModalVisible"
|
||||
:orderId="globalState.selectedOrderId"
|
||||
/>
|
||||
|
||||
<OrderDetailComponent
|
||||
v-model:visible="globalState.orderDetailModalVisible"
|
||||
:orderId="globalState.selectedOrderId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, provide } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import PaymentModal from '@/components/PaymentModal.vue'
|
||||
import ProductDetailModal from '@/components/ProductDetailModal.vue'
|
||||
import ShoppingCartDrawer from '@/components/ShoppingCartDrawer.vue'
|
||||
import SuccessModal from '@/components/SuccessModal.vue'
|
||||
import RechargeModal from '@/components/RechargeModal.vue'
|
||||
import RegisterModal from '@/components/RegisterModal.vue'
|
||||
import { reactive, provide } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import PaymentModal from '@/components/PaymentModal.vue';
|
||||
import ProductDetailModal from '@/components/ProductDetailModal.vue';
|
||||
import ShoppingCartDrawer from '@/components/ShoppingCartDrawer.vue';
|
||||
import SuccessModal from '@/components/SuccessModal.vue';
|
||||
import RechargeModal from '@/components/RechargeModal.vue';
|
||||
import RegisterModal from '@/components/RegisterModal.vue';
|
||||
import LogisticsComponent from '@/components/LogisticsComponent.vue';
|
||||
import OrderDetailComponent from '@/components/OrderDetailComponent.vue';
|
||||
import AppHeader from "@/layouts/Header.vue";
|
||||
import AppFooter from "@/layouts/Footer.vue";
|
||||
|
||||
@@ -72,48 +84,78 @@ const globalState = reactive({
|
||||
successModalVisible: false,
|
||||
rechargeModalVisible: false,
|
||||
registerModalVisible: false,
|
||||
logisticsModalVisible: false,
|
||||
orderDetailModalVisible: false,
|
||||
|
||||
// 弹窗数据
|
||||
selectedProduct: null,
|
||||
paymentAmount: 0,
|
||||
orderNumber: '',
|
||||
selectedOrderId: 0,
|
||||
|
||||
// 全局方法
|
||||
showPaymentModal: (amount) => {
|
||||
globalState.paymentAmount = amount
|
||||
globalState.paymentModalVisible = true
|
||||
globalState.paymentAmount = amount;
|
||||
globalState.paymentModalVisible = true;
|
||||
},
|
||||
|
||||
showProductDetail: (product) => {
|
||||
globalState.selectedProduct = product
|
||||
globalState.productDetailVisible = true
|
||||
globalState.selectedProduct = product;
|
||||
globalState.productDetailVisible = true;
|
||||
},
|
||||
|
||||
showCart: () => {
|
||||
globalState.cartVisible = true
|
||||
globalState.cartVisible = true;
|
||||
},
|
||||
|
||||
showRechargeModal: () => {
|
||||
globalState.rechargeModalVisible = true
|
||||
globalState.rechargeModalVisible = true;
|
||||
},
|
||||
|
||||
showRegisterModal: () => {
|
||||
globalState.registerModalVisible = true
|
||||
globalState.registerModalVisible = true;
|
||||
},
|
||||
|
||||
showLogisticsModal: (orderId) => {
|
||||
globalState.selectedOrderId = orderId;
|
||||
globalState.logisticsModalVisible = true;
|
||||
},
|
||||
|
||||
showOrderDetailModal: (orderId) => {
|
||||
globalState.selectedOrderId = orderId;
|
||||
globalState.orderDetailModalVisible = true;
|
||||
},
|
||||
|
||||
updateUser: (user) => {
|
||||
globalState.currentUser = user
|
||||
localStorage.setItem('currentUser', JSON.stringify(user))
|
||||
globalState.currentUser = user;
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
},
|
||||
|
||||
logout: () => {
|
||||
globalState.currentUser = null
|
||||
localStorage.removeItem('currentUser')
|
||||
localStorage.removeItem('userToken')
|
||||
ElMessage.success('已退出登录')
|
||||
router.push('/')
|
||||
globalState.currentUser = null;
|
||||
localStorage.removeItem('currentUser');
|
||||
localStorage.removeItem('userToken');
|
||||
ElMessage.success('已退出登录');
|
||||
router.push('/');
|
||||
},
|
||||
|
||||
handleAddToCart: (item) => {
|
||||
const existingItem = globalState.cartItems.find(cartItem =>
|
||||
cartItem.productId === item.productId &&
|
||||
cartItem.color === item.color &&
|
||||
cartItem.size === item.size
|
||||
);
|
||||
|
||||
if (existingItem) {
|
||||
existingItem.quantity += item.quantity;
|
||||
} else {
|
||||
globalState.cartItems.push(item);
|
||||
}
|
||||
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems));
|
||||
ElMessage.success('已加入购物车');
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// 购物车相关方法
|
||||
const handleAddToCart = (item) => {
|
||||
@@ -121,41 +163,41 @@ const handleAddToCart = (item) => {
|
||||
cartItem.productId === item.productId &&
|
||||
cartItem.color === item.color &&
|
||||
cartItem.size === item.size
|
||||
)
|
||||
);
|
||||
|
||||
if (existingItem) {
|
||||
existingItem.quantity += item.quantity
|
||||
existingItem.quantity += item.quantity;
|
||||
} else {
|
||||
globalState.cartItems.push(item)
|
||||
globalState.cartItems.push(item);
|
||||
}
|
||||
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
|
||||
ElMessage.success('已加入购物车')
|
||||
globalState.cartVisible = true
|
||||
}
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems));
|
||||
ElMessage.success('已加入购物车');
|
||||
globalState.cartVisible = true;
|
||||
};
|
||||
|
||||
const handleBuyNow = (amount) => {
|
||||
if (!globalState.currentUser) {
|
||||
ElMessage.warning('请先登录')
|
||||
router.push('/login')
|
||||
return
|
||||
ElMessage.warning('请先登录');
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
globalState.showPaymentModal(amount)
|
||||
}
|
||||
globalState.showPaymentModal(amount);
|
||||
};
|
||||
|
||||
const handleCheckout = () => {
|
||||
if (!globalState.currentUser) {
|
||||
ElMessage.warning('请先登录')
|
||||
router.push('/login')
|
||||
return
|
||||
ElMessage.warning('请先登录');
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
const total = globalState.cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0)
|
||||
globalState.showPaymentModal(total)
|
||||
}
|
||||
const total = globalState.cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
||||
globalState.showPaymentModal(total);
|
||||
};
|
||||
|
||||
const handlePaymentConfirm = (paymentData) => {
|
||||
setTimeout(() => {
|
||||
globalState.orderNumber = 'TB' + Date.now()
|
||||
globalState.orderNumber = 'TB' + Date.now();
|
||||
|
||||
// 创建订单
|
||||
const order = {
|
||||
@@ -167,53 +209,53 @@ const handlePaymentConfirm = (paymentData) => {
|
||||
status: '已支付',
|
||||
createTime: new Date().toISOString(),
|
||||
userId: globalState.currentUser?.id
|
||||
}
|
||||
};
|
||||
|
||||
let orders = JSON.parse(localStorage.getItem('orders') || '[]')
|
||||
orders.unshift(order)
|
||||
localStorage.setItem('orders', JSON.stringify(orders))
|
||||
let orders = JSON.parse(localStorage.getItem('orders') || '[]');
|
||||
orders.unshift(order);
|
||||
localStorage.setItem('orders', JSON.stringify(orders));
|
||||
|
||||
// 清空购物车
|
||||
globalState.cartItems = []
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
|
||||
globalState.cartItems = [];
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems));
|
||||
|
||||
globalState.paymentModalVisible = false
|
||||
globalState.successModalVisible = true
|
||||
globalState.paymentModalVisible = false;
|
||||
globalState.successModalVisible = true;
|
||||
|
||||
ElMessage.success('支付成功!')
|
||||
ElMessage.success('支付成功!');
|
||||
// 新标签页前往 /result/success
|
||||
window.open('/result/success', '_blank')
|
||||
window.open('/result/success', '_blank');
|
||||
|
||||
}, 1000)
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleUpdateQuantity = (index, quantity) => {
|
||||
if (quantity <= 0) {
|
||||
globalState.cartItems.splice(index, 1)
|
||||
globalState.cartItems.splice(index, 1);
|
||||
} else {
|
||||
globalState.cartItems[index].quantity = quantity
|
||||
globalState.cartItems[index].quantity = quantity;
|
||||
}
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
|
||||
}
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems));
|
||||
};
|
||||
|
||||
const handleRemoveItem = (index) => {
|
||||
globalState.cartItems.splice(index, 1)
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
|
||||
}
|
||||
globalState.cartItems.splice(index, 1);
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems));
|
||||
};
|
||||
|
||||
const handleViewOrder = () => {
|
||||
globalState.successModalVisible = false
|
||||
router.push('/orders')
|
||||
}
|
||||
globalState.successModalVisible = false;
|
||||
router.push('/orders');
|
||||
};
|
||||
|
||||
const handleRechargeConfirm = (amount) => {
|
||||
if (globalState.currentUser) {
|
||||
globalState.currentUser.balance = (parseFloat(globalState.currentUser.balance) + amount).toFixed(2)
|
||||
globalState.updateUser(globalState.currentUser)
|
||||
ElMessage.success('充值成功!')
|
||||
globalState.currentUser.balance = (parseFloat(globalState.currentUser.balance) + amount).toFixed(2);
|
||||
globalState.updateUser(globalState.currentUser);
|
||||
ElMessage.success('充值成功!');
|
||||
}
|
||||
globalState.rechargeModalVisible = false
|
||||
}
|
||||
globalState.rechargeModalVisible = false;
|
||||
};
|
||||
|
||||
const handleRegisterConfirm = (userData) => {
|
||||
const newUser = {
|
||||
@@ -224,15 +266,15 @@ const handleRegisterConfirm = (userData) => {
|
||||
balance: 100,
|
||||
joinDate: new Date().toISOString(),
|
||||
level: '新用户'
|
||||
}
|
||||
};
|
||||
|
||||
globalState.updateUser(newUser)
|
||||
globalState.registerModalVisible = false
|
||||
ElMessage.success('注册成功!')
|
||||
}
|
||||
globalState.updateUser(newUser);
|
||||
globalState.registerModalVisible = false;
|
||||
ElMessage.success('注册成功!');
|
||||
};
|
||||
|
||||
// 提供全局状态
|
||||
provide('globalState', globalState)
|
||||
provide('globalState', globalState);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
198
src/components/LogisticsComponent.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="props.visible"
|
||||
title="物流信息"
|
||||
width="60%"
|
||||
:before-close="handleClose"
|
||||
class="logistics-dialog"
|
||||
>
|
||||
<h3 class="text-xl font-semibold text-gray-800 mb-6 flex items-center">
|
||||
<el-icon class="mr-2 text-red-500"><Location /></el-icon>
|
||||
物流信息
|
||||
</h3>
|
||||
|
||||
<div v-if="!loading && logisticsData" class="space-y-6">
|
||||
<!-- 物流基本信息 -->
|
||||
<div class="logistics-base-info grid grid-cols-1 md:grid-cols-4 gap-4 p-5 bg-gradient-to-r from-red-50 to-red-100 rounded-xl shadow-sm">
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<p class="text-xs text-gray-500 mb-1">物流公司</p>
|
||||
<p class="font-medium text-gray-800">{{ logisticsData.company }}</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<p class="text-xs text-gray-500 mb-1">物流单号</p>
|
||||
<p class="font-medium font-mono text-gray-800">{{ logisticsData.trackingNumber }}</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<p class="text-xs text-gray-500 mb-1">预计送达</p>
|
||||
<p class="font-medium text-gray-800">{{ logisticsData.estimatedDelivery }}</p>
|
||||
</div>
|
||||
<div class="bg-white p-4 rounded-lg">
|
||||
<p class="text-xs text-gray-500 mb-1">当前状态</p>
|
||||
<p class="font-medium text-red-600">运输中</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 物流状态追踪 -->
|
||||
<div class="logistics-tracking">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||
<el-icon class="mr-2 text-red-500"><Clock /></el-icon>
|
||||
物流追踪
|
||||
</h3>
|
||||
|
||||
<div class="relative pl-8 ml-2">
|
||||
<div class="absolute left-0 top-0 bottom-0 w-0.5 bg-gray-200 ml-1"></div>
|
||||
|
||||
<div
|
||||
v-for="(item, index) in logisticsData.trackingInfo"
|
||||
:key="index"
|
||||
class="relative mb-8"
|
||||
:class="{'animate__animated animate__fadeInLeft': !loading}"
|
||||
:style="`animation-delay: ${index * 0.1}s;`"
|
||||
>
|
||||
<div class="timeline-dot bg-red-500"></div>
|
||||
<div
|
||||
class="card bg-white border border-gray-100 rounded-xl p-4 shadow-sm transition-all duration-300 hover:shadow-md hover:border-red-200"
|
||||
:class="{'border-red-200': index === 0}"
|
||||
>
|
||||
<div class="flex justify-between items-start">
|
||||
<p class="font-semibold text-gray-800">{{ item.status }}</p>
|
||||
<span v-if="index === 0" class="text-xs bg-red-100 text-red-800 px-2 py-1 rounded-full">最新</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600 mt-1">{{ item.location }}</p>
|
||||
<p class="text-xs text-gray-500 mt-2">{{ item.time }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 物流地图 -->
|
||||
<div class="mt-8">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||
<el-icon class="mr-2 text-red-500"><MapLocation /></el-icon>
|
||||
物流路径
|
||||
</h3>
|
||||
<div class="bg-gray-100 rounded-xl p-4 h-48 flex items-center justify-center">
|
||||
<div class="text-center">
|
||||
<div class="w-16 h-16 mx-auto bg-red-100 rounded-full flex items-center justify-center mb-3">
|
||||
<!-- <el-icon class="text-red-600 text-2xl"><Truck /></el-icon>-->
|
||||
</div>
|
||||
<p class="text-gray-600">从 <span class="font-medium text-red-600">杭州</span> 发往 <span class="font-medium text-red-600">上海</span></p>
|
||||
<p class="text-sm text-gray-500 mt-2">预计明天下午14:00前送达</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="logisticsData && logisticsData.trackingInfo.length === 0" class="text-center py-12">
|
||||
<div class="text-5xl text-red-100 mb-4">🚚</div>
|
||||
<p class="text-gray-500 text-lg">暂无物流信息,请稍后再查看</p>
|
||||
<el-button type="danger" class="mt-4" @click="handleClose">关闭</el-button>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, defineProps, defineEmits } from 'vue';
|
||||
import { Location, Clock, MapLocation } from '@element-plus/icons-vue';
|
||||
import 'animate';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
orderId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:visible']);
|
||||
|
||||
const loading = ref(true);
|
||||
const logisticsData = ref<any>(null);
|
||||
|
||||
const handleClose = () => {
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 模拟API请求获取物流信息
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
|
||||
// 模拟物流数据
|
||||
logisticsData.value = {
|
||||
company: '顺丰速运',
|
||||
trackingNumber: 'SF1234567890123',
|
||||
estimatedDelivery: '2025-06-05',
|
||||
trackingInfo: [
|
||||
{
|
||||
time: '2025-06-01 10:30',
|
||||
status: '已签收',
|
||||
location: '上海市浦东新区张江高科技园区'
|
||||
},
|
||||
{
|
||||
time: '2025-06-01 08:15',
|
||||
status: '正在派送途中',
|
||||
location: '上海市浦东新区张江营业部'
|
||||
},
|
||||
{
|
||||
time: '2025-06-01 06:30',
|
||||
status: '到达上海浦东集散中心',
|
||||
location: '上海市浦东新区'
|
||||
},
|
||||
{
|
||||
time: '2025-05-31 22:45',
|
||||
status: '离开杭州集散中心,发往上海',
|
||||
location: '浙江省杭州市'
|
||||
},
|
||||
{
|
||||
time: '2025-05-31 18:30',
|
||||
status: '已揽收',
|
||||
location: '浙江省杭州市余杭区'
|
||||
}
|
||||
]
|
||||
};
|
||||
console.log('Logistics data loaded:', logisticsData.value);
|
||||
}, 800);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.logistics-dialog .el-dialog__header {
|
||||
background: linear-gradient(to right, #fef2f2, #fee2e2);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.logistics-dialog .el-dialog__title {
|
||||
color: #e53e3e;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.logistics-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: 8px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
</style>
|
||||
268
src/components/OrderDetailComponent.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="props.visible"
|
||||
title="订单详情"
|
||||
width="60%"
|
||||
:before-close="handleClose"
|
||||
class="order-detail-dialog"
|
||||
>
|
||||
<div v-if="order" class="order-detail-container bg-white rounded-2xl shadow-xl overflow-hidden transition-all duration-300 hover:shadow-2xl">
|
||||
<!-- 订单头部信息 -->
|
||||
<div class="order-header bg-gradient-to-r from-red-50 to-red-100 px-6 py-5 border-b border-red-100">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between">
|
||||
<div class="flex flex-col md:flex-row items-start md:items-center space-x-0 md:space-x-6 space-y-3 md:space-y-0 mb-3 md:mb-0">
|
||||
<div class="bg-white rounded-lg px-3 py-2 shadow-sm">
|
||||
<span class="text-xs text-gray-500">订单号:</span>
|
||||
<span class="font-mono text-sm font-medium text-gray-800">{{ order.orderNumber }}</span>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg px-3 py-2 shadow-sm">
|
||||
<span class="text-xs text-gray-500">下单时间:</span>
|
||||
<span class="text-sm text-gray-800">{{ formatDate(order.createTime) }}</span>
|
||||
</div>
|
||||
<div class="bg-white rounded-lg px-3 py-2 shadow-sm">
|
||||
<span class="text-xs text-gray-500">支付方式:</span>
|
||||
<span class="text-sm text-gray-800">{{ order.paymentMethod || '支付宝' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<el-tag
|
||||
:type="getStatusType(order.status)"
|
||||
size="large"
|
||||
class="px-5 py-1.5 font-bold rounded-full shadow-md transform transition-transform duration-300 hover:scale-105"
|
||||
effect="dark"
|
||||
>
|
||||
{{ order.status }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 收货地址信息 -->
|
||||
<div class="address-info p-6 border-b border-gray-100">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||
<el-icon class="mr-2 text-red-500"><User /></el-icon>
|
||||
收货信息
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="bg-red-50 rounded-xl p-4">
|
||||
<p class="text-xs text-gray-500 mb-1">收货人</p>
|
||||
<p class="font-medium text-gray-800">{{ order.shippingAddress.name }}</p>
|
||||
</div>
|
||||
<div class="bg-red-50 rounded-xl p-4">
|
||||
<p class="text-xs text-gray-500 mb-1">联系电话</p>
|
||||
<p class="font-medium text-gray-800">{{ order.shippingAddress.phone }}</p>
|
||||
</div>
|
||||
<div class="bg-red-50 rounded-xl p-4 md:col-span-1">
|
||||
<p class="text-xs text-gray-500 mb-1">收货地址</p>
|
||||
<p class="font-medium text-gray-800">
|
||||
{{ order.shippingAddress.province }}
|
||||
{{ order.shippingAddress.city }}
|
||||
{{ order.shippingAddress.district }}
|
||||
{{ order.shippingAddress.detail }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单商品 -->
|
||||
<div class="order-content p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
|
||||
<el-icon class="mr-2 text-red-500"><ShoppingBag /></el-icon>
|
||||
商品信息
|
||||
</h3>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="(item, index) in order.items"
|
||||
:key="`${item.productId}-${item.color}-${item.size}`"
|
||||
class="product-item flex space-x-4 p-4 rounded-xl border border-gray-100 hover:border-red-200 transition-all duration-300 hover:shadow-md bg-white"
|
||||
:class="{'animate__animated animate__fadeInUp': !loading}"
|
||||
:style="`animation-delay: ${index * 0.05}s;`"
|
||||
>
|
||||
<!-- 商品图片 -->
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:src="item.cover"
|
||||
:alt="item.title"
|
||||
class="w-24 h-24 object-cover rounded-xl cursor-pointer transition-transform duration-300 hover:scale-105 shadow-md"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4
|
||||
class="font-medium text-gray-800 line-clamp-2 mb-2 cursor-pointer hover:text-red-600 transition-colors duration-300"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h4>
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm text-gray-500 mb-2">
|
||||
<span class="px-2 py-1 bg-gray-100 rounded-full text-xs">{{ item.color }}</span>
|
||||
<span class="px-2 py-1 bg-gray-100 rounded-full text-xs">{{ item.size }}</span>
|
||||
<span class="ml-1">数量:{{ item.quantity }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-lg font-bold text-red-600">¥{{ item.price }}</span>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
class="ml-2"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
查看商品
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单总计 -->
|
||||
<div class="order-summary mt-8 pt-6 border-t border-gray-200 bg-gradient-to-r from-red-50 to-red-100 rounded-xl p-5">
|
||||
<div class="space-y-3">
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">商品总价</span>
|
||||
<span class="font-medium">¥{{ order.subtotal }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">运费</span>
|
||||
<span class="font-medium text-green-600">免运费</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span class="text-gray-600">优惠券</span>
|
||||
<span class="font-medium text-green-600">-¥{{ order.discount || 0 }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between pt-3 border-t border-gray-200 mt-3">
|
||||
<span class="text-xl font-bold text-gray-800">应付总额</span>
|
||||
<span class="text-2xl font-bold text-red-600">¥{{ order.totalAmount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps, defineEmits, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { User, ShoppingBag } from '@element-plus/icons-vue';
|
||||
import 'animate';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
orderId: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:visible']);
|
||||
|
||||
const router = useRouter();
|
||||
const order = ref<any>(null);
|
||||
const loading = ref(true);
|
||||
|
||||
const handleClose = () => {
|
||||
emits('update:visible', false);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 模拟API请求获取订单详情
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
|
||||
// 模拟订单数据
|
||||
order.value = {
|
||||
id: props.orderId,
|
||||
orderNumber: 'TB1234567890123',
|
||||
createTime: new Date().toISOString(),
|
||||
status: '已发货',
|
||||
paymentMethod: '支付宝',
|
||||
subtotal: 299.00,
|
||||
discount: 30.00,
|
||||
totalAmount: 269.00,
|
||||
shippingAddress: {
|
||||
name: '张三',
|
||||
phone: '138****1234',
|
||||
province: '上海市',
|
||||
city: '上海市',
|
||||
district: '浦东新区',
|
||||
detail: '张江高科技园区博云路2号浦软大厦12层'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
productId: 1001,
|
||||
title: '高品质时尚运动鞋 - 透气网面设计',
|
||||
price: 199.00,
|
||||
cover: 'https://cdn.seovx.com/ha/?mom=302&w=400&h=400&_=1',
|
||||
color: '白色',
|
||||
size: '42',
|
||||
quantity: 1
|
||||
},
|
||||
{
|
||||
productId: 1002,
|
||||
title: '专业运动背包 - 大容量多隔层',
|
||||
price: 100.00,
|
||||
cover: 'https://cdn.seovx.com/ha/?mom=302&w=400&h=400&_=2',
|
||||
color: '黑色',
|
||||
size: '均码',
|
||||
quantity: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
console.log('Order data loaded:', order.value);
|
||||
}, 800);
|
||||
});
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('zh-CN');
|
||||
};
|
||||
|
||||
const getStatusType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'待支付': 'warning',
|
||||
'已支付': 'primary',
|
||||
'已发货': 'success',
|
||||
'已完成': 'success',
|
||||
'已取消': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
const viewProduct = (item: any) => {
|
||||
router.push(`/product/${item.productId}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.order-detail-dialog .el-dialog__header {
|
||||
background: linear-gradient(to right, #fef2f2, #fee2e2);
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.order-detail-dialog .el-dialog__title {
|
||||
color: #e53e3e;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.order-detail-dialog .el-dialog__headerbtn .el-dialog__close {
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.product-item:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
</style>
|
||||
@@ -19,24 +19,26 @@
|
||||
<div class="payment-methods mb-6">
|
||||
<h4 class="font-semibold mb-3">选择支付方式</h4>
|
||||
<el-radio-group v-model="selectedMethod" class="w-full">
|
||||
<div class="space-y-3">
|
||||
<div class="space-y-3 w-full">
|
||||
<el-radio
|
||||
v-for="method in paymentMethods"
|
||||
:key="method.id"
|
||||
:label="method.id"
|
||||
class="payment-method-option"
|
||||
class="payment-method-option w-full"
|
||||
>
|
||||
<div class="flex items-center justify-between w-full">
|
||||
<div class="flex items-center justify-between w-full relative">
|
||||
<div class="flex items-center">
|
||||
<span class="text-2xl mr-3">{{ method.icon }}</span>
|
||||
<div>
|
||||
<div class="font-medium">{{ method.name }}</div>
|
||||
<div class="font-medium">
|
||||
{{ method.name }}
|
||||
<span v-if="method.discount" class="text-red-600 text-sm font-bold ml-10">
|
||||
{{ method.discount }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">{{ method.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="method.discount" class="text-red-600 text-sm">
|
||||
{{ method.discount }}
|
||||
</div>
|
||||
</div>
|
||||
</el-radio>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,177 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="账户充值"
|
||||
width="500px"
|
||||
:before-close="handleClose"
|
||||
>
|
||||
<div class="recharge-content">
|
||||
<!-- 快速充值金额 -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-3">
|
||||
选择充值金额
|
||||
</label>
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div
|
||||
v-for="amount in quickAmounts"
|
||||
:key="amount"
|
||||
class="amount-option"
|
||||
:class="{ selected: form.amount === amount }"
|
||||
@click="form.amount = amount"
|
||||
>
|
||||
¥{{ amount }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 自定义金额 -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">
|
||||
自定义金额
|
||||
</label>
|
||||
<el-input
|
||||
v-model.number="form.amount"
|
||||
type="number"
|
||||
placeholder="请输入充值金额"
|
||||
:min="1"
|
||||
:max="10000"
|
||||
>
|
||||
<template #prepend>¥</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 支付方式 -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-3">
|
||||
支付方式
|
||||
</label>
|
||||
<el-radio-group v-model="form.paymentMethod" class="w-full">
|
||||
<div class="space-y-2">
|
||||
<el-radio
|
||||
v-for="method in paymentMethods"
|
||||
:key="method.id"
|
||||
:label="method.id"
|
||||
class="payment-method-radio"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<span class="text-2xl mr-3">{{ method.icon }}</span>
|
||||
<span>{{ method.name }}</span>
|
||||
</div>
|
||||
</el-radio>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex space-x-3">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
:disabled="!form.amount || !form.paymentMethod"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
确认充值 ¥{{ form.amount || 0 }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<script>
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
export default {
|
||||
name: 'RechargeModal',
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['update:visible', 'confirm'],
|
||||
setup(props, { emit }) {
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value)
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const form = reactive({
|
||||
amount: '',
|
||||
paymentMethod: ''
|
||||
})
|
||||
|
||||
const quickAmounts = [50, 100, 200, 500, 1000, 2000]
|
||||
|
||||
const paymentMethods = [
|
||||
{ id: 'alipay', name: '支付宝', icon: '💰' },
|
||||
{ id: 'wechat', name: '微信支付', icon: '💚' },
|
||||
{ id: 'unionpay', name: '银联支付', icon: '💳' }
|
||||
]
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
form.amount = ''
|
||||
form.paymentMethod = ''
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
if (!form.amount || !form.paymentMethod) {
|
||||
ElMessage.warning('请选择充值金额和支付方式')
|
||||
return
|
||||
}
|
||||
|
||||
if (form.amount < 1 || form.amount > 10000) {
|
||||
ElMessage.warning('充值金额必须在1-10000之间')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 模拟充值处理
|
||||
await new Promise(resolve => setTimeout(resolve, 1500))
|
||||
|
||||
emit('confirm', parseFloat(form.amount))
|
||||
|
||||
loading.value = false
|
||||
handleClose()
|
||||
}
|
||||
|
||||
return {
|
||||
dialogVisible,
|
||||
loading,
|
||||
form,
|
||||
quickAmounts,
|
||||
paymentMethods,
|
||||
handleClose,
|
||||
handleConfirm
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.amount-option {
|
||||
@apply p-3 border-2 border-gray-200 rounded-lg text-center cursor-pointer transition-all;
|
||||
}
|
||||
|
||||
.amount-option:hover {
|
||||
@apply border-red-300;
|
||||
}
|
||||
|
||||
.amount-option.selected {
|
||||
@apply border-red-500 bg-red-50 text-red-600;
|
||||
}
|
||||
|
||||
.payment-method-radio {
|
||||
@apply w-full p-3 border border-gray-200 rounded-lg;
|
||||
}
|
||||
|
||||
.payment-method-radio:hover {
|
||||
@apply border-red-300 bg-red-50;
|
||||
}
|
||||
</style>
|
||||
@@ -185,11 +185,11 @@ const globalState = inject('globalState')
|
||||
|
||||
const searchKeyword = ref('')
|
||||
const showMobileMenu = ref(false)
|
||||
// const cartCount = 0;
|
||||
|
||||
// const cartCount = computed(() => {
|
||||
// return globalState.cartItems.reduce((total, item) => total + item.quantity, 0)
|
||||
// })
|
||||
const cartCount = 1;
|
||||
const cartCount = computed(() => {
|
||||
return globalState.cartItems.reduce((total, item) => total + item.quantity, 0)
|
||||
})
|
||||
const mainNav = [
|
||||
{ name: '首页', link: '/' },
|
||||
{ name: '天猫超市', link: '/products?category=supermarket' },
|
||||
|
||||
@@ -1,247 +1,242 @@
|
||||
<template>
|
||||
<div class="order-list-page bg-gray-50 min-h-screen">
|
||||
|
||||
|
||||
<main class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- 页面标题 -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 mb-2">我的订单</h1>
|
||||
<p class="text-gray-600">管理您的所有订单信息</p>
|
||||
</div>
|
||||
<!-- 页面标题 -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 mb-2">我的订单</h1>
|
||||
<p class="text-gray-600">管理您的所有订单信息</p>
|
||||
</div>
|
||||
|
||||
<!-- 订单统计卡片 -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
|
||||
<div
|
||||
v-for="stat in orderStats"
|
||||
:key="stat.status"
|
||||
class="bg-white rounded-xl p-6 shadow-sm hover:shadow-md transition-shadow cursor-pointer"
|
||||
:class="{ 'ring-2 ring-red-500 bg-red-50': activeTab === stat.status }"
|
||||
@click="activeTab = stat.status"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl mb-2">{{ stat.icon }}</div>
|
||||
<div class="text-2xl font-bold text-gray-800 mb-1">{{ stat.count }}</div>
|
||||
<div class="text-sm text-gray-600">{{ stat.label }}</div>
|
||||
</div>
|
||||
<!-- 订单统计卡片 -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
|
||||
<div
|
||||
v-for="stat in orderStats"
|
||||
:key="stat.status"
|
||||
class="bg-white rounded-xl p-6 shadow-sm hover:shadow-md transition-all cursor-pointer transform hover:-translate-y-1"
|
||||
:class="{ 'ring-2 ring-red-500 bg-red-50': activeTab === stat.status }"
|
||||
@click="activeTab = stat.status"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl mb-2 text-gray-400">{{ stat.icon }}</div>
|
||||
<div class="text-2xl font-bold text-gray-800 mb-1">{{ stat.count }}</div>
|
||||
<div class="text-sm text-gray-600">{{ stat.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单筛选标签 -->
|
||||
<div class="bg-white rounded-xl shadow-sm mb-6">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="order-tabs">
|
||||
<el-tab-pane
|
||||
v-for="tab in orderTabs"
|
||||
:key="tab.value"
|
||||
:label="tab.label"
|
||||
:name="tab.value"
|
||||
/>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<div v-loading="loading" class="order-list">
|
||||
<!-- 空状态 -->
|
||||
<div v-if="filteredOrders.length === 0" class="empty-orders bg-white rounded-xl p-12 text-center shadow-sm">
|
||||
<div class="w-32 h-32 mx-auto mb-6 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<div class="text-6xl">📋</div>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-800 mb-2">暂无相关订单</h3>
|
||||
<p class="text-gray-500 mb-6">您还没有{{ activeTab === 'all' ? '' : activeTab }}订单</p>
|
||||
<el-button type="primary" size="large" @click="$router.push('/')">
|
||||
立即购物
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 订单卡片列表 -->
|
||||
<div v-else class="space-y-6">
|
||||
<div
|
||||
v-for="order in filteredOrders"
|
||||
:key="order.id"
|
||||
class="order-card bg-white rounded-xl shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden"
|
||||
>
|
||||
<!-- 订单头部 -->
|
||||
<div class="order-header bg-gray-50 px-6 py-4 border-b">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-6">
|
||||
<div>
|
||||
<span class="text-sm text-gray-500">订单号:</span>
|
||||
<span class="font-mono text-sm font-medium">{{ order.orderNumber }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-sm text-gray-500">下单时间:</span>
|
||||
<span class="text-sm">{{ formatDate(order.createTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<el-tag
|
||||
:type="getStatusType(order.status)"
|
||||
size="large"
|
||||
class="px-4 py-1 font-medium"
|
||||
>
|
||||
{{ order.status }}
|
||||
</el-tag>
|
||||
<el-dropdown @command="(command) => handleOrderAction(command, order)">
|
||||
<el-button text class="text-gray-600 hover:text-red-600">
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="detail">
|
||||
<el-icon><View /></el-icon>查看详情
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已支付'" command="logistics">
|
||||
<el-icon><View /></el-icon>查看物流
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已完成'" command="review">
|
||||
<el-icon><Star /></el-icon>评价商品
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已完成'" command="repurchase">
|
||||
<el-icon><RefreshRight /></el-icon>再次购买
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status !== '已取消'" command="cancel" divided>
|
||||
<el-icon><Close /></el-icon>取消订单
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单商品 -->
|
||||
<div class="order-content p-6">
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="item in order.items"
|
||||
:key="`${item.productId}-${item.color}-${item.size}`"
|
||||
class="product-item flex space-x-4 p-4 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<!-- 商品图片 -->
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:src="item.cover"
|
||||
:alt="item.title"
|
||||
class="w-20 h-20 object-cover rounded-lg cursor-pointer hover:scale-105 transition-transform"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4
|
||||
class="font-medium text-gray-800 line-clamp-2 mb-2 cursor-pointer hover:text-red-600 transition-colors"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h4>
|
||||
<div class="flex items-center space-x-4 text-sm text-gray-500 mb-2">
|
||||
<span class="px-2 py-1 bg-gray-100 rounded">{{ item.color }}</span>
|
||||
<span class="px-2 py-1 bg-gray-100 rounded">{{ item.size }}</span>
|
||||
<span>数量:{{ item.quantity }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-lg font-bold text-red-600">¥{{ item.price }}</span>
|
||||
<div class="flex space-x-2">
|
||||
<el-button
|
||||
v-if="order.status === '已完成'"
|
||||
size="small"
|
||||
@click="reviewProduct(item)"
|
||||
>
|
||||
评价
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
@click="buyAgain(item)"
|
||||
>
|
||||
再次购买
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单总计 -->
|
||||
<div class="order-summary mt-6 pt-6 border-t bg-gray-50 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-600">
|
||||
共{{ order.items.reduce((sum, item) => sum + item.quantity, 0) }}件商品
|
||||
<span class="mx-2">|</span>
|
||||
运费:<span class="text-green-600">免运费</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm text-gray-500 mb-1">应付总额</div>
|
||||
<div class="text-2xl font-bold text-red-600">¥{{ order.totalAmount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单操作按钮 -->
|
||||
<div class="order-actions mt-6 flex justify-end space-x-3">
|
||||
<el-button
|
||||
v-if="order.status === '待支付'"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="payOrder(order)"
|
||||
>
|
||||
立即支付
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '已发货'"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="confirmReceive(order)"
|
||||
>
|
||||
确认收货
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '已完成'"
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="reviewOrder(order)"
|
||||
>
|
||||
评价商品
|
||||
</el-button>
|
||||
<el-button
|
||||
size="large"
|
||||
@click="viewOrderDetail(order)"
|
||||
>
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '待支付'"
|
||||
size="large"
|
||||
@click="cancelOrder(order)"
|
||||
>
|
||||
取消订单
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="filteredOrders.length > 0" class="mt-8 flex justify-center">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:total="totalOrders"
|
||||
layout="prev, pager, next, jumper"
|
||||
background
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<div v-loading="loading" class="order-list">
|
||||
<!-- 空状态 -->
|
||||
<div v-if="filteredOrders.length === 0" class="empty-orders bg-white rounded-2xl p-12 text-center shadow-md">
|
||||
<div class="w-32 h-32 mx-auto mb-6 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<div class="text-6xl">📋</div>
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold text-gray-800 mb-2">暂无相关订单</h3>
|
||||
<p class="text-gray-500 mb-6">您还没有{{ activeTab === 'all' ? '' : activeTab }}订单</p>
|
||||
<el-button type="primary" size="large" @click="$router.push('/')">
|
||||
立即购物
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 订单卡片列表 -->
|
||||
<div v-else class="space-y-6">
|
||||
<div
|
||||
v-for="order in filteredOrders"
|
||||
:key="order.id"
|
||||
class="order-card bg-white rounded-2xl shadow-sm hover:shadow-lg transition-all duration-300 overflow-hidden border border-gray-100"
|
||||
>
|
||||
<!-- 订单头部 -->
|
||||
<div class="order-header bg-gray-50 px-6 py-4 border-b border-gray-200">
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between">
|
||||
<div class="flex flex-col md:flex-row items-start md:items-center space-x-6 mb-3 md:mb-0">
|
||||
<div>
|
||||
<span class="text-sm text-gray-500">订单号:</span>
|
||||
<span class="font-mono text-sm font-medium">{{ order.orderNumber }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-sm text-gray-500">下单时间:</span>
|
||||
<span class="text-sm">{{ formatDate(order.createTime) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
<el-tag
|
||||
:type="getStatusType(order.status)"
|
||||
size="large"
|
||||
class="px-4 py-1 font-medium rounded-full"
|
||||
>
|
||||
{{ order.status }}
|
||||
</el-tag>
|
||||
<el-dropdown @command="(command) => handleOrderAction(command, order)">
|
||||
<el-button text class="text-gray-600 hover:text-red-600">
|
||||
<el-icon><MoreFilled /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="detail">
|
||||
<el-icon><View /></el-icon>查看详情
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已支付' || order.status === '已发货'" command="logistics">
|
||||
<el-icon><View /></el-icon>查看物流
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已完成'" command="review">
|
||||
<el-icon><Star /></el-icon>评价商品
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status === '已完成'" command="repurchase">
|
||||
<el-icon><RefreshRight /></el-icon>再次购买
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="order.status !== '已取消'" command="cancel" divided>
|
||||
<el-icon><Close /></el-icon>取消订单
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单商品 -->
|
||||
<div class="order-content p-6">
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="item in order.items"
|
||||
:key="`${item.productId}-${item.color}-${item.size}`"
|
||||
class="product-item flex space-x-4 p-4 rounded-xl hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<!-- 商品图片 -->
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
:src="item.cover"
|
||||
:alt="item.title"
|
||||
class="w-24 h-24 object-cover rounded-xl cursor-pointer hover:scale-105 transition-transform"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 商品信息 -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<h4
|
||||
class="font-medium text-gray-800 line-clamp-2 mb-2 cursor-pointer hover:text-red-600 transition-colors"
|
||||
@click="viewProduct(item)"
|
||||
>
|
||||
{{ item.title }}
|
||||
</h4>
|
||||
<div class="flex flex-wrap items-center space-x-2 text-sm text-gray-500 mb-2">
|
||||
<span class="px-2 py-1 bg-gray-100 rounded-full">{{ item.color }}</span>
|
||||
<span class="px-2 py-1 bg-gray-100 rounded-full">{{ item.size }}</span>
|
||||
<span>数量:{{ item.quantity }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-lg font-bold text-red-600">¥{{ item.price }}</span>
|
||||
<div class="flex space-x-2">
|
||||
<el-button
|
||||
v-if="order.status === '已完成'"
|
||||
size="small"
|
||||
@click="reviewProduct(item)"
|
||||
>
|
||||
评价
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
@click="buyAgain(item)"
|
||||
>
|
||||
再次购买
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单总计 -->
|
||||
<div class="order-summary mt-6 pt-6 border-t border-gray-200 bg-gray-50 rounded-xl p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-600">
|
||||
共{{ order.items.reduce((sum, item) => sum + item.quantity, 0) }}件商品
|
||||
<span class="mx-2">|</span>
|
||||
运费:<span class="text-green-600">免运费</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm text-gray-500 mb-1">应付总额</div>
|
||||
<div class="text-2xl font-bold text-red-600">¥{{ order.totalAmount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 订单操作按钮 - 底部 -->
|
||||
<div class="order-actions p-6 pt-0 flex justify-end space-x-3">
|
||||
<el-button
|
||||
v-if="order.status === '待支付'"
|
||||
type="primary"
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="payOrder(order)"
|
||||
>
|
||||
立即支付
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '已发货'"
|
||||
type="primary"
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="confirmReceive(order)"
|
||||
>
|
||||
确认收货
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '已完成'"
|
||||
type="primary"
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="reviewOrder(order)"
|
||||
>
|
||||
评价商品
|
||||
</el-button>
|
||||
<el-button
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="viewOrderDetail(order)"
|
||||
>
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '已支付' || order.status === '已发货'"
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="viewLogistics(order)"
|
||||
>
|
||||
查看物流
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="order.status === '待支付'"
|
||||
size="large"
|
||||
class="rounded-full px-6"
|
||||
@click="cancelOrder(order)"
|
||||
>
|
||||
取消订单
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="filteredOrders.length > 0" class="mt-10 flex justify-center">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:total="totalOrders"
|
||||
layout="prev, pager, next, jumper"
|
||||
background
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, inject } from 'vue'
|
||||
import { ref, computed, onMounted, inject, watch, defineEmits } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
MoreFilled,
|
||||
@@ -252,9 +247,9 @@ import {
|
||||
} from '@element-plus/icons-vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
|
||||
const router = useRouter()
|
||||
const globalState = inject('globalState')
|
||||
const emits = defineEmits(['open-logistics-modal', 'open-order-detail-modal'])
|
||||
|
||||
const loading = ref(false)
|
||||
const activeTab = ref('all')
|
||||
@@ -262,15 +257,6 @@ const currentPage = ref(1)
|
||||
const pageSize = ref(5)
|
||||
const orders = ref([])
|
||||
|
||||
const orderTabs = [
|
||||
{ label: '全部订单', value: 'all' },
|
||||
{ label: '待支付', value: '待支付' },
|
||||
{ label: '已支付', value: '已支付' },
|
||||
{ label: '已发货', value: '已发货' },
|
||||
{ label: '已完成', value: '已完成' },
|
||||
{ label: '已取消', value: '已取消' }
|
||||
]
|
||||
|
||||
// 订单统计
|
||||
const stats = ref([
|
||||
{ status: 'all', label: '全部', icon: '📋', count: 0 },
|
||||
@@ -281,15 +267,22 @@ const stats = ref([
|
||||
])
|
||||
|
||||
const orderStats = computed(() => {
|
||||
// Reset counts
|
||||
stats.value.forEach(s => s.count = 0);
|
||||
// 创建一个副本,避免直接修改原始数据
|
||||
const updatedStats = JSON.parse(JSON.stringify(stats.value))
|
||||
|
||||
// 重置计数
|
||||
updatedStats.forEach(s => s.count = 0)
|
||||
|
||||
// 统计各类订单数量
|
||||
orders.value.forEach(order => {
|
||||
const stat = stats.value.find(s => s.status === order.status)
|
||||
const stat = updatedStats.find(s => s.status === order.status)
|
||||
if (stat) stat.count++
|
||||
|
||||
// 更新全部订单数量
|
||||
updatedStats[0].count++
|
||||
})
|
||||
|
||||
return stats.value
|
||||
return updatedStats
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -421,28 +414,16 @@ const buyAgain = (item) => {
|
||||
quantity: 1
|
||||
}
|
||||
|
||||
const existingItem = globalState.cartItems.find(cartItem =>
|
||||
cartItem.productId === item.productId &&
|
||||
cartItem.color === item.color &&
|
||||
cartItem.size === item.size
|
||||
)
|
||||
globalState.handleAddToCart(cartItem);
|
||||
|
||||
if (existingItem) {
|
||||
existingItem.quantity += 1
|
||||
} else {
|
||||
globalState.cartItems.push(cartItem)
|
||||
}
|
||||
|
||||
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
|
||||
ElMessage.success('商品已加入购物车')
|
||||
}
|
||||
|
||||
const viewOrderDetail = (order) => {
|
||||
ElMessage.info('订单详情页面开发中...')
|
||||
globalState.showOrderDetailModal(order.id)
|
||||
}
|
||||
|
||||
const viewLogistics = (order) => {
|
||||
ElMessage.info('物流查询功能开发中...')
|
||||
globalState.showLogisticsModal(order.id)
|
||||
}
|
||||
|
||||
const repurchaseOrder = (order) => {
|
||||
@@ -556,6 +537,30 @@ const generateSampleOrders = () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 自定义工具类 */
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.transition-all-300 {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hover-lift {
|
||||
@apply hover:-translate-y-1 transition-all duration-300;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.card-shadow-hover {
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
/* 订单标签样式 */
|
||||
.order-tabs :deep(.el-tabs__header) {
|
||||
margin: 0;
|
||||
padding: 0 24px;
|
||||
@@ -565,24 +570,78 @@ const generateSampleOrders = () => {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
/* 自定义Element Plus标签样式 */
|
||||
::v-deep .el-tabs__item {
|
||||
@apply rounded-full px-6 py-2 text-gray-600 hover:text-red-600 transition-all duration-300;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
@apply bg-red-50 text-red-600 font-medium;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__active-bar {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
/* 订单卡片样式 */
|
||||
.order-card {
|
||||
border: 1px solid #f0f0f0;
|
||||
@apply border border-gray-100 rounded-2xl overflow-hidden transition-all duration-300 hover:shadow-lg;
|
||||
}
|
||||
|
||||
.order-card:hover {
|
||||
border-color: #ff4d4f;
|
||||
@apply border-red-100;
|
||||
}
|
||||
|
||||
.product-item:hover {
|
||||
transform: translateY(-1px);
|
||||
/* 商品项样式 */
|
||||
.product-item {
|
||||
@apply transition-all duration-300 hover:bg-gray-50 rounded-xl;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
.empty-orders {
|
||||
border: 2px dashed #e5e7eb;
|
||||
@apply border-2 border-dashed border-gray-200 rounded-3xl;
|
||||
}
|
||||
|
||||
/* 标签样式 */
|
||||
:deep(.el-tag--large) {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
@apply rounded-full px-4 py-1.5 text-sm font-medium;
|
||||
}
|
||||
</style>
|
||||
|
||||
/* 按钮样式 */
|
||||
:deep(.el-button) {
|
||||
@apply rounded-full transition-all duration-300;
|
||||
}
|
||||
|
||||
:deep(.el-button--primary) {
|
||||
@apply bg-red-600 hover:bg-red-700 text-white;
|
||||
}
|
||||
|
||||
:deep(.el-button--text) {
|
||||
@apply hover:text-red-600;
|
||||
}
|
||||
|
||||
/* 分页样式 */
|
||||
:deep(.el-pagination) {
|
||||
@apply rounded-xl overflow-hidden;
|
||||
}
|
||||
|
||||
:deep(.el-pager li) {
|
||||
@apply min-w-10 h-10 leading-10 rounded-lg transition-all duration-300 hover:bg-gray-100;
|
||||
}
|
||||
|
||||
:deep(.el-pager li.active) {
|
||||
@apply bg-red-600 text-white hover:bg-red-700;
|
||||
}
|
||||
|
||||
:deep(.el-pagination__editor) {
|
||||
@apply rounded-lg;
|
||||
}
|
||||
|
||||
:deep(.el-pagination__editor:focus) {
|
||||
@apply border-red-500;
|
||||
}
|
||||
|
||||
:deep(.el-pagination__sizes .el-input .el-input__inner) {
|
||||
@apply rounded-lg;
|
||||
}
|
||||
</style>
|
||||
@@ -481,14 +481,16 @@
|
||||
v-show="showMagnifier"
|
||||
class="magnifier-viewer fixed border border-gray-300 rounded-lg overflow-hidden bg-white shadow-xl z-50"
|
||||
:style="{
|
||||
left: magnifierLeft + 'px',
|
||||
top: '200px',
|
||||
width: '450px',
|
||||
height: '450px',
|
||||
backgroundImage: 'url(' + currentImage + ')',
|
||||
backgroundSize: zoomLevel + '%',
|
||||
backgroundPosition: backgroundPosition
|
||||
}"
|
||||
// left: magnifierLeft + 'px',
|
||||
// left: magnifierTop + 'px',
|
||||
left: magnifierLeft>0? `${magnifierLeft}px`: '45%',
|
||||
top: '200px',
|
||||
width: '450px',
|
||||
height: '450px',
|
||||
backgroundImage: 'url(' + currentImage + ')',
|
||||
backgroundSize: zoomLevel + '%',
|
||||
backgroundPosition: backgroundPosition
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||