1381 lines
28 KiB
Plaintext
1381 lines
28 KiB
Plaintext
<!-- 订单管理页面 - 基于CRMEB设计 -->
|
|
<template>
|
|
<view class="order-management">
|
|
<!-- 订单类型标签页 -->
|
|
<view class="order-tabs">
|
|
<view class="tab-list">
|
|
<view
|
|
v-for="tab in orderTabs"
|
|
:key="tab.type"
|
|
class="tab-item"
|
|
:class="{ active: activeTab === tab.type }"
|
|
@click="switchTab(tab.type)"
|
|
>
|
|
<text class="tab-text">{{ tab.label }}</text>
|
|
<text v-if="tab.value > 0" class="tab-count">{{ tab.value }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 搜索和筛选区域 -->
|
|
<view class="search-section">
|
|
<view class="search-form">
|
|
<view class="search-row">
|
|
<view class="form-item">
|
|
<text class="label">订单搜索:</text>
|
|
<view class="input-group">
|
|
<picker mode="selector" :range="searchTypes" range-key="label" @change="onSearchTypeChange">
|
|
<view class="search-type">{{ searchTypes[currentSearchType].label }}</view>
|
|
</picker>
|
|
<input
|
|
v-model="searchForm.keyword"
|
|
placeholder="请输入搜索内容"
|
|
clearable
|
|
class="search-input"
|
|
/>
|
|
</view>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">订单状态:</text>
|
|
<picker mode="selector" :range="orderStatuses" range-key="label" @change="onOrderStatusChange">
|
|
<view class="picker-text">{{ selectedOrderStatus || '全部' }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">支付方式:</text>
|
|
<picker mode="selector" :range="payTypes" range-key="label" @change="onPayTypeChange">
|
|
<view class="picker-text">{{ selectedPayType || '全部' }}</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 高级搜索选项 -->
|
|
<view v-if="showAdvancedSearch" class="search-row advanced">
|
|
<view class="form-item">
|
|
<text class="label">下单时间:</text>
|
|
<view class="date-range">
|
|
<picker mode="date" @change="onStartDateChange">
|
|
<view class="date-picker">{{ startDate || '开始日期' }}</view>
|
|
</picker>
|
|
<text class="date-separator">至</text>
|
|
<picker mode="date" @change="onEndDateChange">
|
|
<view class="date-picker">{{ endDate || '结束日期' }}</view>
|
|
</picker>
|
|
</view>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">订单金额:</text>
|
|
<view class="number-range">
|
|
<input
|
|
v-model="searchForm.amountMin"
|
|
placeholder="最小金额"
|
|
type="number"
|
|
class="number-input"
|
|
/>
|
|
<text class="range-separator">~</text>
|
|
<input
|
|
v-model="searchForm.amountMax"
|
|
placeholder="最大金额"
|
|
type="number"
|
|
class="number-input"
|
|
/>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-actions">
|
|
<button class="btn btn-primary" @click="handleSearch">搜索</button>
|
|
<button class="btn btn-default" @click="handleReset">重置</button>
|
|
<button class="btn btn-success" @click="exportOrders">导出订单</button>
|
|
<text class="toggle-search" @click="showAdvancedSearch = !showAdvancedSearch">
|
|
{{ showAdvancedSearch ? '收起' : '展开' }} <text class="icon">{{ showAdvancedSearch ? '▲' : '▼' }}</text>
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 数据统计 -->
|
|
<view class="stats-section">
|
|
<view class="stats-grid">
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ stats.today_orders }}</text>
|
|
<text class="stat-label">今日订单</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">¥{{ stats.today_sales }}</text>
|
|
<text class="stat-label">今日销售额</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ stats.pending_orders }}</text>
|
|
<text class="stat-label">待发货</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{ stats.return_orders }}</text>
|
|
<text class="stat-label">退款订单</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 订单列表 -->
|
|
<view class="order-list">
|
|
<!-- 表头 -->
|
|
<view class="table-header">
|
|
<view class="table-row">
|
|
<view class="table-cell order-cell">订单信息</view>
|
|
<view class="table-cell">收货信息</view>
|
|
<view class="table-cell">订单金额</view>
|
|
<view class="table-cell">订单状态</view>
|
|
<view class="table-cell">下单时间</view>
|
|
<view class="table-cell">操作</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 表格内容 -->
|
|
<view class="table-body">
|
|
<view v-for="order in orderList" :key="order.id" class="table-row data-row" @click="viewOrderDetail(order.id)">
|
|
<view class="table-cell order-cell">
|
|
<view class="order-info">
|
|
<text class="order-sn">{{ order.order_sn }}</text>
|
|
<view class="order-products">
|
|
<view v-for="item in order.items.slice(0, 2)" :key="item.id" class="product-item">
|
|
<image :src="item.product_image" class="product-image" />
|
|
<text class="product-name">{{ item.product_title }}</text>
|
|
<text class="product-quantity">x{{ item.quantity }}</text>
|
|
</view>
|
|
<text v-if="order.items.length > 2" class="more-products">等{{ order.items.length }}件商品</text>
|
|
</view>
|
|
<text class="user-info">买家:{{ order.user_nickname }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="table-cell">
|
|
<view class="address-info">
|
|
<text class="receiver">{{ order.address_info?.receiver }}</text>
|
|
<text class="phone">{{ order.address_info?.phone }}</text>
|
|
<text class="address">{{ order.address_info?.address }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="table-cell">
|
|
<view class="amount-info">
|
|
<text class="total-amount">¥{{ order.total_price }}</text>
|
|
<text v-if="order.pay_price !== order.total_price" class="pay-amount">(实付: ¥{{ order.pay_price }})</text>
|
|
<text class="pay-type">{{ getPayTypeText(order.pay_type) }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="table-cell">
|
|
<view class="status-info">
|
|
<text class="status-tag" :class="'status-' + order.status">{{ getOrderStatusText(order.status) }}</text>
|
|
<text v-if="order.status === 1 && !order.paid" class="unpaid-mark">未支付</text>
|
|
</view>
|
|
</view>
|
|
<view class="table-cell">
|
|
<text class="order-time">{{ formatDateTime(order.created_at) }}</text>
|
|
</view>
|
|
<view class="table-cell action-cell">
|
|
<view class="action-buttons">
|
|
<text class="action-link" @click.stop="viewOrderDetail(order.id)">详情</text>
|
|
<text v-if="order.status === 0" class="action-link success" @click.stop="confirmOrder(order.id)">确认订单</text>
|
|
<text v-if="order.status === 1" class="action-link primary" @click.stop="shipOrder(order.id)">发货</text>
|
|
<text v-if="order.status === 2" class="action-link info" @click.stop="viewLogistics(order.id)">物流</text>
|
|
<text class="action-link" @click.stop="editOrder(order.id)">编辑</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 分页 -->
|
|
<view class="pagination">
|
|
<view class="page-buttons">
|
|
<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="showShipModal" class="modal-overlay" @click="closeShipModal">
|
|
<view class="modal-content ship-modal" @click.stop>
|
|
<view class="modal-header">
|
|
<text class="modal-title">订单发货</text>
|
|
<text class="modal-close" @click="closeShipModal">✕</text>
|
|
</view>
|
|
<view class="modal-body">
|
|
<view class="form-item">
|
|
<text class="label">物流公司:</text>
|
|
<picker mode="selector" :range="expressCompanies" range-key="name" @change="onExpressChange">
|
|
<view class="picker-text">{{ selectedExpress || '选择物流公司' }}</view>
|
|
</picker>
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">物流单号:</text>
|
|
<input v-model="shipForm.express_number" placeholder="请输入物流单号" class="input-field" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">发货备注:</text>
|
|
<textarea v-model="shipForm.remark" placeholder="发货备注(可选)" class="textarea-field" />
|
|
</view>
|
|
</view>
|
|
<view class="modal-footer">
|
|
<button class="btn btn-default" @click="closeShipModal">取消</button>
|
|
<button class="btn btn-primary" @click="confirmShip">确认发货</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, onMounted, computed } from 'vue'
|
|
import supa from '@/components/supadb/aksupainstance.uts'
|
|
|
|
// 响应式数据
|
|
const showAdvancedSearch = ref(false)
|
|
const showShipModal = ref(false)
|
|
const activeTab = ref('')
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(20)
|
|
const totalOrders = ref(0)
|
|
const totalPages = ref(0)
|
|
|
|
// 订单类型标签页
|
|
const orderTabs = ref([
|
|
{ type: '', label: '全部订单', value: 0 },
|
|
{ type: '0', label: '普通订单', value: 0 },
|
|
{ type: '1', label: '待支付', value: 0 },
|
|
{ type: '2', label: '待发货', value: 0 },
|
|
{ type: '3', label: '已发货', value: 0 },
|
|
{ type: '4', label: '已完成', value: 0 },
|
|
{ type: '5', label: '退款中', value: 0 }
|
|
])
|
|
|
|
// 搜索表单
|
|
const searchForm = ref({
|
|
keyword: '',
|
|
field: 'order_sn', // order_sn, user, receiver
|
|
amountMin: '',
|
|
amountMax: ''
|
|
})
|
|
|
|
const currentSearchType = ref(0)
|
|
const searchTypes = ref([
|
|
{ value: 'order_sn', label: '订单号' },
|
|
{ value: 'user', label: '用户名' },
|
|
{ value: 'receiver', label: '收货人' }
|
|
])
|
|
|
|
// 筛选选项
|
|
const orderStatuses = ref([
|
|
{ value: '', label: '全部' },
|
|
{ value: '0', label: '待确认' },
|
|
{ value: '1', label: '待支付' },
|
|
{ value: '2', label: '待发货' },
|
|
{ value: '3', label: '已发货' },
|
|
{ value: '4', label: '已完成' },
|
|
{ value: '5', label: '已取消' },
|
|
{ value: '6', label: '退款中' }
|
|
])
|
|
|
|
const payTypes = ref([
|
|
{ value: '', label: '全部' },
|
|
{ value: '1', label: '微信支付' },
|
|
{ value: '2', label: '余额支付' },
|
|
{ value: '3', label: '线下支付' },
|
|
{ value: '4', label: '支付宝' }
|
|
])
|
|
|
|
// 选中的筛选值
|
|
const selectedOrderStatus = ref('')
|
|
const selectedPayType = ref('')
|
|
const startDate = ref('')
|
|
const endDate = ref('')
|
|
|
|
// 订单列表和统计
|
|
const orderList = ref([])
|
|
const stats = ref({
|
|
today_orders: 0,
|
|
today_sales: '0.00',
|
|
pending_orders: 0,
|
|
return_orders: 0
|
|
})
|
|
|
|
// 发货相关
|
|
const shipForm = ref({
|
|
order_id: 0,
|
|
express_company: '',
|
|
express_number: '',
|
|
remark: ''
|
|
})
|
|
const expressCompanies = ref([])
|
|
const selectedExpress = ref('')
|
|
|
|
// 计算属性
|
|
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 switchTab = (tabType: string) => {
|
|
activeTab.value = tabType
|
|
currentPage.value = 1
|
|
loadOrderList()
|
|
loadOrderStats()
|
|
}
|
|
|
|
const onSearchTypeChange = (e: any) => {
|
|
currentSearchType.value = e.detail.value
|
|
searchForm.value.field = searchTypes.value[currentSearchType.value].value
|
|
}
|
|
|
|
const onOrderStatusChange = (e: any) => {
|
|
selectedOrderStatus.value = orderStatuses.value[e.detail.value].label
|
|
searchForm.value.status = orderStatuses.value[e.detail.value].value
|
|
}
|
|
|
|
const onPayTypeChange = (e: any) => {
|
|
selectedPayType.value = payTypes.value[e.detail.value].label
|
|
searchForm.value.pay_type = payTypes.value[e.detail.value].value
|
|
}
|
|
|
|
const onStartDateChange = (e: any) => {
|
|
startDate.value = e.detail.value
|
|
searchForm.value.start_date = e.detail.value
|
|
}
|
|
|
|
const onEndDateChange = (e: any) => {
|
|
endDate.value = e.detail.value
|
|
searchForm.value.end_date = e.detail.value
|
|
}
|
|
|
|
const onExpressChange = (e: any) => {
|
|
selectedExpress.value = expressCompanies.value[e.detail.value].name
|
|
shipForm.value.express_company = expressCompanies.value[e.detail.value].code
|
|
}
|
|
|
|
const handleSearch = () => {
|
|
loadOrderList()
|
|
}
|
|
|
|
const handleReset = () => {
|
|
searchForm.value = {
|
|
keyword: '',
|
|
field: 'order_sn',
|
|
amountMin: '',
|
|
amountMax: ''
|
|
}
|
|
currentSearchType.value = 0
|
|
selectedOrderStatus.value = ''
|
|
selectedPayType.value = ''
|
|
startDate.value = ''
|
|
endDate.value = ''
|
|
loadOrderList()
|
|
}
|
|
|
|
const exportOrders = () => {
|
|
uni.showToast({
|
|
title: '导出功能开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
|
|
const viewOrderDetail = (orderId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/order-detail?id=${orderId}`
|
|
})
|
|
}
|
|
|
|
const confirmOrder = async (orderId: number) => {
|
|
try {
|
|
await supa.from('orders').update({ status: 1 }).eq('id', orderId)
|
|
uni.showToast({
|
|
title: '确认成功',
|
|
icon: 'success'
|
|
})
|
|
loadOrderList()
|
|
loadOrderStats()
|
|
} catch (error) {
|
|
console.error('确认订单失败:', error)
|
|
uni.showToast({
|
|
title: '操作失败',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const shipOrder = (orderId: number) => {
|
|
shipForm.value.order_id = orderId
|
|
showShipModal.value = true
|
|
}
|
|
|
|
const closeShipModal = () => {
|
|
showShipModal.value = false
|
|
shipForm.value = {
|
|
order_id: 0,
|
|
express_company: '',
|
|
express_number: '',
|
|
remark: ''
|
|
}
|
|
selectedExpress.value = ''
|
|
}
|
|
|
|
const confirmShip = async () => {
|
|
if (!shipForm.value.express_company || !shipForm.value.express_number) {
|
|
uni.showToast({
|
|
title: '请填写完整信息',
|
|
icon: 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
try {
|
|
// 更新订单状态为已发货
|
|
await supa.from('orders').update({
|
|
status: 3,
|
|
ship_info: {
|
|
express_company: shipForm.value.express_company,
|
|
express_number: shipForm.value.express_number,
|
|
remark: shipForm.value.remark,
|
|
ship_time: new Date().toISOString()
|
|
}
|
|
}).eq('id', shipForm.value.order_id)
|
|
|
|
uni.showToast({
|
|
title: '发货成功',
|
|
icon: 'success'
|
|
})
|
|
|
|
closeShipModal()
|
|
loadOrderList()
|
|
loadOrderStats()
|
|
} catch (error) {
|
|
console.error('发货失败:', error)
|
|
uni.showToast({
|
|
title: '发货失败',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const viewLogistics = (orderId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/order-logistics?id=${orderId}`
|
|
})
|
|
}
|
|
|
|
const editOrder = (orderId: number) => {
|
|
uni.navigateTo({
|
|
url: `/pages/mall/admin/order-edit?id=${orderId}`
|
|
})
|
|
}
|
|
|
|
const goToPage = (page: number) => {
|
|
if (page >= 1 && page <= totalPages.value) {
|
|
currentPage.value = page
|
|
loadOrderList()
|
|
}
|
|
}
|
|
|
|
const getOrderStatusText = (status: number) => {
|
|
const statusMap = {
|
|
0: '待确认',
|
|
1: '待支付',
|
|
2: '待发货',
|
|
3: '已发货',
|
|
4: '已完成',
|
|
5: '已取消',
|
|
6: '退款中'
|
|
}
|
|
return statusMap[status] || '未知状态'
|
|
}
|
|
|
|
const getPayTypeText = (payType: number) => {
|
|
const payTypeMap = {
|
|
1: '微信支付',
|
|
2: '余额支付',
|
|
3: '线下支付',
|
|
4: '支付宝'
|
|
}
|
|
return payTypeMap[payType] || '未知支付'
|
|
}
|
|
|
|
const formatDateTime = (dateStr: string) => {
|
|
if (!dateStr) return ''
|
|
const date = new Date(dateStr)
|
|
return date.toLocaleString()
|
|
}
|
|
|
|
// 数据加载方法
|
|
const loadOrderStats = async () => {
|
|
try {
|
|
const { data } = await supa.rpc('get_order_stats')
|
|
if (data) {
|
|
stats.value = data
|
|
// 更新标签页数量
|
|
orderTabs.value[0].value = data.total_orders || 0
|
|
orderTabs.value[1].value = data.general_orders || 0
|
|
orderTabs.value[2].value = data.pending_pay_orders || 0
|
|
orderTabs.value[3].value = data.pending_ship_orders || 0
|
|
orderTabs.value[4].value = data.shipped_orders || 0
|
|
orderTabs.value[5].value = data.completed_orders || 0
|
|
orderTabs.value[6].value = data.return_orders || 0
|
|
}
|
|
} catch (error) {
|
|
console.error('加载订单统计失败:', error)
|
|
}
|
|
}
|
|
|
|
const loadOrderList = async () => {
|
|
try {
|
|
let query = supa.from('orders').select(`
|
|
*,
|
|
users!inner(nickname),
|
|
order_items(
|
|
id,
|
|
product_title,
|
|
product_image,
|
|
quantity
|
|
)
|
|
`).order('created_at', { ascending: false })
|
|
|
|
// 订单类型筛选
|
|
if (activeTab.value) {
|
|
query = query.eq('status', parseInt(activeTab.value))
|
|
}
|
|
|
|
// 搜索条件
|
|
if (searchForm.value.keyword) {
|
|
switch (searchForm.value.field) {
|
|
case 'order_sn':
|
|
query = query.ilike('order_sn', `%${searchForm.value.keyword}%`)
|
|
break
|
|
case 'user':
|
|
query = query.ilike('users.nickname', `%${searchForm.value.keyword}%`)
|
|
break
|
|
case 'receiver':
|
|
query = query.ilike('address_info->receiver', `%${searchForm.value.keyword}%`)
|
|
break
|
|
}
|
|
}
|
|
|
|
// 状态筛选
|
|
if (searchForm.value.status) {
|
|
query = query.eq('status', parseInt(searchForm.value.status))
|
|
}
|
|
|
|
// 支付方式筛选
|
|
if (searchForm.value.pay_type) {
|
|
query = query.eq('pay_type', parseInt(searchForm.value.pay_type))
|
|
}
|
|
|
|
// 日期筛选
|
|
if (searchForm.value.start_date && searchForm.value.end_date) {
|
|
query = query.gte('created_at', searchForm.value.start_date + ' 00:00:00')
|
|
.lte('created_at', searchForm.value.end_date + ' 23:59:59')
|
|
}
|
|
|
|
// 分页
|
|
const from = (currentPage.value - 1) * pageSize.value
|
|
const to = from + pageSize.value - 1
|
|
|
|
const { data, count } = await query.range(from, to)
|
|
orderList.value = data || []
|
|
totalOrders.value = count || 0
|
|
totalPages.value = Math.ceil(totalOrders.value / pageSize.value)
|
|
|
|
} catch (error) {
|
|
console.error('加载订单列表失败:', error)
|
|
uni.showToast({
|
|
title: '加载失败',
|
|
icon: 'error'
|
|
})
|
|
}
|
|
}
|
|
|
|
const loadExpressCompanies = async () => {
|
|
try {
|
|
const { data } = await supa.from('express_companies').select('*')
|
|
expressCompanies.value = data || []
|
|
} catch (error) {
|
|
console.error('加载物流公司失败:', error)
|
|
}
|
|
}
|
|
|
|
// 页面初始化
|
|
onMounted(async () => {
|
|
await Promise.all([
|
|
loadOrderStats(),
|
|
loadOrderList(),
|
|
loadExpressCompanies()
|
|
])
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.order-management {
|
|
padding: 30rpx;
|
|
background-color: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
// 订单类型标签页样式
|
|
.order-tabs {
|
|
background-color: #fff;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 30rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
|
.tab-list {
|
|
display: flex;
|
|
padding: 0 30rpx;
|
|
}
|
|
|
|
.tab-item {
|
|
position: relative;
|
|
padding: 30rpx 20rpx;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:not(:last-child)::after {
|
|
content: '';
|
|
position: absolute;
|
|
right: 0;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
width: 1rpx;
|
|
height: 40rpx;
|
|
background-color: #e9ecef;
|
|
}
|
|
|
|
&.active {
|
|
background-color: #007bff;
|
|
color: white;
|
|
|
|
.tab-text {
|
|
color: white;
|
|
}
|
|
}
|
|
|
|
.tab-text {
|
|
font-size: 28rpx;
|
|
color: #495057;
|
|
}
|
|
|
|
.tab-count {
|
|
display: inline-block;
|
|
margin-left: 10rpx;
|
|
padding: 2rpx 8rpx;
|
|
background-color: #dc3545;
|
|
color: white;
|
|
border-radius: 20rpx;
|
|
font-size: 20rpx;
|
|
min-width: 32rpx;
|
|
text-align: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 搜索区域样式
|
|
.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-form {
|
|
.search-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 20rpx;
|
|
margin-bottom: 20rpx;
|
|
|
|
&.advanced {
|
|
border-top: 1rpx solid #e8e8e8;
|
|
padding-top: 20rpx;
|
|
}
|
|
}
|
|
|
|
.form-item {
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: 300rpx;
|
|
margin-bottom: 20rpx;
|
|
|
|
.label {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
margin-right: 20rpx;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
|
|
.input-group {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
|
|
.search-type {
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
background-color: #f5f5f5;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx 0 0 6rpx;
|
|
font-size: 26rpx;
|
|
min-width: 120rpx;
|
|
}
|
|
|
|
.search-input {
|
|
flex: 1;
|
|
height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-left: none;
|
|
border-radius: 0 6rpx 6rpx 0;
|
|
padding: 0 20rpx;
|
|
font-size: 26rpx;
|
|
}
|
|
}
|
|
|
|
.picker-text {
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
min-width: 200rpx;
|
|
}
|
|
|
|
.date-range {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
gap: 10rpx;
|
|
|
|
.date-picker {
|
|
flex: 1;
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.date-separator {
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.number-range {
|
|
display: flex;
|
|
align-items: center;
|
|
flex: 1;
|
|
gap: 10rpx;
|
|
|
|
.number-input {
|
|
flex: 1;
|
|
height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.range-separator {
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 20rpx;
|
|
margin-top: 20rpx;
|
|
|
|
.btn {
|
|
padding: 12rpx 24rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
cursor: pointer;
|
|
|
|
&.btn-primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
&.btn-default {
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
}
|
|
|
|
&.btn-success {
|
|
background-color: #28a745;
|
|
color: white;
|
|
}
|
|
}
|
|
|
|
.toggle-search {
|
|
margin-left: auto;
|
|
color: #007bff;
|
|
font-size: 26rpx;
|
|
cursor: pointer;
|
|
|
|
.icon {
|
|
font-size: 20rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 统计区域样式
|
|
.stats-section {
|
|
background-color: #fff;
|
|
border-radius: 12rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 30rpx;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
|
|
.stats-grid {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 20rpx;
|
|
background-color: #f8f9fa;
|
|
border-radius: 8rpx;
|
|
flex: 1;
|
|
|
|
.stat-value {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #007bff;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 订单列表样式
|
|
.order-list {
|
|
background-color: #fff;
|
|
border-radius: 12rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.table-header {
|
|
background-color: #f8f9fa;
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
font-weight: bold;
|
|
font-size: 26rpx;
|
|
color: #495057;
|
|
}
|
|
}
|
|
|
|
.table-body {
|
|
.data-row {
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
|
|
&:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20rpx 30rpx;
|
|
min-height: 120rpx;
|
|
|
|
.table-cell {
|
|
flex: 1;
|
|
font-size: 26rpx;
|
|
color: #495057;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&.order-cell {
|
|
flex: 3;
|
|
}
|
|
|
|
&.action-cell {
|
|
flex: 0 0 200rpx;
|
|
justify-content: flex-end;
|
|
}
|
|
}
|
|
}
|
|
|
|
.order-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12rpx;
|
|
flex: 1;
|
|
|
|
.order-sn {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #007bff;
|
|
}
|
|
|
|
.order-products {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8rpx;
|
|
|
|
.product-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12rpx;
|
|
|
|
.product-image {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
border-radius: 4rpx;
|
|
flex-shrink: 0;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.product-name {
|
|
flex: 1;
|
|
font-size: 24rpx;
|
|
color: #495057;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.product-quantity {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
}
|
|
}
|
|
|
|
.more-products {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
font-style: italic;
|
|
}
|
|
}
|
|
|
|
.user-info {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
}
|
|
}
|
|
|
|
.address-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4rpx;
|
|
|
|
.receiver {
|
|
font-size: 26rpx;
|
|
font-weight: bold;
|
|
color: #212529;
|
|
}
|
|
|
|
.phone {
|
|
font-size: 24rpx;
|
|
color: #007bff;
|
|
}
|
|
|
|
.address {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
line-height: 1.4;
|
|
}
|
|
}
|
|
|
|
.amount-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4rpx;
|
|
|
|
.total-amount {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #e74c3c;
|
|
}
|
|
|
|
.pay-amount {
|
|
font-size: 24rpx;
|
|
color: #28a745;
|
|
}
|
|
|
|
.pay-type {
|
|
font-size: 24rpx;
|
|
color: #6c757d;
|
|
}
|
|
}
|
|
|
|
.status-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8rpx;
|
|
align-items: flex-start;
|
|
|
|
.status-tag {
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 12rpx;
|
|
font-size: 24rpx;
|
|
font-weight: bold;
|
|
|
|
&.status-0 {
|
|
background-color: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
|
|
&.status-1 {
|
|
background-color: #cce5ff;
|
|
color: #0066cc;
|
|
}
|
|
|
|
&.status-2 {
|
|
background-color: #d1ecf1;
|
|
color: #0c5460;
|
|
}
|
|
|
|
&.status-3 {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
&.status-4 {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
&.status-5 {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
|
|
&.status-6 {
|
|
background-color: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
}
|
|
|
|
.unpaid-mark {
|
|
font-size: 20rpx;
|
|
color: #dc3545;
|
|
background-color: #f8d7da;
|
|
padding: 2rpx 6rpx;
|
|
border-radius: 4rpx;
|
|
}
|
|
}
|
|
|
|
.order-time {
|
|
color: #6c757d;
|
|
font-size: 24rpx;
|
|
}
|
|
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
flex-wrap: wrap;
|
|
|
|
.action-link {
|
|
color: #007bff;
|
|
font-size: 24rpx;
|
|
cursor: pointer;
|
|
padding: 4rpx 8rpx;
|
|
border-radius: 4rpx;
|
|
transition: all 0.2s;
|
|
|
|
&:hover {
|
|
background-color: #e3f2fd;
|
|
}
|
|
|
|
&.success {
|
|
color: #28a745;
|
|
|
|
&:hover {
|
|
background-color: #d4edda;
|
|
}
|
|
}
|
|
|
|
&.primary {
|
|
color: #007bff;
|
|
|
|
&:hover {
|
|
background-color: #cce5ff;
|
|
}
|
|
}
|
|
|
|
&.info {
|
|
color: #17a2b8;
|
|
|
|
&:hover {
|
|
background-color: #d1ecf1;
|
|
}
|
|
}
|
|
|
|
&.danger {
|
|
color: #dc3545;
|
|
|
|
&:hover {
|
|
background-color: #f8d7da;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 分页样式
|
|
.pagination {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 30rpx;
|
|
|
|
.page-buttons {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
}
|
|
|
|
.page-btn,
|
|
.page-number {
|
|
padding: 12rpx 20rpx;
|
|
border: 1rpx solid #ddd;
|
|
background-color: #fff;
|
|
color: #333;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:disabled {
|
|
background-color: #f5f5f5;
|
|
color: #999;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
&:hover:not(:disabled) {
|
|
background-color: #007bff;
|
|
color: white;
|
|
border-color: #007bff;
|
|
}
|
|
|
|
&.active {
|
|
background-color: #007bff;
|
|
color: white;
|
|
border-color: #007bff;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 弹窗样式
|
|
.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: 80%;
|
|
max-width: 500rpx;
|
|
max-height: 80vh;
|
|
overflow: hidden;
|
|
|
|
&.ship-modal {
|
|
max-width: 600rpx;
|
|
}
|
|
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 30rpx;
|
|
border-bottom: 1rpx solid #e9ecef;
|
|
|
|
.modal-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #212529;
|
|
}
|
|
|
|
.modal-close {
|
|
font-size: 36rpx;
|
|
color: #999;
|
|
cursor: pointer;
|
|
|
|
&:hover {
|
|
color: #333;
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-body {
|
|
padding: 30rpx;
|
|
|
|
.form-item {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 20rpx;
|
|
|
|
.label {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
margin-right: 20rpx;
|
|
white-space: nowrap;
|
|
min-width: 120rpx;
|
|
}
|
|
|
|
.picker-text {
|
|
flex: 1;
|
|
padding: 0 20rpx;
|
|
height: 60rpx;
|
|
line-height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.input-field {
|
|
flex: 1;
|
|
height: 60rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 26rpx;
|
|
}
|
|
|
|
.textarea-field {
|
|
flex: 1;
|
|
min-height: 100rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 6rpx;
|
|
padding: 20rpx;
|
|
font-size: 26rpx;
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 20rpx;
|
|
padding: 30rpx;
|
|
border-top: 1rpx solid #e9ecef;
|
|
|
|
.btn {
|
|
padding: 12rpx 24rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 26rpx;
|
|
border: none;
|
|
cursor: pointer;
|
|
|
|
&.btn-default {
|
|
background-color: #f5f5f5;
|
|
color: #333;
|
|
}
|
|
|
|
&.btn-primary {
|
|
background-color: #007bff;
|
|
color: white;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 响应式设计
|
|
@media (max-width: 750rpx) {
|
|
.order-tabs {
|
|
.tab-list {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.tab-item {
|
|
flex: 1;
|
|
min-width: 150rpx;
|
|
padding: 20rpx 10rpx;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
.search-row {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.form-item {
|
|
min-width: auto;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.stats-grid {
|
|
flex-direction: column;
|
|
gap: 15rpx;
|
|
}
|
|
|
|
.table-row {
|
|
flex-wrap: wrap;
|
|
padding: 15rpx;
|
|
|
|
.table-cell {
|
|
min-width: 200rpx;
|
|
margin-bottom: 10rpx;
|
|
|
|
&.order-cell {
|
|
min-width: 300rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.action-buttons {
|
|
justify-content: center;
|
|
}
|
|
}
|
|
</style>
|