fix: 优化订单列表、新增物流,订单详情(半成品)

This commit is contained in:
2025-05-29 16:47:02 +08:00
parent fb63d830f0
commit dc91dccd53
22 changed files with 1150 additions and 398 deletions

View File

@@ -10,32 +10,3 @@
- Tailwind Css
- Element Plus
# 运行截图
## 首页
![img.png](public/md/index.png)
## 商品模态框
![img.png](public/md/product-modal.png)
## 商品详情
![img.png](public/md/product-detail.png)
## 商品介绍图(长图片)
![img.png](public/md/product-detail-lang-image.png)
## 购物车
![img.png](public/md/shopping-cart.png)
## 个人中心
![img.png](public/md/my1.png)
![img.png](public/md/my2.png)
![img.png](public/md/my3.png)
![img.png](public/md/my4.png)
![img.png](public/md/my5.png)
![img.png](public/md/order.png)

View File

@@ -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
View File

@@ -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: {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 816 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 KiB

View File

@@ -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
}
globalState.showPaymentModal(amount)
ElMessage.warning('请先登录');
router.push('/login');
return;
}
globalState.showPaymentModal(amount);
};
const handleCheckout = () => {
if (!globalState.currentUser) {
ElMessage.warning('请先登录')
router.push('/login')
return
}
const total = globalState.cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0)
globalState.showPaymentModal(total)
ElMessage.warning('请先登录');
router.push('/login');
return;
}
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
}
localStorage.setItem('cart', JSON.stringify(globalState.cartItems))
globalState.cartItems[index].quantity = quantity;
}
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.rechargeModalVisible = false
globalState.currentUser.balance = (parseFloat(globalState.currentUser.balance) + amount).toFixed(2);
globalState.updateUser(globalState.currentUser);
ElMessage.success('充值成功!');
}
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>

View 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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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' },

View File

@@ -1,9 +1,6 @@
<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>
@@ -15,34 +12,22 @@
<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="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">{{ stat.icon }}</div>
<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>
<!-- 订单筛选标签 -->
<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 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>
@@ -58,12 +43,12 @@
<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"
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">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-6">
<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>
@@ -77,7 +62,7 @@
<el-tag
:type="getStatusType(order.status)"
size="large"
class="px-4 py-1 font-medium"
class="px-4 py-1 font-medium rounded-full"
>
{{ order.status }}
</el-tag>
@@ -90,7 +75,7 @@
<el-dropdown-item command="detail">
<el-icon><View /></el-icon>查看详情
</el-dropdown-item>
<el-dropdown-item v-if="order.status === '已支付'" command="logistics">
<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">
@@ -115,14 +100,14 @@
<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"
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-20 h-20 object-cover rounded-lg cursor-pointer hover:scale-105 transition-transform"
class="w-24 h-24 object-cover rounded-xl cursor-pointer hover:scale-105 transition-transform"
@click="viewProduct(item)"
>
</div>
@@ -135,9 +120,9 @@
>
{{ 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>
<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">
@@ -163,7 +148,7 @@
</div>
<!-- 订单总计 -->
<div class="order-summary mt-6 pt-6 border-t bg-gray-50 rounded-lg p-4">
<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) }}件商品
@@ -176,13 +161,15 @@
</div>
</div>
</div>
</div>
<!-- 订单操作按钮 -->
<div class="order-actions mt-6 flex justify-end space-x-3">
<!-- 订单操作按钮 - 底部 -->
<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)"
>
立即支付
@@ -191,6 +178,7 @@
v-if="order.status === '已发货'"
type="primary"
size="large"
class="rounded-full px-6"
@click="confirmReceive(order)"
>
确认收货
@@ -199,19 +187,30 @@
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)"
>
取消订单
@@ -220,10 +219,9 @@
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div v-if="filteredOrders.length > 0" class="mt-8 flex justify-center">
<div v-if="filteredOrders.length > 0" class="mt-10 flex justify-center">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
@@ -233,15 +231,12 @@
@current-change="handlePageChange"
/>
</div>
</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;
}
/* 按钮样式 */
: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>

View File

@@ -481,7 +481,9 @@
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',
// left: magnifierLeft + 'px',
// left: magnifierTop + 'px',
left: magnifierLeft>0? `${magnifierLeft}px`: '45%',
top: '200px',
width: '450px',
height: '450px',