refactor: 重构订单管理模块,拆分合并订单页面

1.  删除旧的统一订单列表页,拆分为景点和商品订单面板组件
2.  新增用户个人中心的订单导航,支持通过query参数跳转对应订单页
3.  后端补全商品订单分页查询的关联商品项逻辑
4.  新增商品订单相关API和支付、取消、收货接口
5.  调整下单和支付成功后的跳转路径,重定向到对应订单列表页
6.  添加未登录状态的全局拦截处理
This commit is contained in:
sparksfly 2026-08-08 11:42:14 +08:00
parent 1671215071
commit 966c458348
10 changed files with 1743 additions and 809 deletions

View File

@ -242,7 +242,9 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
wrapper.eq(ProductOrder::getStatus, status);
}
wrapper.orderByDesc(ProductOrder::getCreateTime);
return baseMapper.selectPage(page, wrapper).convert(this::convertToVO);
IPage<ProductOrderVO> result = baseMapper.selectPage(page, wrapper).convert(this::convertToVO);
result.getRecords().forEach(vo -> vo.setItems(getOrderItems(vo.getId())));
return result;
}
// ========== 私有方法 ==========

View File

@ -19,6 +19,11 @@ http.interceptors.request.use((config) => {
http.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data
if (res.code === 401) {
localStorage.removeItem('token')
window.location.href = '/user/login'
return Promise.reject(new Error(res.msg || '未登录'))
}
if (res.code !== 200) {
ElMessage.error(res.msg || '请求失败')
return Promise.reject(new Error(res.msg))

View File

@ -46,3 +46,50 @@ export function cancelAttractionOrder(data: { id: number; cancelReason?: string
export function getMyAttractionOrders(params: { current?: number; size?: number; status?: number }) {
return http.get<{ records: AttractionOrder[]; total: number }>('/order/attraction/my', { params })
}
export interface ProductOrderItem {
id: number
productId: number
productName: string
skuId: number
skuName: string
quantity: number
unitPrice: number
totalPrice: number
}
export interface ProductOrder {
id: number
orderNo: string
totalQuantity: number
totalPrice: number
actualAmount: number
consignee: string
phone: string
province: string
city: string
district: string
detailAddress: string
status: number
statusText: string
createTime: string
deliveryTime: string
receiveTime: string
items: ProductOrderItem[]
}
export function getMyProductOrders(params: { current?: number; size?: number; status?: number }) {
return http.get<{ records: ProductOrder[]; total: number }>('/order/product/my', { params })
}
export function payProductOrder(id: number) {
return http.put<void>(`/order/product/${id}/pay`)
}
export function cancelProductOrder(data: { id: number; cancelReason?: string }) {
return http.put<void>('/order/product/cancel', data)
}
export function receiveProductOrder(id: number) {
return http.put<void>(`/order/product/${id}/receive`)
}

View File

@ -78,12 +78,6 @@ const routes: RouteRecordRaw[] = [
component: () => import('@/views/user/AddressView.vue'),
meta: { title: '地址管理' },
},
{
path: 'order',
name: 'OrderList',
component: () => import('@/views/order/OrderList.vue'),
meta: { title: '我的订单' },
},
{
path: 'cart',
name: 'Cart',

View File

@ -192,7 +192,7 @@ function submitOrder() {
}
ElMessage.success('下单成功')
cartStore.clear()
setTimeout(() => { router.push('/user/order') }, 1000)
setTimeout(() => { router.push('/user/profile?orders=product') }, 1000)
}
</script>

View File

@ -1,198 +0,0 @@
<template>
<div class="order-list-page" style="padding: 100px 20px 80px;">
<div class="container">
<h1 class="page-title">我的订单</h1>
<el-tabs v-model="activeTab" class="order-tabs">
<el-tab-pane label="全部" name="all" />
<el-tab-pane label="待支付" name="unpaid" />
<el-tab-pane label="已支付" name="paid" />
<el-tab-pane label="已取消" name="cancelled" />
</el-tabs>
<div v-if="filteredOrders.length === 0" class="empty-state">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#CBD5E1" stroke-width="1.5"><path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/></svg>
<p>{{ loading ? '加载中...' : '暂无门票订单' }}</p>
</div>
<div v-for="order in filteredOrders" :key="order.id" class="order-card">
<div class="order-header">
<span class="order-no">订单号{{ order.orderNo }}</span>
<span class="order-status" :class="statusClass(order.status)">{{ statusText(order.status) }}</span>
</div>
<div class="order-item">
<div class="item-img" style="background: linear-gradient(135deg, #38bdf8, #0ea5e9)">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="1.5"><path d="M20 13c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3a3 3 0 013-3z"/><path d="M4 6h16v4.5a3 3 0 010 3V18a2 2 0 01-2 2H6a2 2 0 01-2-2v-4.5a3 3 0 010-3V6z"/></svg>
</div>
<div class="item-info">
<h4>{{ order.attractionName }} - {{ order.ticketName }}</h4>
<p>游玩日期{{ order.visitDate || '不限' }} · 联系人{{ order.contactName }} {{ order.contactPhone }}</p>
</div>
<span class="item-price">¥{{ order.unitPrice }} x {{ order.quantity }}</span>
</div>
<div class="order-footer">
<span class="order-total">合计<strong>¥{{ Number(order.totalPrice).toFixed(2) }}</strong></span>
<div class="order-actions">
<el-button v-if="order.status === 1" type="primary" class="btn-action" @click="goPay(order)">去支付</el-button>
<el-button v-if="order.status === 1" text @click="handleCancel(order)">取消订单</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getMyAttractionOrders, cancelAttractionOrder } from '@/api/order'
const router = useRouter()
const activeTab = ref('all')
const orders = ref<any[]>([])
const loading = ref(false)
const statusTextMap: Record<number, string> = {
1: '待支付',
2: '已支付',
3: '已使用',
4: '已取消',
5: '退款中',
6: '已退款',
}
const filteredOrders = computed(() => {
if (activeTab.value === 'all') return orders.value
if (activeTab.value === 'unpaid') return orders.value.filter((o) => o.status === 1)
if (activeTab.value === 'paid') return orders.value.filter((o) => o.status === 2 || o.status === 3)
if (activeTab.value === 'cancelled') return orders.value.filter((o) => o.status === 4)
return orders.value
})
function statusText(status: number) {
return statusTextMap[status] || '未知状态'
}
function statusClass(status: number) {
const map: Record<number, string> = { 1: 'unpaid', 2: 'paid', 3: 'used', 4: 'cancelled', 5: 'refunding', 6: 'refunded' }
return map[status] || ''
}
function goPay(order: any) {
router.push({
path: '/payment',
query: { type: 'attraction', ids: String(order.id), amount: Number(order.totalPrice).toFixed(2) },
})
}
async function handleCancel(order: any) {
try {
await cancelAttractionOrder({ id: order.id, cancelReason: '用户取消' })
ElMessage.success('订单已取消')
fetchOrders()
} catch {
// axios
}
}
async function fetchOrders() {
loading.value = true
try {
const res: any = await getMyAttractionOrders({ current: 1, size: 100 })
orders.value = res.data.records || []
} catch {
// axios
} finally {
loading.value = false
}
}
onMounted(fetchOrders)
</script>
<style scoped lang="scss">
.page-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 24px;
}
.empty-state {
text-align: center;
padding: 60px 0;
svg { margin-bottom: 16px; }
p { color: var(--text-muted); font-size: 15px; }
}
.order-card {
background: #fff;
border-radius: var(--radius);
box-shadow: var(--shadow);
margin-bottom: 16px;
overflow: hidden;
}
.order-header {
display: flex;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--border);
font-size: 13px;
}
.order-no { color: var(--text-muted); }
.order-status { font-weight: 500;
&.unpaid { color: var(--cta); }
&.paid, &.used { color: var(--primary); }
&.cancelled { color: #94a3b8; }
&.refunding, &.refunded { color: #f59e0b; }}
.order-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 20px;
border-bottom: 1px solid var(--border);
&:last-child { border-bottom: none; }
}
.item-img {
width: 64px;
height: 64px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.item-info {
flex: 1;
h4 { font-size: 14px; font-weight: 500; }
p { font-size: 13px; color: var(--text-muted); margin-top: 2px; }
}
.item-price { font-size: 14px; color: var(--text-light); white-space: nowrap; }
.order-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
}
.order-total { font-size: 14px; strong { color: var(--cta); font-size: 16px; }}
.order-actions { display: flex; gap: 8px; }
.btn-action {
background: linear-gradient(135deg, var(--cta), var(--cta-hover));
border: none;
border-radius: 8px;
font-weight: 500;
&:hover { box-shadow: 0 4px 16px rgba(249,115,22,0.3); }
}
</style>

View File

@ -36,7 +36,7 @@
import { ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { payAttractionOrder } from '@/api/order'
import { payAttractionOrder, payProductOrder } from '@/api/order'
const route = useRoute()
const router = useRouter()
@ -60,9 +60,14 @@ async function handlePay() {
for (const id of orderIds.value) {
await payAttractionOrder(id)
}
} else if (orderType.value === 'product' && orderIds.value.length > 0) {
for (const id of orderIds.value) {
await payProductOrder(id)
}
}
ElMessage.success('支付成功(沙箱环境)')
setTimeout(() => { router.push('/user/order') }, 1000)
const target = orderType.value === 'product' ? '/user/profile?orders=product' : '/user/profile?orders=attraction'
setTimeout(() => { router.push(target) }, 1000)
} catch {
// axios
} finally {

View File

@ -0,0 +1,311 @@
<template>
<div class="order-panel">
<el-tabs v-model="activeStatus" class="order-tabs">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
</el-tabs>
<div v-if="filteredOrders.length === 0" class="empty-state">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#CBD5E1" stroke-width="1.5"><path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/><line x1="3" y1="6" x2="21" y2="6"/></svg>
<p>{{ loading ? '加载中...' : '暂无订单' }}</p>
</div>
<!-- 景点门票订单 -->
<template v-if="isAttraction">
<div v-for="order in filteredOrders" :key="order.id" class="order-card">
<div class="order-header">
<span class="order-no">订单号{{ order.orderNo }}</span>
<span class="order-status" :class="attractionStatusClass(order.status)">{{ attractionStatusText(order.status) }}</span>
</div>
<div class="order-item">
<div class="item-img" style="background: linear-gradient(135deg, #38bdf8, #0ea5e9)">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="1.5"><path d="M20 13c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3a3 3 0 013-3z"/><path d="M4 6h16v4.5a3 3 0 010 3V18a2 2 0 01-2 2H6a2 2 0 01-2-2v-4.5a3 3 0 010-3V6z"/></svg>
</div>
<div class="item-info">
<h4>{{ order.attractionName }} - {{ order.ticketName }}</h4>
<p>游玩日期{{ order.visitDate || '不限' }} · 联系人{{ order.contactName }} {{ order.contactPhone }}</p>
</div>
<span class="item-price">¥{{ order.unitPrice }} x {{ order.quantity }}</span>
</div>
<div class="order-footer">
<span class="order-total">合计<strong>¥{{ Number(order.totalPrice).toFixed(2) }}</strong></span>
<div class="order-actions">
<el-button v-if="order.status === 1" type="primary" class="btn-action" @click="goPay(order)">去支付</el-button>
<el-button v-if="order.status === 1" text @click="handleCancel(order)">取消订单</el-button>
</div>
</div>
</div>
</template>
<!-- 商品订单 -->
<template v-else>
<div v-for="order in filteredOrders" :key="order.id" class="order-card">
<div class="order-header">
<span class="order-no">订单号{{ order.orderNo }}</span>
<span class="order-status" :class="productStatusClass(order.status)">{{ productStatusText(order.status) }}</span>
</div>
<div v-for="item in order.items || []" :key="item.id" class="order-item">
<div class="item-img" style="background: linear-gradient(135deg, #667eea, #764ba2)">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="1.5"><rect x="3" y="7" width="18" height="14" rx="2"/><path d="M16 7V5a2 2 0 00-4-2h-2a2 2 0 00-2 2v2"/></svg>
</div>
<div class="item-info">
<h4>{{ item.productName }}</h4>
<p>{{ item.skuName || '默认规格' }}</p>
</div>
<span class="item-price">¥{{ item.unitPrice }} x {{ item.quantity }}</span>
</div>
<div class="order-item address-item">
<div class="item-info">
<p>收货人{{ order.consignee }} {{ order.phone }}</p>
<p>{{ order.province }}{{ order.city }}{{ order.district }}{{ order.detailAddress }}</p>
</div>
</div>
<div class="order-footer">
<span class="order-total">实付<strong>¥{{ Number(order.actualAmount ?? order.totalPrice).toFixed(2) }}</strong></span>
<div class="order-actions">
<el-button v-if="order.status === 1" type="primary" class="btn-action" @click="goPay(order)">去支付</el-button>
<el-button v-if="order.status === 1" text @click="handleCancel(order)">取消订单</el-button>
<el-button v-if="order.status === 4" type="primary" class="btn-action" @click="handleReceive(order)">确认收货</el-button>
</div>
</div>
</div>
</template>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
getMyAttractionOrders,
cancelAttractionOrder,
getMyProductOrders,
cancelProductOrder,
receiveProductOrder,
} from '@/api/order'
const props = defineProps<{ orderType: 'attraction' | 'product' }>()
const router = useRouter()
const activeStatus = ref('all')
const attractionOrders = ref<any[]>([])
const productOrders = ref<any[]>([])
const loading = ref(false)
const isAttraction = computed(() => props.orderType === 'attraction')
const attractionStatusMap: Record<number, string> = {
1: '待支付',
2: '已支付',
3: '已使用',
4: '已取消',
5: '退款中',
6: '已退款',
}
const productStatusMap: Record<number, string> = {
1: '待支付',
2: '已支付',
3: '待发货',
4: '已发货',
5: '待收货',
6: '已完成',
7: '已取消',
8: '退款中',
9: '已退款',
}
const statusTabs = computed(() => {
if (isAttraction.value) {
return [
{ label: '全部', value: 'all' },
{ label: '待支付', value: 'unpaid' },
{ label: '已支付', value: 'paid' },
{ label: '已使用', value: 'used' },
{ label: '已取消', value: 'cancelled' },
]
}
return [
{ label: '全部', value: 'all' },
{ label: '待支付', value: 'unpaid' },
{ label: '已支付', value: 'paid' },
{ label: '已发货', value: 'delivered' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' },
]
})
const filteredOrders = computed(() => {
const source = isAttraction.value ? attractionOrders.value : productOrders.value
if (activeStatus.value === 'all') return source
if (activeStatus.value === 'unpaid') return source.filter((o) => o.status === 1)
if (activeStatus.value === 'paid') {
return isAttraction.value
? source.filter((o) => o.status === 2)
: source.filter((o) => o.status === 2 || o.status === 3)
}
if (activeStatus.value === 'used') return source.filter((o) => o.status === 3)
if (activeStatus.value === 'delivered') return source.filter((o) => o.status === 4)
if (activeStatus.value === 'completed') return source.filter((o) => o.status === 6)
if (activeStatus.value === 'cancelled') return source.filter((o) => o.status === 7 || o.status === 4)
return source
})
function attractionStatusText(status: number) {
return attractionStatusMap[status] || '未知状态'
}
function productStatusText(status: number) {
return productStatusMap[status] || '未知状态'
}
function attractionStatusClass(status: number) {
const map: Record<number, string> = { 1: 'unpaid', 2: 'paid', 3: 'used', 4: 'cancelled', 5: 'refunding', 6: 'refunded' }
return map[status] || ''
}
function productStatusClass(status: number) {
const map: Record<number, string> = { 1: 'unpaid', 2: 'paid', 3: 'pending', 4: 'delivered', 5: 'received', 6: 'completed', 7: 'cancelled', 8: 'refunding', 9: 'refunded' }
return map[status] || ''
}
function goPay(order: any) {
const amount = isAttraction.value
? Number(order.totalPrice).toFixed(2)
: Number(order.actualAmount ?? order.totalPrice).toFixed(2)
router.push({
path: '/payment',
query: { type: props.orderType, ids: String(order.id), amount },
})
}
async function handleCancel(order: any) {
try {
if (isAttraction.value) {
await cancelAttractionOrder({ id: order.id, cancelReason: '用户取消' })
} else {
await cancelProductOrder({ id: order.id, cancelReason: '用户取消' })
}
ElMessage.success('订单已取消')
fetchOrders()
} catch {
// axios
}
}
async function handleReceive(order: any) {
try {
await receiveProductOrder(order.id)
ElMessage.success('已确认收货')
fetchOrders()
} catch {
// axios
}
}
async function fetchOrders() {
loading.value = true
try {
if (isAttraction.value) {
const res: any = await getMyAttractionOrders({ current: 1, size: 100 })
attractionOrders.value = res.data.records || []
} else {
const res: any = await getMyProductOrders({ current: 1, size: 100 })
productOrders.value = res.data.records || []
}
} catch {
// axios
} finally {
loading.value = false
}
}
onMounted(fetchOrders)
</script>
<style scoped lang="scss">
.empty-state {
text-align: center;
padding: 40px 0;
svg { margin-bottom: 12px; }
p { color: var(--text-muted); font-size: 14px; }
}
.order-card {
background: #fff;
border-radius: var(--radius);
box-shadow: var(--shadow);
margin-bottom: 16px;
overflow: hidden;
}
.order-header {
display: flex;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid var(--border);
font-size: 13px;
}
.order-no { color: var(--text-muted); }
.order-status { font-weight: 500;
&.unpaid { color: var(--cta); }
&.paid, &.used, &.completed { color: var(--primary); }
&.delivered, &.received { color: #0ea5e9; }
&.pending { color: #64748b; }
&.cancelled { color: #94a3b8; }
&.refunding, &.refunded { color: #f59e0b; }}
.order-item {
display: flex;
align-items: center;
gap: 16px;
padding: 14px 16px;
border-bottom: 1px solid var(--border);
&:last-child { border-bottom: none; }
}
.address-item {
background: var(--bg);
p { font-size: 13px; color: var(--text-muted); line-height: 1.7; }
}
.item-img {
width: 56px;
height: 56px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.item-info {
flex: 1;
h4 { font-size: 14px; font-weight: 500; }
p { font-size: 13px; color: var(--text-muted); margin-top: 2px; }
}
.item-price { font-size: 14px; color: var(--text-light); white-space: nowrap; }
.order-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 16px;
}
.order-total { font-size: 14px; strong { color: var(--cta); font-size: 16px; }}
.order-actions { display: flex; gap: 8px; }
.btn-action {
background: linear-gradient(135deg, var(--cta), var(--cta-hover));
border: none;
border-radius: 8px;
font-weight: 500;
&:hover { box-shadow: 0 4px 16px rgba(249,115,22,0.3); }
}
</style>

View File

@ -34,7 +34,7 @@
<div class="container page-body">
<aside class="profile-sidebar">
<nav class="sidebar-nav">
<a v-for="item in navItems" :key="item.path" class="nav-item" :class="{ active: currentNav === item.path }" @click="currentNav = item.path" @keydown.enter="currentNav = item.path" tabindex="0">
<a v-for="item in navItems" :key="item.path" class="nav-item" :class="{ active: currentNav === item.path }" @click="handleNav(item)" @keydown.enter="handleNav(item)" tabindex="0">
<span class="nav-icon" v-html="item.icon"></span>
<span>{{ item.label }}</span>
</a>
@ -127,6 +127,22 @@
<router-link to="/user/travel/create" class="btn-primary">发布游记</router-link>
</div>
</div>
<div v-if="currentNav === '/user/orders/attraction'" class="content-card">
<div class="card-header">
<h3>景点门票订单</h3>
<span class="card-desc">查询你的门票订单</span>
</div>
<OrderPanel order-type="attraction" />
</div>
<div v-if="currentNav === '/user/orders/product'" class="content-card">
<div class="card-header">
<h3>商品订单</h3>
<span class="card-desc">查询你的商品订单</span>
</div>
<OrderPanel order-type="product" />
</div>
</main>
</div>
</div>
@ -134,12 +150,14 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/stores/user'
import { getTravelNoteList } from '@/api/travel'
import OrderPanel from '@/views/user/OrderPanel.vue'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const saving = ref(false)
@ -149,8 +167,14 @@ const navItems = [
{ path: '/user/profile', label: '个人资料', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>' },
{ path: '/user/security', label: '账号安全', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>' },
{ path: '/user/activity', label: '最近动态', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>' },
{ path: '/user/orders/attraction', label: '景点门票订单', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 13c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3a3 3 0 013-3z"/><path d="M4 6h16v4.5a3 3 0 010 3V18a2 2 0 01-2 2H6a2 2 0 01-2-2v-4.5a3 3 0 010-3V6z"/></svg>' },
{ path: '/user/orders/product', label: '商品订单', icon: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="7" width="18" height="14" rx="2"/><path d="M16 7V5a2 2 0 00-4-2h-2a2 2 0 00-2 2v2"/></svg>' },
]
function handleNav(item: any) {
currentNav.value = item.path
}
const genderOptions = [
{ value: 0, label: '保密', icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>' },
{ value: 1, label: '男', icon: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>' },
@ -223,6 +247,11 @@ onMounted(async () => {
router.replace('/user/login')
return
}
if (route.query.orders === 'attraction') {
currentNav.value = '/user/orders/attraction'
} else if (route.query.orders === 'product') {
currentNav.value = '/user/orders/product'
}
try {
await userStore.fetchUserInfo()
initForm()

File diff suppressed because it is too large Load Diff