2025-09-26 16:40:38 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="orders-page">
|
|
|
|
|
|
<h2 class="title">订单列表</h2>
|
|
|
|
|
|
<el-tabs v-model="active" @tab-click="handleTabClick">
|
|
|
|
|
|
<el-tab-pane label="订单进行中" name="7">
|
2026-01-16 15:03:50 +08:00
|
|
|
|
<order-list :items="orders[7]" :show-checkout="true" :show-end-time="false" :on-cancel="handleCancelOrder" empty-text="暂无进行中的订单" />
|
2025-09-26 16:40:38 +08:00
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="订单已完成" name="8">
|
2026-01-16 15:03:50 +08:00
|
|
|
|
<order-list :items="orders[8]" :show-checkout="false" :show-end-time="true" empty-text="暂无已完成的订单" />
|
2025-09-26 16:40:38 +08:00
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<!-- <el-tab-pane label="余额不足,订单已取消" name="9">
|
|
|
|
|
|
<order-list :items="orders[9]" :show-checkout="false" empty-text="暂无因余额不足取消的订单" />
|
|
|
|
|
|
</el-tab-pane> -->
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 订单列表页(个人中心)
|
|
|
|
|
|
* - 使用标签页按状态展示订单
|
|
|
|
|
|
* - 列表项可展开以显示 orderItemDtoList(机器列表)
|
|
|
|
|
|
* - 通过 getOrdersByStatus(status) 拉取,兼容 { data: { rows: [] } } 或 { rows: [] }
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { getOrdersByStatus, cancelOrder } from '../../api/order'
|
|
|
|
|
|
import OrderList from './OrderList.vue'
|
|
|
|
|
|
|
|
|
|
|
|
// 使用外部 SFC 组件,避免 runtime-only 构建下的模板编译警告
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'AccountOrders',
|
|
|
|
|
|
components: { OrderList },
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
active: '7', // 默认值改为 '7'(订单进行中)
|
|
|
|
|
|
orders: { 7: [], 8: [], 9: [] },
|
|
|
|
|
|
loading: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
// 优先从 URL 参数获取状态,其次从 localStorage 获取,最后使用默认值
|
|
|
|
|
|
const urlStatus = this.$route && this.$route.query && this.$route.query.status ? String(this.$route.query.status) : null
|
|
|
|
|
|
const savedStatus = localStorage.getItem('orderListActiveTab')
|
|
|
|
|
|
const initial = urlStatus || savedStatus || '7'
|
|
|
|
|
|
|
|
|
|
|
|
this.active = initial
|
|
|
|
|
|
this.fetchOrders(initial)
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
async fetchCancelOrder(params) {
|
|
|
|
|
|
const res = await cancelOrder(params)
|
|
|
|
|
|
if (res && Number(res.code) === 200) {
|
|
|
|
|
|
this.$message({
|
|
|
|
|
|
message: '取消订单成功',
|
|
|
|
|
|
type: 'success',
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
})
|
|
|
|
|
|
this.fetchOrders(this.active)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message({
|
|
|
|
|
|
message: (res && res.msg) || '取消失败',
|
|
|
|
|
|
type: 'error',
|
|
|
|
|
|
showClose: true
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
handleCancelOrder({ orderId }) {
|
|
|
|
|
|
if (!orderId) return
|
|
|
|
|
|
this.fetchCancelOrder({ orderId })
|
|
|
|
|
|
},
|
|
|
|
|
|
handleTabClick(tab) {
|
|
|
|
|
|
const name = tab && tab.name ? String(tab.name) : this.active
|
|
|
|
|
|
// 保存用户选择的标签页状态到 localStorage
|
|
|
|
|
|
try {
|
|
|
|
|
|
localStorage.setItem('orderListActiveTab', name)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.warn('保存标签页状态失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.fetchOrders(name)
|
|
|
|
|
|
},
|
|
|
|
|
|
async fetchOrders(status) {
|
|
|
|
|
|
const key = String(status)
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.loading = true
|
|
|
|
|
|
const res = await getOrdersByStatus({ status: Number(status) })
|
|
|
|
|
|
const payload = (res && res.data) != null ? res.data : res
|
|
|
|
|
|
const list = Array.isArray(payload)
|
|
|
|
|
|
? payload
|
|
|
|
|
|
: (Array.isArray(payload && payload.rows) ? payload.rows : [])
|
|
|
|
|
|
this.$set(this.orders, key, list)
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log(e,'获取订单失败');
|
|
|
|
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.loading = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.orders-page { padding: 12px; }
|
|
|
|
|
|
.title { margin: 0 0 12px 0; font-weight: 600; color: #2c3e50; }
|
|
|
|
|
|
.empty { color: #888; padding: 24px; text-align: center; }
|
|
|
|
|
|
.order-list { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
|
|
|
|
.order-card { border: 1px solid #eee; border-radius: 8px; padding: 0; background: #fff; overflow: hidden; }
|
|
|
|
|
|
.order-header { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 12px; padding: 12px; cursor: pointer; position: relative; }
|
|
|
|
|
|
.order-header:focus { outline: 2px solid #409eff; outline-offset: -2px; }
|
|
|
|
|
|
.order-header.is-open { background: #fafafa; }
|
|
|
|
|
|
.header-row { display: flex; gap: 8px; line-height: 1.8; align-items: center; }
|
|
|
|
|
|
.chevron { position: absolute; right: 12px; top: 12px; width: 10px; height: 10px; border-right: 2px solid #666; border-bottom: 2px solid #666; transform: rotate(-45deg); transition: transform 0.2s ease; }
|
|
|
|
|
|
.chevron.chevron-open { transform: rotate(45deg); }
|
|
|
|
|
|
.order-details { border-top: 1px solid #eee; padding: 12px; }
|
|
|
|
|
|
.machine-list { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
|
|
|
|
.machine-card { border: 1px dashed #e2e2e2; border-radius: 6px; padding: 10px; background: #fff; }
|
|
|
|
|
|
.row { display: flex; gap: 8px; line-height: 1.8; }
|
|
|
|
|
|
.label { color: #666; }
|
|
|
|
|
|
.value { color: #333; }
|
|
|
|
|
|
.value.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; word-break: break-all; }
|
|
|
|
|
|
.value.strong { font-weight: 700; color: #e74c3c; }
|
|
|
|
|
|
@media (max-width: 960px) {
|
|
|
|
|
|
.order-list { grid-template-columns: 1fr; }
|
|
|
|
|
|
.order-header { grid-template-columns: 1fr; }
|
|
|
|
|
|
.machine-list { grid-template-columns: 1fr; }
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|