1001 lines
27 KiB
Vue
1001 lines
27 KiB
Vue
<template>
|
|
<div class="admin-orders">
|
|
<!-- 统计卡片 -->
|
|
<div class="order-stats">
|
|
<div class="stat-card">
|
|
<div class="stat-icon total">
|
|
<el-icon><ShoppingCart /></el-icon>
|
|
</div>
|
|
<div class="stat-info">
|
|
<div class="stat-number">{{ orderStats.total }}</div>
|
|
<div class="stat-label">{{ t('admin.orders.stats.total') }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-icon pending">
|
|
<el-icon><Clock /></el-icon>
|
|
</div>
|
|
<div class="stat-info">
|
|
<div class="stat-number">{{ orderStats.pending }}</div>
|
|
<div class="stat-label">{{ t('admin.orders.stats.pending') }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-icon completed">
|
|
<el-icon><Check /></el-icon>
|
|
</div>
|
|
<div class="stat-info">
|
|
<div class="stat-number">{{ orderStats.completed }}</div>
|
|
<div class="stat-label">{{ t('admin.orders.stats.completed') }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stat-card">
|
|
<div class="stat-icon revenue">
|
|
<el-icon><Money /></el-icon>
|
|
</div>
|
|
<div class="stat-info">
|
|
<div class="stat-number">¥{{ orderStats.revenue.toLocaleString() }}</div>
|
|
<div class="stat-label">{{ t('admin.orders.stats.revenue') }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 筛选和搜索 -->
|
|
<div class="orders-filters">
|
|
<div class="filter-group">
|
|
<el-select
|
|
v-model="selectedStatus"
|
|
:placeholder="t('admin.orders.status')"
|
|
clearable
|
|
>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.pendingConfirmation')"
|
|
value="pendingConfirmation"
|
|
/>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.confirmed')"
|
|
value="confirmed"
|
|
/>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.rejected')"
|
|
value="rejected"
|
|
/>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.processing')"
|
|
value="processing"
|
|
/>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.readyToShip')"
|
|
value="readyToShip"
|
|
/>
|
|
<el-option
|
|
:label="t('admin.orders.statusOptions.shipped')"
|
|
value="shipped"
|
|
/>
|
|
</el-select>
|
|
|
|
<el-date-picker
|
|
v-model="dateRange"
|
|
type="daterange"
|
|
range-separator="-"
|
|
:start-placeholder="t('admin.orders.dateRange')"
|
|
:end-placeholder="t('admin.orders.dateRange')"
|
|
format="YYYY-MM-DD"
|
|
value-format="YYYY-MM-DD"
|
|
clearable
|
|
/>
|
|
</div>
|
|
|
|
<div class="search-group">
|
|
<el-input
|
|
v-model="searchQuery"
|
|
:placeholder="t('admin.orders.search')"
|
|
prefix-icon="Search"
|
|
clearable
|
|
/>
|
|
</div>
|
|
|
|
<div class="filter-actions">
|
|
<el-button
|
|
type="primary"
|
|
@click="handleExportOrders"
|
|
>
|
|
<el-icon><Download /></el-icon>
|
|
{{ t('admin.orders.export') }}
|
|
</el-button>
|
|
<el-button
|
|
@click="refresh"
|
|
>
|
|
<el-icon><Refresh /></el-icon>
|
|
{{ t('admin.common.refresh') }}
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 订单列表 -->
|
|
<div class="orders-list">
|
|
<!-- stripe -->
|
|
<el-table
|
|
:data="filteredOrdersList"
|
|
style="width: 100%"
|
|
v-loading="loading"
|
|
@row-click="handleOrderDetail"
|
|
@selection-change="handleSelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="55" />
|
|
<el-table-column prop="id" label="ID" width="100" />
|
|
<el-table-column prop="orderNumber" :label="t('admin.orders.orderNumber')" width="150" />
|
|
<el-table-column prop="customerName" :label="t('admin.orders.customer')" width="120" />
|
|
<el-table-column prop="totalAmount" :label="t('admin.orders.total')" width="120">
|
|
<template #default="{ row }">
|
|
¥{{ row.totalAmount.toFixed(2) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="status" :label="t('admin.orders.status')" width="120">
|
|
<template #default="{ row }">
|
|
<el-tag :type="getStatusTagType(row.status)">
|
|
{{ t(`admin.orders.statusOptions.${row.status}`) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="paymentMethod" :label="t('admin.orders.payment')" width="100">
|
|
<template #default="{ row }">
|
|
{{ t(`admin.orders.paymentOptions.${row.paymentMethod}`) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="orderDate" :label="t('admin.orders.date')" width="180">
|
|
<template #default="{ row }">
|
|
{{ formatDate(row.orderDate) }}
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column :label="t('admin.orders.actions')" width="200" fixed="right">
|
|
<template #default="{ row }">
|
|
<div class="actions-container">
|
|
<!-- 待确认状态:显示"去确认"按钮 -->
|
|
<el-button
|
|
v-if="row.status === 'pendingConfirmation'"
|
|
size="small"
|
|
type="primary"
|
|
@click.stop="handleConfirmOrder(row)"
|
|
>
|
|
{{ t('admin.orders.confirm') }}
|
|
</el-button>
|
|
|
|
<!-- 已拒绝状态:显示退款提示 -->
|
|
<el-tag v-if="row.status === 'rejected'" type="danger" size="small">
|
|
{{ t('admin.orders.refundNotice') }}
|
|
</el-tag>
|
|
|
|
<!-- 待处理状态:显示"去处理"按钮 -->
|
|
<el-button
|
|
v-if="row.status === 'processing'"
|
|
size="small"
|
|
type="primary"
|
|
@click.stop="handleProcessOrder(row)"
|
|
>
|
|
{{ t('admin.orders.process') }}
|
|
</el-button>
|
|
|
|
<!-- 待发货状态:显示"发货"按钮 -->
|
|
<el-button
|
|
v-if="row.status === 'readyToShip'"
|
|
size="small"
|
|
type="primary"
|
|
@click.stop="handleShipOrder(row)"
|
|
>
|
|
{{ t('admin.orders.ship') }}
|
|
</el-button>
|
|
|
|
<!-- 已发货状态:显示"查看物流"按钮 -->
|
|
<el-button
|
|
v-if="row.status === 'shipped'"
|
|
size="small"
|
|
type="success"
|
|
@click.stop="handleViewLogistics(row)"
|
|
>
|
|
{{ t('admin.orders.viewLogistics') }}
|
|
</el-button>
|
|
|
|
<!-- 所有状态都显示"查看"按钮 -->
|
|
<el-button size="small" @click.stop="handleViewOrder(row)">
|
|
{{ t('admin.orders.view') }}
|
|
</el-button>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<!-- 分页 -->
|
|
<div class="pagination">
|
|
<el-pagination
|
|
v-model:current-page="currentPage"
|
|
v-model:page-size="pageSize"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:total="totalOrders"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
@size-change="handleSizeChange"
|
|
@current-change="handleCurrentChange"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 订单详情对话框 -->
|
|
<el-dialog
|
|
:title="t('admin.orders.detail')"
|
|
v-model="detailDialogVisible"
|
|
width="800px"
|
|
>
|
|
<div v-if="selectedOrder" class="order-detail">
|
|
<div class="detail-section">
|
|
<h4>{{ t('admin.orders.basicInfo') }}</h4>
|
|
<el-descriptions :column="2" border>
|
|
<el-descriptions-item :label="t('admin.orders.orderNumber')">
|
|
{{ selectedOrder.orderNumber }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item :label="t('admin.orders.customer')">
|
|
{{ selectedOrder.customerName }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item :label="t('admin.orders.total')">
|
|
¥{{ selectedOrder.totalAmount.toFixed(2) }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item :label="t('admin.orders.status')">
|
|
<el-tag :type="getStatusTagType(selectedOrder.status)">
|
|
{{ t(`admin.orders.statusOptions.${selectedOrder.status}`) }}
|
|
</el-tag>
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
</div>
|
|
|
|
<div class="detail-section">
|
|
<h4>{{ t('admin.orders.items') }}</h4>
|
|
<!-- stripe -->
|
|
<el-table :data="selectedOrder.items" >
|
|
<el-table-column prop="name" :label="t('admin.orders.itemName')" />
|
|
<el-table-column prop="quantity" :label="t('admin.orders.quantity')" width="100" />
|
|
<el-table-column prop="price" :label="t('admin.orders.price')" width="120">
|
|
<template #default="{ row }">
|
|
¥{{ row.price.toFixed(2) }}
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 更新状态对话框 -->
|
|
<el-dialog
|
|
:title="t('admin.orders.updateStatus')"
|
|
v-model="statusDialogVisible"
|
|
width="400px"
|
|
>
|
|
<el-form v-if="selectedOrderForStatus" :model="statusForm" label-width="100px">
|
|
<el-form-item :label="t('admin.orders.currentStatus')">
|
|
<el-tag :type="getStatusTagType(selectedOrderForStatus.status)">
|
|
{{ t(`admin.orders.statusOptions.${selectedOrderForStatus.status}`) }}
|
|
</el-tag>
|
|
</el-form-item>
|
|
<el-form-item :label="t('admin.orders.newStatus')" prop="status">
|
|
<el-select v-model="statusForm.status" :placeholder="t('admin.orders.selectStatus')">
|
|
<el-option
|
|
v-for="status in availableStatuses"
|
|
:key="status.value"
|
|
:label="status.label"
|
|
:value="status.value"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="statusDialogVisible = false">{{ t('common.cancel') }}</el-button>
|
|
<el-button type="primary" @click="handleStatusUpdate">{{ t('common.confirm') }}</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 发货对话框 -->
|
|
<el-dialog
|
|
v-model="shippingDialogVisible"
|
|
:title="t('admin.orders.ship')"
|
|
width="40%"
|
|
>
|
|
<div v-if="selectedOrder">
|
|
<el-form :model="shippingForm" label-width="120px">
|
|
<el-form-item :label="t('admin.orders.trackingNumber')" required>
|
|
<el-input v-model="shippingForm.trackingNumber" placeholder="请输入物流单号"></el-input>
|
|
</el-form-item>
|
|
<el-form-item :label="t('admin.orders.carrier')" required>
|
|
<el-select v-model="shippingForm.carrier" placeholder="请选择物流公司">
|
|
<el-option label="顺丰速运" value="sf"></el-option>
|
|
<el-option label="圆通速递" value="yto"></el-option>
|
|
<el-option label="中通快递" value="zto"></el-option>
|
|
<el-option label="申通快递" value="sto"></el-option>
|
|
<el-option label="韵达速递" value="yd"></el-option>
|
|
<el-option label="邮政EMS" value="ems"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item :label="t('admin.orders.shippingNote')">
|
|
<el-input
|
|
v-model="shippingForm.note"
|
|
type="textarea"
|
|
rows="3"
|
|
placeholder="发货备注(可选)">
|
|
</el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
<template #footer>
|
|
<el-button @click="shippingDialogVisible = false">{{ t('common.cancel') }}</el-button>
|
|
<el-button type="primary" @click="confirmShipOrder">{{ t('common.confirm') }}</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<!-- 物流对话框 -->
|
|
<el-dialog
|
|
v-model="logisticsDialogVisible"
|
|
:title="t('admin.orders.viewLogistics')"
|
|
width="50%"
|
|
>
|
|
<div v-if="selectedOrder">
|
|
<el-descriptions :column="2" border>
|
|
<el-descriptions-item :label="t('admin.orders.trackingNumber')">
|
|
{{ selectedOrder.trackingNumber || 'SF1234567890' }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item :label="t('admin.orders.carrier')">
|
|
{{ selectedOrder.carrier || '顺丰速运' }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
|
|
<el-divider>{{ t('admin.orders.logisticsTimeline') }}</el-divider>
|
|
|
|
<el-timeline>
|
|
<el-timeline-item
|
|
v-for="(activity, index) in logisticsActivities"
|
|
:key="index"
|
|
:timestamp="activity.timestamp"
|
|
:type="activity.type"
|
|
>
|
|
{{ activity.content }}
|
|
</el-timeline-item>
|
|
</el-timeline>
|
|
</div>
|
|
<template #footer>
|
|
<el-button @click="logisticsDialogVisible = false">{{ t('common.close') }}</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, computed, onMounted, reactive } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useRouter } from 'vue-router'
|
|
import {
|
|
Download,
|
|
ShoppingCart,
|
|
Clock,
|
|
Check,
|
|
Money,
|
|
Search,
|
|
Refresh
|
|
} from '@element-plus/icons-vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
|
export default {
|
|
name: 'AdminOrders',
|
|
components: {
|
|
Download,
|
|
ShoppingCart,
|
|
Clock,
|
|
Check,
|
|
Money,
|
|
Search,
|
|
Refresh
|
|
},
|
|
setup() {
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
|
|
// 响应式数据
|
|
const loading = ref(false)
|
|
const searchQuery = ref('')
|
|
const selectedStatus = ref('')
|
|
const dateRange = ref([])
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(20)
|
|
const totalOrders = ref(0)
|
|
const detailDialogVisible = ref(false)
|
|
const statusDialogVisible = ref(false)
|
|
const shippingDialogVisible = ref(false)
|
|
const logisticsDialogVisible = ref(false)
|
|
const selectedOrder = ref(null)
|
|
const selectedOrderForStatus = ref(null)
|
|
const selectedOrders = ref([]) // 多选选中的订单
|
|
|
|
const statusForm = reactive({
|
|
status: ''
|
|
})
|
|
|
|
// 发货表单
|
|
const shippingForm = reactive({
|
|
trackingNumber: '',
|
|
carrier: '',
|
|
note: ''
|
|
})
|
|
|
|
// 物流活动时间线
|
|
const logisticsActivities = ref([
|
|
{
|
|
content: '您的订单已发货',
|
|
timestamp: '2023-12-01 10:00:00',
|
|
type: 'primary'
|
|
},
|
|
{
|
|
content: '您的订单已到达【北京转运中心】',
|
|
timestamp: '2023-12-01 14:30:00',
|
|
type: 'success'
|
|
},
|
|
{
|
|
content: '您的订单正在派送中',
|
|
timestamp: '2023-12-02 09:00:00',
|
|
type: 'warning'
|
|
},
|
|
{
|
|
content: '您的订单已签收',
|
|
timestamp: '2023-12-02 16:30:00',
|
|
type: 'success'
|
|
}
|
|
])
|
|
|
|
// 模拟订单数据
|
|
const ordersList = ref([
|
|
{
|
|
id: 1,
|
|
orderNumber: 'ORD-2024-001',
|
|
customerName: '张三',
|
|
totalAmount: 1299.00,
|
|
status: 'shipped',
|
|
paymentMethod: 'alipay',
|
|
orderDate: '2024-01-15 10:30:00',
|
|
items: [
|
|
{ name: 'AI智能音箱', quantity: 1, price: 799.00 },
|
|
{ name: '智能插座', quantity: 2, price: 250.00 }
|
|
]
|
|
},
|
|
{
|
|
id: 2,
|
|
orderNumber: 'ORD-2024-002',
|
|
customerName: '李四',
|
|
totalAmount: 2599.00,
|
|
status: 'readyToShip',
|
|
paymentMethod: 'wechat',
|
|
orderDate: '2024-01-14 16:20:00',
|
|
items: [
|
|
{ name: '智能门锁', quantity: 1, price: 1999.00 },
|
|
{ name: '智能摄像头', quantity: 1, price: 600.00 }
|
|
]
|
|
},
|
|
{
|
|
id: 3,
|
|
orderNumber: 'ORD-2024-003',
|
|
customerName: '王五',
|
|
totalAmount: 899.00,
|
|
status: 'processing',
|
|
paymentMethod: 'credit',
|
|
orderDate: '2024-01-13 09:15:00',
|
|
items: [
|
|
{ name: '智能灯泡', quantity: 3, price: 299.00 }
|
|
]
|
|
},
|
|
{
|
|
id: 4,
|
|
orderNumber: 'ORD-2024-004',
|
|
customerName: '赵六',
|
|
totalAmount: 1599.00,
|
|
status: 'pendingConfirmation',
|
|
paymentMethod: 'alipay',
|
|
orderDate: '2024-01-12 14:45:00',
|
|
items: [
|
|
{ name: '智能扫地机器人', quantity: 1, price: 1599.00 }
|
|
]
|
|
},
|
|
{
|
|
id: 5,
|
|
orderNumber: 'ORD-2024-005',
|
|
customerName: '孙七',
|
|
totalAmount: 799.00,
|
|
status: 'processing',
|
|
paymentMethod: 'wechat',
|
|
orderDate: '2024-01-11 11:30:00',
|
|
items: [
|
|
{ name: '智能体重秤', quantity: 1, price: 799.00 }
|
|
]
|
|
},
|
|
{
|
|
id: 6,
|
|
orderNumber: 'ORD-2024-006',
|
|
customerName: '周八',
|
|
totalAmount: 1299.00,
|
|
status: 'rejected',
|
|
paymentMethod: 'credit',
|
|
orderDate: '2024-01-10 15:20:00',
|
|
items: [
|
|
{ name: '智能手环', quantity: 1, price: 1299.00 }
|
|
]
|
|
}
|
|
])
|
|
|
|
// 统计数据
|
|
const orderStats = ref({
|
|
total: 156,
|
|
pending: 23,
|
|
completed: 128,
|
|
revenue: 156789
|
|
})
|
|
|
|
// 计算属性
|
|
const filteredOrdersList = computed(() => {
|
|
let result = ordersList.value
|
|
|
|
if (searchQuery.value) {
|
|
result = result.filter(item =>
|
|
item.orderNumber.includes(searchQuery.value) ||
|
|
item.customerName.includes(searchQuery.value)
|
|
)
|
|
}
|
|
|
|
if (selectedStatus.value) {
|
|
result = result.filter(item => item.status === selectedStatus.value)
|
|
}
|
|
|
|
if (dateRange.value && dateRange.value.length === 2) {
|
|
result = result.filter(item => {
|
|
const orderDate = new Date(item.orderDate)
|
|
const startDate = new Date(dateRange.value[0])
|
|
const endDate = new Date(dateRange.value[1])
|
|
return orderDate >= startDate && orderDate <= endDate
|
|
})
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
const availableStatuses = computed(() => {
|
|
const allStatuses = [
|
|
{ value: 'pendingConfirmation', label: t('admin.orders.statusOptions.pendingConfirmation') },
|
|
{ value: 'rejected', label: t('admin.orders.statusOptions.rejected') },
|
|
{ value: 'processing', label: t('admin.orders.statusOptions.processing') },
|
|
{ value: 'readyToShip', label: t('admin.orders.statusOptions.readyToShip') },
|
|
{ value: 'shipped', label: t('admin.orders.statusOptions.shipped') }
|
|
]
|
|
return allStatuses
|
|
})
|
|
|
|
// 方法
|
|
const getStatusTagType = (status) => {
|
|
const statusMap = {
|
|
pendingConfirmation: 'info', // 待确认 - 蓝色
|
|
rejected: 'danger', // 已拒绝 - 红色
|
|
processing: 'warning', // 待处理 - 橙色
|
|
readyToShip: 'primary', // 待发货 - 主色调
|
|
shipped: 'success' // 已发货 - 绿色
|
|
}
|
|
return statusMap[status] || 'info'
|
|
}
|
|
|
|
const formatDate = (dateString) => {
|
|
const date = new Date(dateString)
|
|
return date.toLocaleString('zh-CN')
|
|
}
|
|
|
|
const handleExportOrders = () => {
|
|
ElMessage.success('Orders exported successfully')
|
|
}
|
|
|
|
const refresh = () => {
|
|
console.log('Refresh data')
|
|
ElMessage.success('Data refreshed successfully')
|
|
}
|
|
|
|
const handleOrderDetail = (row) => {
|
|
selectedOrder.value = row
|
|
detailDialogVisible.value = true
|
|
}
|
|
|
|
const handleViewOrder = (row) => {
|
|
selectedOrder.value = row
|
|
detailDialogVisible.value = true
|
|
}
|
|
|
|
const handleUpdateStatus = (row) => {
|
|
selectedOrderForStatus.value = row
|
|
statusForm.value.status = row.status
|
|
statusDialogVisible.value = true
|
|
}
|
|
|
|
const handleStatusUpdate = async () => {
|
|
try {
|
|
await ElMessageBox.confirm(
|
|
'确定要更新订单状态吗?',
|
|
'确认更新',
|
|
{
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}
|
|
)
|
|
ElMessage.success('订单状态更新成功')
|
|
statusDialogVisible.value = false
|
|
} catch {
|
|
// 用户取消更新
|
|
}
|
|
}
|
|
|
|
// 确认订单
|
|
const handleConfirmOrder = (row) => {
|
|
// 跳转到订单审核页面
|
|
router.push('/admin/content-review')
|
|
}
|
|
|
|
// 处理订单
|
|
const handleProcessOrder = (row) => {
|
|
// 跳转到拆解订单页面
|
|
router.push('/admin/disassembly-orders')
|
|
}
|
|
|
|
// 发货订单
|
|
const handleShipOrder = (row) => {
|
|
selectedOrder.value = row
|
|
shippingDialogVisible.value = true
|
|
}
|
|
|
|
// 查看物流
|
|
const handleViewLogistics = (row) => {
|
|
selectedOrder.value = row
|
|
logisticsDialogVisible.value = true
|
|
}
|
|
|
|
// 确认发货
|
|
const confirmShipOrder = () => {
|
|
if (!shippingForm.trackingNumber || !shippingForm.carrier) {
|
|
ElMessage.warning('请填写必要的发货信息')
|
|
return
|
|
}
|
|
|
|
// 更新订单状态
|
|
const orderIndex = orders.value.findIndex(order => order.id === selectedOrder.value.id)
|
|
if (orderIndex !== -1) {
|
|
orders.value[orderIndex].status = 'shipped'
|
|
orders.value[orderIndex].trackingNumber = shippingForm.trackingNumber
|
|
orders.value[orderIndex].carrier = shippingForm.carrier
|
|
}
|
|
|
|
ElMessage.success('发货成功')
|
|
shippingDialogVisible.value = false
|
|
|
|
// 重置表单
|
|
shippingForm.trackingNumber = ''
|
|
shippingForm.carrier = ''
|
|
shippingForm.note = ''
|
|
}
|
|
|
|
const handleSizeChange = (val) => {
|
|
pageSize.value = val
|
|
currentPage.value = 1
|
|
}
|
|
|
|
const handleCurrentChange = (val) => {
|
|
currentPage.value = val
|
|
}
|
|
|
|
// 多选处理函数
|
|
const handleSelectionChange = (selection) => {
|
|
selectedOrders.value = selection
|
|
}
|
|
|
|
|
|
|
|
// 生命周期
|
|
onMounted(() => {
|
|
totalOrders.value = ordersList.value.length
|
|
})
|
|
|
|
return {
|
|
t,
|
|
loading,
|
|
searchQuery,
|
|
selectedStatus,
|
|
dateRange,
|
|
currentPage,
|
|
pageSize,
|
|
totalOrders,
|
|
detailDialogVisible,
|
|
statusDialogVisible,
|
|
shippingDialogVisible,
|
|
logisticsDialogVisible,
|
|
selectedOrder,
|
|
selectedOrderForStatus,
|
|
selectedOrders,
|
|
statusForm,
|
|
shippingForm,
|
|
logisticsActivities,
|
|
ordersList,
|
|
orderStats,
|
|
filteredOrdersList,
|
|
availableStatuses,
|
|
getStatusTagType,
|
|
formatDate,
|
|
handleExportOrders,
|
|
refresh,
|
|
handleOrderDetail,
|
|
handleViewOrder,
|
|
handleUpdateStatus,
|
|
handleStatusUpdate,
|
|
handleConfirmOrder,
|
|
handleProcessOrder,
|
|
handleShipOrder,
|
|
handleViewLogistics,
|
|
confirmShipOrder,
|
|
handleSizeChange,
|
|
handleCurrentChange,
|
|
handleSelectionChange
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.admin-orders {
|
|
padding: 20px;
|
|
background-color: #f8fafc;
|
|
min-height: calc(100vh - 60px);
|
|
}
|
|
|
|
/* 统计卡片样式 */
|
|
.order-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.stat-card {
|
|
background: white;
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #e5e7eb;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
.stat-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 24px;
|
|
color: white;
|
|
}
|
|
|
|
.stat-icon.total {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
}
|
|
|
|
.stat-icon.pending {
|
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
}
|
|
|
|
.stat-icon.completed {
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
}
|
|
|
|
.stat-icon.revenue {
|
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 32px;
|
|
font-weight: 700;
|
|
color: #1f2937;
|
|
line-height: 1;
|
|
}
|
|
|
|
.stat-label {
|
|
color: #6b7280;
|
|
font-size: 14px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* 筛选器样式 */
|
|
.orders-filters {
|
|
padding: 20px;
|
|
border-radius: 12px;
|
|
margin-bottom: 24px;
|
|
display: flex;
|
|
gap: 12px;
|
|
align-items: flex-end;
|
|
justify-content: flex-end;
|
|
overflow-x: auto;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.filter-group {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: flex-end;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.search-group {
|
|
flex: 1;
|
|
min-width: 200px;
|
|
max-width: 300px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.filter-actions {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
flex-shrink: 0;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* 订单列表样式 */
|
|
.orders-list {
|
|
background: white;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
border: 1px solid #e5e7eb;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
/* 表格样式优化 */
|
|
.orders-list :deep(.el-table) {
|
|
width: 100% !important;
|
|
table-layout: auto;
|
|
}
|
|
|
|
.orders-list :deep(.el-table__header) {
|
|
width: 100% !important;
|
|
}
|
|
|
|
.orders-list :deep(.el-table__body) {
|
|
width: 100% !important;
|
|
}
|
|
|
|
.orders-list :deep(.el-table__cell) {
|
|
padding: 8px 12px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.orders-list :deep(.el-table__header-wrapper) {
|
|
background-color: #f8fafc;
|
|
}
|
|
|
|
.orders-list :deep(.el-table__header th) {
|
|
background-color: #f8fafc;
|
|
color: #374151;
|
|
font-weight: 600;
|
|
border-bottom: 2px solid #e5e7eb;
|
|
}
|
|
|
|
/* 操作按钮容器 */
|
|
.actions-container {
|
|
display: flex;
|
|
gap: 6px;
|
|
flex-wrap: nowrap;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.actions-container .el-button {
|
|
flex-shrink: 0;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 分页样式 */
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 16px 0;
|
|
}
|
|
|
|
/* 订单详情样式 */
|
|
.order-detail {
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.detail-section {
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.detail-section h4 {
|
|
margin: 0 0 16px 0;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #1f2937;
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 1024px) {
|
|
.order-stats {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.orders-filters {
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.admin-orders {
|
|
padding: 16px;
|
|
}
|
|
|
|
.order-stats {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.orders-filters {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: 12px;
|
|
}
|
|
|
|
.filter-group {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.search-group {
|
|
min-width: auto;
|
|
max-width: none;
|
|
}
|
|
|
|
.filter-actions {
|
|
justify-content: center;
|
|
}
|
|
|
|
/* 移动端表格优化 */
|
|
.orders-list {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.orders-list :deep(.el-table) {
|
|
min-width: 800px;
|
|
}
|
|
|
|
.actions-container {
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 769px) and (max-width: 1024px) {
|
|
.order-stats {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.stat-card {
|
|
min-width: calc(50% - 8px);
|
|
}
|
|
|
|
.orders-filters {
|
|
flex-wrap: wrap;
|
|
}
|
|
}
|
|
</style> |