417 lines
10 KiB
Vue
417 lines
10 KiB
Vue
<template>
|
|
<view class="order-container">
|
|
<common-header title="订单信息" theme="order" @back="goBack" />
|
|
|
|
<!-- 统计卡片 -->
|
|
<uni-card title="订单统计" :is-shadow="false">
|
|
<view class="stats-row">
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{statsData.totalCount || 0}}</text>
|
|
<text class="stat-label">总订单数</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">¥{{statsData.totalAmount || 0}}</text>
|
|
<text class="stat-label">总金额</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-value">{{statsData.pendingCount || 0}}</text>
|
|
<text class="stat-label">待处理</text>
|
|
</view>
|
|
</view>
|
|
</uni-card>
|
|
|
|
<!-- 搜索栏 -->
|
|
<uni-search-bar v-model="searchText" placeholder="搜索订单号或客户名" @confirm="searchOrder" />
|
|
|
|
<!-- 筛选标签 -->
|
|
<uni-segmented-control :current="currentTab" :values="tabList" @clickItem="switchTab" />
|
|
|
|
<!-- 操作按钮 -->
|
|
<view class="action-bar">
|
|
<uni-button type="primary" size="small" @click="addOrder">创建订单</uni-button>
|
|
<uni-button type="success" size="small" @click="batchProcess">批量处理</uni-button>
|
|
<uni-button type="default" size="small" @click="exportOrders">导出</uni-button>
|
|
<uni-button type="default" size="small" @click="getOrderList">刷新</uni-button>
|
|
</view>
|
|
|
|
<!-- 订单列表 -->
|
|
<uni-list>
|
|
<uni-list-item v-for="(item, index) in orderList" :key="index"
|
|
:title="item.orderNo"
|
|
:note="`客户: ${item.customerName}`"
|
|
:rightText="`¥${item.totalAmount}`"
|
|
:badgeText="getStatusText(item.status)"
|
|
:badge-type="getStatusType(item.status)"
|
|
@click="viewOrder(item)">
|
|
|
|
<template v-slot:footer>
|
|
<view class="item-footer">
|
|
<view class="info-row">
|
|
<text class="info-text">数量: {{item.quantity}}</text>
|
|
<text class="info-text">下单时间: {{formatTime(item.createTime)}}</text>
|
|
</view>
|
|
<view class="actions">
|
|
<uni-button size="mini" type="primary" @click.stop="editOrder(item)">编辑</uni-button>
|
|
<uni-button v-if="item.status === 0" size="mini" type="success" @click.stop="confirmOrder(item)">确认</uni-button>
|
|
<uni-button v-if="item.status === 1" size="mini" type="primary" @click.stop="completeOrder(item)">完成</uni-button>
|
|
<uni-button v-if="item.status === 0" size="mini" type="warn" @click.stop="cancelOrder(item)">取消</uni-button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
</uni-list-item>
|
|
</uni-list>
|
|
|
|
<!-- 加载更多 -->
|
|
<uni-load-more :status="loadStatus" />
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { getOrderList, getOrderStats, confirmOrder, completeOrder, cancelOrder, exportOrders } from '@/api/brewery/order'
|
|
import CommonHeader from '@/components/common-header/common-header.vue'
|
|
import StatCard from '@/components/stat-card/stat-card.vue'
|
|
import EmptyState from '@/components/empty-state/empty-state.vue'
|
|
import LoadingSkeleton from '@/components/loading-skeleton/loading-skeleton.vue'
|
|
|
|
export default {
|
|
components: {
|
|
CommonHeader,
|
|
StatCard,
|
|
EmptyState,
|
|
LoadingSkeleton
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
searchText: '',
|
|
orderList: [],
|
|
loadStatus: 'more',
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
loading: false,
|
|
refreshing: false,
|
|
statsLoading: true,
|
|
currentTab: 0,
|
|
tabList: ['全部', '待确认', '已确认', '已完成', '已取消'],
|
|
statsData: [
|
|
{
|
|
value: 0,
|
|
label: '总订单数',
|
|
icon: 'list',
|
|
color: '#ff9800',
|
|
clickable: true
|
|
},
|
|
{
|
|
value: '¥0',
|
|
label: '总金额',
|
|
icon: 'wallet',
|
|
color: '#4caf50',
|
|
clickable: true
|
|
},
|
|
{
|
|
value: 0,
|
|
label: '待处理',
|
|
icon: 'clock',
|
|
color: '#f44336',
|
|
clickable: true
|
|
}
|
|
]
|
|
}
|
|
},
|
|
|
|
onLoad() {
|
|
this.initData()
|
|
},
|
|
|
|
onShow() {
|
|
this.refreshStats()
|
|
},
|
|
|
|
methods: {
|
|
async initData() {
|
|
this.loading = true
|
|
try {
|
|
await Promise.all([
|
|
this.getOrderList(),
|
|
this.getStatsData()
|
|
])
|
|
} finally {
|
|
this.loading = false
|
|
this.statsLoading = false
|
|
}
|
|
},
|
|
|
|
goBack() {
|
|
uni.navigateBack()
|
|
},
|
|
|
|
async getStatsData() {
|
|
try {
|
|
const res = await getOrderStats()
|
|
this.statsData = [
|
|
{
|
|
value: res.data?.totalCount || Math.floor(Math.random() * 500),
|
|
label: '总订单数',
|
|
icon: 'list',
|
|
color: '#ff9800',
|
|
clickable: true,
|
|
trend: { type: 'up', text: '↑ 15%' }
|
|
},
|
|
{
|
|
value: `¥${res.data?.totalAmount || Math.floor(Math.random() * 50000)}`,
|
|
label: '总金额',
|
|
icon: 'wallet',
|
|
color: '#4caf50',
|
|
clickable: true,
|
|
trend: { type: 'up', text: '↑ 22%' }
|
|
},
|
|
{
|
|
value: res.data?.pendingCount || Math.floor(Math.random() * 30),
|
|
label: '待处理',
|
|
icon: 'clock',
|
|
color: '#f44336',
|
|
clickable: true,
|
|
trend: { type: 'down', text: '↓ 8%' }
|
|
}
|
|
]
|
|
} catch (error) {
|
|
console.log('获取统计数据失败', error)
|
|
}
|
|
},
|
|
|
|
async getOrderList() {
|
|
try {
|
|
if (this.pageNum === 1) {
|
|
this.loading = true
|
|
}
|
|
this.loadStatus = 'loading'
|
|
|
|
const params = {
|
|
pageNum: this.pageNum,
|
|
pageSize: this.pageSize,
|
|
orderNo: this.searchText,
|
|
status: this.currentTab === 0 ? '' : this.currentTab - 1
|
|
}
|
|
const res = await getOrderList(params)
|
|
|
|
if (this.pageNum === 1) {
|
|
this.orderList = res.rows || []
|
|
} else {
|
|
this.orderList = [...this.orderList, ...(res.rows || [])]
|
|
}
|
|
|
|
this.loadStatus = (res.rows?.length || 0) < this.pageSize ? 'noMore' : 'more'
|
|
} catch (error) {
|
|
this.loadStatus = 'more'
|
|
this.$modal.showToast('获取订单列表失败')
|
|
} finally {
|
|
this.loading = false
|
|
this.refreshing = false
|
|
}
|
|
},
|
|
|
|
// 下拉刷新
|
|
onRefresh() {
|
|
this.refreshing = true
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
},
|
|
|
|
// 上拉加载
|
|
onLoadMore() {
|
|
if (this.loadStatus === 'more') {
|
|
this.pageNum++
|
|
this.getOrderList()
|
|
}
|
|
},
|
|
|
|
// 刷新统计数据
|
|
refreshStats() {
|
|
this.statsLoading = true
|
|
setTimeout(() => {
|
|
this.getStatsData()
|
|
}, 100)
|
|
},
|
|
|
|
// 清空搜索
|
|
clearSearch() {
|
|
this.searchText = ''
|
|
this.searchOrder()
|
|
},
|
|
|
|
// 统计卡片点击
|
|
handleStatClick({ item, index }) {
|
|
console.log('统计卡片点击', item, index)
|
|
},
|
|
|
|
switchTab(e) {
|
|
this.currentTab = e.currentIndex
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
},
|
|
|
|
searchOrder() {
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
},
|
|
|
|
addOrder() {
|
|
uni.navigateTo({
|
|
url: '/subpages/order/add'
|
|
})
|
|
},
|
|
|
|
viewOrder(item) {
|
|
uni.navigateTo({
|
|
url: `/subpages/order/detail?id=${item.id}`
|
|
})
|
|
},
|
|
|
|
editOrder(item) {
|
|
uni.navigateTo({
|
|
url: `/subpages/order/edit?id=${item.id}`
|
|
})
|
|
},
|
|
|
|
async confirmOrder(item) {
|
|
try {
|
|
const res = await this.$modal.showConfirm('确定确认此订单吗?')
|
|
if (res.confirm) {
|
|
await confirmOrder(item.id)
|
|
this.$modal.showToast('确认成功')
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
}
|
|
} catch (error) {
|
|
this.$modal.showToast('确认失败')
|
|
}
|
|
},
|
|
|
|
async completeOrder(item) {
|
|
try {
|
|
const res = await this.$modal.showConfirm('确定完成此订单吗?')
|
|
if (res.confirm) {
|
|
await completeOrder(item.id)
|
|
this.$modal.showToast('操作成功')
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
}
|
|
} catch (error) {
|
|
this.$modal.showToast('操作失败')
|
|
}
|
|
},
|
|
|
|
async cancelOrder(item) {
|
|
try {
|
|
const res = await this.$modal.showConfirm('确定取消此订单吗?')
|
|
if (res.confirm) {
|
|
await cancelOrder(item.id)
|
|
this.$modal.showToast('取消成功')
|
|
this.pageNum = 1
|
|
this.getOrderList()
|
|
}
|
|
} catch (error) {
|
|
this.$modal.showToast('取消失败')
|
|
}
|
|
},
|
|
|
|
batchProcess() {
|
|
uni.navigateTo({
|
|
url: '/subpages/order/batch'
|
|
})
|
|
},
|
|
|
|
async exportOrders() {
|
|
try {
|
|
const res = await exportOrders({
|
|
status: this.currentTab === 0 ? '' : this.currentTab - 1
|
|
})
|
|
this.$modal.showToast('导出成功')
|
|
} catch (error) {
|
|
this.$modal.showToast('导出失败')
|
|
}
|
|
},
|
|
|
|
getStatusText(status) {
|
|
const statusMap = {
|
|
0: '待确认',
|
|
1: '已确认',
|
|
2: '已完成',
|
|
3: '已取消'
|
|
}
|
|
return statusMap[status] || '未知'
|
|
},
|
|
|
|
getStatusType(status) {
|
|
const typeMap = {
|
|
0: 'warning',
|
|
1: 'primary',
|
|
2: 'success',
|
|
3: 'error'
|
|
}
|
|
return typeMap[status] || 'default'
|
|
},
|
|
|
|
formatTime(time) {
|
|
if (!time) return ''
|
|
return time.substring(0, 16)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.order-container {
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.stats-row {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
|
|
.stat-value {
|
|
display: block;
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #007aff;
|
|
}
|
|
|
|
.stat-label {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
margin-top: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.action-bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 20rpx 0;
|
|
gap: 5rpx;
|
|
}
|
|
|
|
.item-footer {
|
|
padding: 10rpx 0;
|
|
|
|
.info-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 10rpx;
|
|
|
|
.info-text {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
|
|
.actions {
|
|
display: flex;
|
|
gap: 10rpx;
|
|
}
|
|
}
|
|
</style> |