Files
medical-mall/pages/mall/admin/order-management.uvue

1517 lines
34 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<AdminLayout current-page="order">
<view class="order-management">
<!-- 页面标题 -->
<view class="page-header">
<text class="page-title">订单管理</text>
<text class="page-subtitle">管理系统订单和物流信息</text>
</view>
<!-- Tab 切换栏 -->
<view class="tab-bar">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ 'active': activeTab === tab.key }"
@click="switchTab(tab.key)"
>
<text class="iconfont tab-icon">{{ tab.icon }}</text>
<text class="tab-title">{{ tab.title }}</text>
</view>
</view>
<!-- 订单列表Tab -->
<view v-if="activeTab === 'order-list'">
<!-- 统计卡片 -->
<view class="stats-cards">
<view class="stat-card">
<view class="stat-icon">📦</view>
<view class="stat-content">
<text class="stat-value">{{ totalOrders }}</text>
<text class="stat-label">总订单数</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">⏳</view>
<view class="stat-content">
<text class="stat-value">{{ pendingOrders }}</text>
<text class="stat-label">待处理</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">🚚</view>
<view class="stat-content">
<text class="stat-value">{{ shippingOrders }}</text>
<text class="stat-label">配送中</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">✅</view>
<view class="stat-content">
<text class="stat-value">{{ completedOrders }}</text>
<text class="stat-label">已完成</text>
</view>
</view>
</view>
<!-- 搜索和筛选 -->
<view class="search-section">
<view class="search-bar">
<view class="search-input-wrapper">
<input
v-model="searchKeyword"
class="search-input"
placeholder="搜索订单号、用户名、商品名称"
@confirm="handleSearch"
/>
<view class="search-btn" @click="handleSearch">
<text class="iconfont icon-search"></text>
</view>
</view>
<view class="advanced-toggle" @click="showAdvancedSearch = !showAdvancedSearch">
<text>{{ showAdvancedSearch ? '收起' : '展开' }}筛选</text>
<text class="iconfont">{{ showAdvancedSearch ? 'icon-up' : 'icon-down' }}</text>
</view>
</view>
<!-- 高级搜索 -->
<view v-if="showAdvancedSearch" class="advanced-search">
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">订单状态:</text>
<picker
mode="selector"
:range="statusOptions"
:value="selectedStatus"
@change="handleStatusChange"
>
<view class="picker-display">
<text>{{ statusOptions[selectedStatus] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
<view class="filter-item">
<text class="filter-label">支付方式:</text>
<picker
mode="selector"
:range="paymentOptions"
:value="selectedPayment"
@change="handlePaymentChange"
>
<view class="picker-display">
<text>{{ paymentOptions[selectedPayment] }}</text>
<text class="iconfont icon-down"></text>
</view>
</picker>
</view>
</view>
<view class="filter-row">
<view class="filter-item">
<text class="filter-label">订单时间:</text>
<view class="date-range">
<picker
mode="date"
:value="startDate"
:start="minDate"
:end="maxDate"
@change="handleStartDateChange"
>
<view class="date-input">
<text>{{ startDate || '开始日期' }}</text>
</view>
</picker>
<text class="date-separator">-</text>
<picker
mode="date"
:value="endDate"
:start="minDate"
:end="maxDate"
@change="handleEndDateChange"
>
<view class="date-input">
<text>{{ endDate || '结束日期' }}</text>
</view>
</picker>
</view>
</view>
</view>
<view class="filter-actions">
<button class="btn-secondary" @click="resetFilters">重置</button>
<button class="btn-primary" @click="handleAdvancedSearch">搜索</button>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-bar">
<view class="left-actions">
<button class="btn-primary" @click="handleExport">
<text class="iconfont icon-export"></text>
导出订单
</button>
</view>
<view class="right-actions">
<view class="select-all">
<checkbox :checked="selectAll" @change="handleSelectAll" />
<text>全选</text>
</view>
<button class="btn-danger" :disabled="selectedOrders.length === 0" @click="handleBatchDelete">
<text class="iconfont icon-delete"></text>
批量删除
</button>
</view>
</view>
<!-- 订单列表 -->
<view class="order-table">
<view class="table-header">
<view class="table-row">
<view class="col-select">选择</view>
<view class="col-order">订单信息</view>
<view class="col-user">用户信息</view>
<view class="col-amount">金额</view>
<view class="col-status">状态</view>
<view class="col-time">下单时间</view>
<view class="col-actions">操作</view>
</view>
</view>
<view class="table-body">
<view
v-for="order in orderList"
:key="order.id"
class="table-row"
:class="{ 'selected': selectedOrders.includes(order.id) }"
>
<view class="col-select">
<checkbox
:checked="selectedOrders.includes(order.id)"
@change="handleOrderSelect(order.id, $event)"
/>
</view>
<view class="col-order">
<view class="order-number">{{ order.orderNumber }}</view>
<view class="order-items">
<text v-for="item in order.items" :key="item.id" class="order-item">
{{ item.name }} x{{ item.quantity }}
</text>
</view>
</view>
<view class="col-user">
<view class="user-name">{{ order.userName }}</view>
<view class="user-phone">{{ order.userPhone }}</view>
</view>
<view class="col-amount">
<view class="order-amount">¥{{ order.totalAmount }}</view>
<view class="payment-method">{{ order.paymentMethod }}</view>
</view>
<view class="col-status">
<view class="status-badge" :class="order.status">
{{ order.status === 'pending' ? '待处理' :
order.status === 'paid' ? '已支付' :
order.status === 'shipping' ? '配送中' :
order.status === 'completed' ? '已完成' : '已取消' }}
</view>
</view>
<view class="col-time">
<text class="order-time">{{ formatDate(order.createdAt) }}</text>
</view>
<view class="col-actions">
<button class="action-btn view" @click="handleViewOrder(order)">
<text class="iconfont icon-view"></text>
</button>
<button class="action-btn ship" @click="handleShipOrder(order)" v-if="order.status === 'paid'">
<text class="iconfont icon-ship"></text>
</button>
<button class="action-btn complete" @click="handleCompleteOrder(order)" v-if="order.status === 'shipping'">
<text class="iconfont icon-complete"></text>
</button>
<button class="action-btn delete" @click="handleDeleteOrder(order)">
<text class="iconfont icon-delete"></text>
</button>
</view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<view class="page-info">
<text>共 {{ totalOrders }} 条记录,显示 {{ (currentPage - 1) * pageSize + 1 }}-{{ Math.min(currentPage * pageSize, totalOrders) }} 条</text>
</view>
<view class="page-controls">
<button
class="page-btn"
:disabled="currentPage === 1"
@click="goToPage(currentPage - 1)"
>
上一页
</button>
<view class="page-numbers">
<button
v-for="page in visiblePages"
:key="page"
class="page-number"
:class="{ 'active': page === currentPage }"
@click="goToPage(page)"
>
{{ page }}
</button>
</view>
<button
class="page-btn"
:disabled="currentPage === totalPages"
@click="goToPage(currentPage + 1)"
>
下一页
</button>
</view>
</view>
<!-- 订单详情模态框 -->
<view v-if="showOrderDetail" class="modal-overlay" @click="closeModal">
<view class="modal-content order-detail-modal" @click.stop>
<view class="modal-header">
<text class="modal-title">订单详情 - {{ currentOrder?.orderNumber }}</text>
<view class="modal-close" @click="closeModal">
<text class="iconfont icon-close"></text>
</view>
</view>
<view class="modal-body">
<view class="order-info-section">
<view class="info-row">
<text class="info-label">订单号:</text>
<text class="info-value">{{ currentOrder?.orderNumber }}</text>
</view>
<view class="info-row">
<text class="info-label">下单时间:</text>
<text class="info-value">{{ formatDate(currentOrder?.createdAt) }}</text>
</view>
<view class="info-row">
<text class="info-label">订单状态:</text>
<view class="status-badge small" :class="currentOrder?.status">
{{ currentOrder?.status === 'pending' ? '待处理' :
currentOrder?.status === 'paid' ? '已支付' :
currentOrder?.status === 'shipping' ? '配送中' :
currentOrder?.status === 'completed' ? '已完成' : '已取消' }}
</view>
</view>
</view>
<view class="user-info-section">
<text class="section-title">用户信息</text>
<view class="info-row">
<text class="info-label">用户名:</text>
<text class="info-value">{{ currentOrder?.userName }}</text>
</view>
<view class="info-row">
<text class="info-label">联系电话:</text>
<text class="info-value">{{ currentOrder?.userPhone }}</text>
</view>
<view class="info-row">
<text class="info-label">收货地址:</text>
<text class="info-value">{{ currentOrder?.address }}</text>
</view>
</view>
<view class="items-section">
<text class="section-title">商品信息</text>
<view class="order-items-list">
<view v-for="item in currentOrder?.items" :key="item.id" class="item-row">
<view class="item-info">
<text class="item-name">{{ item.name }}</text>
<text class="item-spec">{{ item.spec }}</text>
</view>
<view class="item-price">¥{{ item.price }}</view>
<view class="item-quantity">x{{ item.quantity }}</view>
<view class="item-total">¥{{ item.price * item.quantity }}</view>
</view>
</view>
</view>
<view class="amount-section">
<view class="amount-row">
<text class="amount-label">商品总价:</text>
<text class="amount-value">¥{{ currentOrder?.subtotal }}</text>
</view>
<view class="amount-row">
<text class="amount-label">运费:</text>
<text class="amount-value">¥{{ currentOrder?.shippingFee }}</text>
</view>
<view class="amount-row total">
<text class="amount-label">订单总价:</text>
<text class="amount-value">¥{{ currentOrder?.totalAmount }}</text>
</view>
</view>
</view>
<view class="modal-footer">
<button class="btn-secondary" @click="closeModal">关闭</button>
<view class="action-buttons">
<button
class="btn-primary"
@click="handleShipOrder(currentOrder)"
v-if="currentOrder?.status === 'paid'"
>
发货
</button>
<button
class="btn-success"
@click="handleCompleteOrder(currentOrder)"
v-if="currentOrder?.status === 'shipping'"
>
确认收货
</button>
</view>
</view>
</view>
</view>
<!-- 订单详情Tab -->
<view v-if="activeTab === 'order-detail'" class="order-detail-section">
<view class="order-detail-placeholder">
<text class="placeholder-title">订单详情页面</text>
<text class="placeholder-desc">选择订单列表中的订单查看详情</text>
</view>
</view>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref, computed} from 'vue'
import AdminLayout from '@/layouts/admin/index.uvue'
// Tab 相关
const activeTab = ref('order-list')
const tabs = ref([
{ key: 'order-list', title: '订单列表', icon: 'icon-list' },
{ key: 'order-detail', title: '订单详情', icon: 'icon-detail' }
])
// 响应式数据
const searchKeyword = ref('')
const showAdvancedSearch = ref(false)
const totalOrders = ref(342)
const pendingOrders = ref(25)
const shippingOrders = ref(15)
const completedOrders = ref(302)
// 筛选条件
const selectedStatus = ref(0)
const selectedPayment = ref(0)
const startDate = ref('')
const endDate = ref('')
const minDate = '2020-01-01'
const maxDate = new Date().toISOString().split('T')[0]
// 选项数据
const statusOptions = ['全部状态', '待处理', '已支付', '配送中', '已完成', '已取消']
const paymentOptions = ['全部方式', '支付宝', '微信支付', '银行卡', '货到付款']
// 订单列表
const orderList = ref([
{
id: 1,
orderNumber: 'DD202401150001',
userName: '张三',
userPhone: '13800138001',
address: '北京市朝阳区某某街道某某小区某某号',
status: 'pending',
paymentMethod: '支付宝',
subtotal: 2599,
shippingFee: 0,
totalAmount: 2599,
createdAt: '2024-01-15T10:30:00Z',
items: [
{ id: 1, name: 'iPhone 15 Pro', spec: '256GB 黑色', price: 8999, quantity: 1 },
{ id: 2, name: '手机壳', spec: '硅胶保护壳', price: 59, quantity: 1 }
]
},
{
id: 2,
orderNumber: 'DD202401150002',
userName: '李四',
userPhone: '13800138002',
address: '上海市浦东新区某某街道某某小区某某号',
status: 'paid',
paymentMethod: '微信支付',
subtotal: 699,
shippingFee: 10,
totalAmount: 709,
createdAt: '2024-01-15T09:15:00Z',
items: [
{ id: 3, name: 'Nike 运动鞋', spec: '42码 白色', price: 599, quantity: 1 },
{ id: 4, name: '运动袜', spec: '中筒 黑色', price: 29, quantity: 2 }
]
},
{
id: 3,
orderNumber: 'DD202401150003',
userName: '王五',
userPhone: '13800138003',
address: '广州市天河区某某街道某某小区某某号',
status: 'shipping',
paymentMethod: '支付宝',
subtotal: 1299,
shippingFee: 0,
totalAmount: 1299,
createdAt: '2024-01-14T16:45:00Z',
items: [
{ id: 5, name: '咖啡机', spec: '全自动磨豆', price: 1299, quantity: 1 }
]
},
{
id: 4,
orderNumber: 'DD202401150004',
userName: '赵六',
userPhone: '13800138004',
address: '深圳市南山区某某街道某某小区某某号',
status: 'completed',
paymentMethod: '银行卡',
subtotal: 4599,
shippingFee: 0,
totalAmount: 4599,
createdAt: '2024-01-13T14:20:00Z',
items: [
{ id: 6, name: '联想笔记本', spec: 'i5 16GB 512GB SSD', price: 4599, quantity: 1 }
]
}
])
// 分页相关
const currentPage = ref(1)
const pageSize = ref(10)
const totalPages = computed(() => Math.ceil(totalOrders.value / pageSize.value))
const visiblePages = computed(() => {
const pages = []
const start = Math.max(1, currentPage.value - 2)
const end = Math.min(totalPages.value, currentPage.value + 2)
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
})
// 选择相关
const selectAll = ref(false)
const selectedOrders = ref<number[]>([])
// 模态框相关
const showOrderDetail = ref(false)
const currentOrder = ref(null)
// 方法
const handleSearch = () => {
console.log('搜索:', searchKeyword.value)
// TODO: 实现搜索逻辑
}
const handleStatusChange = (e: any) => {
selectedStatus.value = e.detail.value
}
const handlePaymentChange = (e: any) => {
selectedPayment.value = e.detail.value
}
const handleStartDateChange = (e: any) => {
startDate.value = e.detail.value
}
const handleEndDateChange = (e: any) => {
endDate.value = e.detail.value
}
const resetFilters = () => {
selectedStatus.value = 0
selectedPayment.value = 0
startDate.value = ''
endDate.value = ''
searchKeyword.value = ''
}
const handleAdvancedSearch = () => {
console.log('高级搜索:', {
status: statusOptions[selectedStatus.value],
payment: paymentOptions[selectedPayment.value],
startDate: startDate.value,
endDate: endDate.value,
keyword: searchKeyword.value
})
// TODO: 实现高级搜索逻辑
}
const handleExport = () => {
uni.showToast({
title: '导出功能开发中',
icon: 'none'
})
}
const handleSelectAll = (e: any) => {
selectAll.value = e.detail.value
if (selectAll.value) {
selectedOrders.value = orderList.value.map(order => order.id)
} else {
selectedOrders.value = []
}
}
const handleOrderSelect = (orderId: number, e: any) => {
if (e.detail.value) {
selectedOrders.value.push(orderId)
} else {
selectedOrders.value = selectedOrders.value.filter(id => id !== orderId)
}
selectAll.value = selectedOrders.value.length === orderList.value.length
}
const handleBatchDelete = () => {
if (selectedOrders.value.length === 0) return
uni.showModal({
title: '确认删除',
content: `确定要删除 ${selectedOrders.value.length} 个订单吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现批量删除逻辑
selectedOrders.value.forEach(id => {
const index = orderList.value.findIndex(o => o.id === id)
if (index > -1) {
orderList.value.splice(index, 1)
totalOrders.value--
}
})
uni.showToast({
title: '批量删除成功',
icon: 'success'
})
selectedOrders.value = []
selectAll.value = false
}
}
})
}
const handleViewOrder = (order: any) => {
currentOrder.value = order
showOrderDetail.value = true
}
const handleShipOrder = (order: any) => {
uni.showModal({
title: '确认发货',
content: `确定要为订单 "${order.orderNumber}" 发货吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现发货逻辑
order.status = 'shipping'
uni.showToast({
title: '发货成功',
icon: 'success'
})
closeModal()
}
}
})
}
const handleCompleteOrder = (order: any) => {
uni.showModal({
title: '确认收货',
content: `确定订单 "${order.orderNumber}" 已收货吗?`,
success: (res) => {
if (res.confirm) {
// TODO: 实现确认收货逻辑
order.status = 'completed'
uni.showToast({
title: '确认收货成功',
icon: 'success'
})
closeModal()
}
}
})
}
const handleDeleteOrder = (order: any) => {
uni.showModal({
title: '确认删除',
content: `确定要删除订单 "${order.orderNumber}" 吗?此操作不可恢复。`,
success: (res) => {
if (res.confirm) {
// TODO: 实现删除逻辑
const index = orderList.value.findIndex(o => o.id === order.id)
if (index > -1) {
orderList.value.splice(index, 1)
totalOrders.value--
}
uni.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
}
const closeModal = () => {
showOrderDetail.value = false
currentOrder.value = null
}
const goToPage = (page: number) => {
currentPage.value = page
// TODO: 实现分页数据加载
}
const formatDate = (dateString: string) => {
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', { hour12: false })
}
// Tab 切换方法
const switchTab = (tabKey: string) => {
activeTab.value = tabKey
}
// 页面生命周期
onLoad((options: any) => {
// 处理页面参数切换到对应的tab
if (options && options.action) {
if (options.action === 'detail') {
activeTab.value = 'order-detail'
} else {
activeTab.value = 'order-list'
}
} else {
activeTab.value = 'order-list'
}
console.log('订单管理页面加载,参数:', options)
})
onMounted(() => {
// 初始化数据
console.log('订单管理页面初始化')
})
</script>
<style lang="scss">
/* Tab 栏样式 */
.tab-bar {
display: flex;
background-color: #ffffff;
border-radius: 8rpx;
padding: 8rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.tab-item {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 16rpx 24rpx;
border-radius: 6rpx;
cursor: pointer;
transition: all 0.2s;
background-color: #f5f5f5;
color: #666666;
}
.tab-item:hover {
background-color: #e8e8e8;
}
.tab-item.active {
background-color: #1890ff;
color: #ffffff;
}
.tab-icon {
font-size: 16rpx;
}
.tab-title {
font-size: 14rpx;
font-weight: 500;
}
/* 订单详情占位符 */
.order-detail-section {
background-color: #ffffff;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
min-height: 400rpx;
}
.order-detail-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80rpx 40rpx;
text-align: center;
}
.placeholder-title {
font-size: 20rpx;
font-weight: 600;
color: #262626;
margin-bottom: 12rpx;
}
.placeholder-desc {
font-size: 14rpx;
color: #666666;
}
.order-management {
padding: 20rpx;
}
.page-header {
margin-bottom: 30rpx;
}
.page-title {
display: block;
font-size: 36rpx;
font-weight: bold;
color: #333;
margin-bottom: 10rpx;
}
.page-subtitle {
display: block;
font-size: 26rpx;
color: #666;
}
.stats-cards {
display: flex;
justify-content: space-between;
margin-bottom: 40rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.stat-card {
flex: 1;
min-width: 200rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16rpx;
padding: 40rpx 30rpx;
text-align: center;
color: #fff;
box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
}
.stat-icon {
font-size: 48rpx;
margin-bottom: 20rpx;
}
.stat-content {
flex: 1;
}
.stat-value {
display: block;
font-size: 48rpx;
font-weight: bold;
margin-bottom: 8rpx;
}
.stat-label {
display: block;
font-size: 24rpx;
opacity: 0.9;
}
.search-section {
background-color: #fff;
border-radius: 12rpx;
padding: 30rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.search-bar {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 20rpx;
}
.search-input-wrapper {
flex: 1;
display: flex;
align-items: center;
border: 1rpx solid #ddd;
border-radius: 8rpx;
overflow: hidden;
}
.search-input {
flex: 1;
padding: 16rpx 20rpx;
border: none;
font-size: 28rpx;
}
.search-btn {
padding: 16rpx 20rpx;
background-color: #1890ff;
color: #fff;
border: none;
cursor: pointer;
}
.advanced-toggle {
display: flex;
align-items: center;
gap: 10rpx;
color: #1890ff;
cursor: pointer;
font-size: 26rpx;
}
.advanced-search {
border-top: 1rpx solid #eee;
padding-top: 30rpx;
margin-top: 20rpx;
}
.filter-row {
display: flex;
gap: 30rpx;
margin-bottom: 20rpx;
flex-wrap: wrap;
}
.filter-item {
flex: 1;
min-width: 200rpx;
}
.filter-label {
display: block;
font-size: 26rpx;
color: #666;
margin-bottom: 10rpx;
}
.picker-display {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background-color: #fff;
cursor: pointer;
}
.date-range {
display: flex;
align-items: center;
gap: 10rpx;
}
.date-input {
flex: 1;
padding: 16rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
background-color: #fff;
text-align: center;
cursor: pointer;
}
.date-separator {
color: #666;
font-size: 24rpx;
}
.filter-actions {
display: flex;
gap: 20rpx;
justify-content: flex-end;
margin-top: 20rpx;
}
.action-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.left-actions,
.right-actions {
display: flex;
align-items: center;
gap: 15rpx;
}
.select-all {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 26rpx;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-secondary {
background-color: #fff;
color: #666;
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-success {
background-color: #52c41a;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-danger {
background-color: #ff4d4f;
color: #fff;
border: none;
border-radius: 8rpx;
padding: 16rpx 24rpx;
font-size: 26rpx;
cursor: pointer;
display: flex;
align-items: center;
gap: 8rpx;
}
.btn-danger:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.order-table {
background-color: #fff;
border-radius: 12rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.table-header {
background-color: #f5f5f5;
border-bottom: 1rpx solid #eee;
}
.table-row {
display: flex;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.table-row:last-child {
border-bottom: none;
}
.table-row.selected {
background-color: #f0f8ff;
}
.col-select {
width: 80rpx;
text-align: center;
}
.col-order {
flex: 2;
}
.col-user {
flex: 1.5;
}
.col-amount,
.col-status,
.col-time {
flex: 1;
}
.col-actions {
width: 200rpx;
display: flex;
gap: 10rpx;
justify-content: center;
}
.order-number {
font-size: 28rpx;
font-weight: bold;
color: #1890ff;
margin-bottom: 8rpx;
}
.order-items {
display: flex;
flex-direction: column;
gap: 4rpx;
}
.order-item {
font-size: 24rpx;
color: #666;
display: block;
}
.user-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.user-phone {
font-size: 24rpx;
color: #666;
}
.order-amount {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
margin-bottom: 4rpx;
}
.payment-method {
font-size: 24rpx;
color: #999;
}
.status-badge {
display: inline-block;
padding: 6rpx 12rpx;
border-radius: 12rpx;
font-size: 22rpx;
font-weight: bold;
text-align: center;
}
.status-badge.small {
padding: 4rpx 8rpx;
font-size: 20rpx;
}
.status-badge.pending {
background-color: #faad14;
color: #fff;
}
.status-badge.paid {
background-color: #1890ff;
color: #fff;
}
.status-badge.shipping {
background-color: #52c41a;
color: #fff;
}
.status-badge.completed {
background-color: #722ed1;
color: #fff;
}
.status-badge.cancelled {
background-color: #ff4d4f;
color: #fff;
}
.order-time {
font-size: 24rpx;
color: #666;
}
.action-btn {
width: 60rpx;
height: 60rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
font-size: 24rpx;
}
.action-btn.view {
background-color: #1890ff;
color: #fff;
}
.action-btn.ship {
background-color: #52c41a;
color: #fff;
}
.action-btn.complete {
background-color: #722ed1;
color: #fff;
}
.action-btn.delete {
background-color: #ff4d4f;
color: #fff;
}
.pagination {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 30rpx;
flex-wrap: wrap;
gap: 20rpx;
}
.page-info {
font-size: 26rpx;
color: #666;
}
.page-controls {
display: flex;
align-items: center;
gap: 15rpx;
}
.page-btn {
padding: 12rpx 20rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
background-color: #fff;
color: #333;
cursor: pointer;
font-size: 26rpx;
}
.page-btn:disabled {
background-color: #f5f5f5;
color: #ccc;
cursor: not-allowed;
}
.page-numbers {
display: flex;
gap: 8rpx;
margin: 0 20rpx;
}
.page-number {
width: 50rpx;
height: 50rpx;
border: 1rpx solid #ddd;
border-radius: 6rpx;
background-color: #fff;
color: #333;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 26rpx;
}
.page-number.active {
background-color: #1890ff;
color: #fff;
border-color: #1890ff;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: #fff;
border-radius: 12rpx;
width: 90%;
max-width: 800rpx;
max-height: 80vh;
overflow-y: auto;
}
.order-detail-modal {
max-width: 700rpx;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #eee;
}
.modal-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.modal-close {
cursor: pointer;
font-size: 28rpx;
color: #999;
padding: 10rpx;
}
.modal-body {
padding: 30rpx;
}
.order-info-section,
.user-info-section,
.items-section,
.amount-section {
margin-bottom: 30rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.info-row:last-child {
margin-bottom: 0;
}
.info-label {
font-size: 26rpx;
color: #666;
flex: 1;
}
.info-value {
font-size: 26rpx;
color: #333;
flex: 2;
text-align: right;
}
.order-items-list {
border: 1rpx solid #eee;
border-radius: 8rpx;
overflow: hidden;
}
.item-row {
display: flex;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
gap: 15rpx;
}
.item-row:last-child {
border-bottom: none;
}
.item-info {
flex: 2;
}
.item-name {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
display: block;
}
.item-spec {
font-size: 24rpx;
color: #666;
display: block;
}
.item-price,
.item-quantity,
.item-total {
flex: 1;
text-align: center;
font-size: 26rpx;
color: #333;
}
.amount-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.amount-row.total {
border-top: 1rpx solid #eee;
padding-top: 15rpx;
margin-top: 15rpx;
}
.amount-label {
font-size: 26rpx;
color: #666;
}
.amount-value {
font-size: 26rpx;
color: #333;
}
.amount-row.total .amount-value {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
.modal-footer {
display: flex;
gap: 20rpx;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-top: 1rpx solid #eee;
}
.action-buttons {
display: flex;
gap: 15rpx;
}
.iconfont {
font-family: 'iconfont';
font-size: 24rpx;
}
/* 响应式设计 */
@media screen and (max-width: 750rpx) {
.stats-cards {
flex-direction: column;
}
.filter-row {
flex-direction: column;
gap: 15rpx;
}
.action-bar {
flex-direction: column;
align-items: stretch;
}
.left-actions,
.right-actions {
justify-content: center;
}
.table-row {
flex-wrap: wrap;
gap: 10rpx;
}
.col-order,
.col-user {
flex: 1 1 100%;
}
.pagination {
flex-direction: column;
text-align: center;
}
.info-row {
flex-direction: column;
align-items: flex-start;
gap: 5rpx;
}
.info-value {
text-align: left;
}
.item-row {
flex-wrap: wrap;
gap: 10rpx;
}
.modal-footer {
flex-direction: column;
}
.action-buttons {
width: 100%;
justify-content: center;
}
}
</style>