consumer模块完成度95%,安卓端大部分页面能正常获取数据,页面样式显示基本正常,逐渐完善;消费者端的积分、余额、评价、优惠券等小模块正在完善
This commit is contained in:
@@ -18,13 +18,20 @@
|
||||
</view>
|
||||
|
||||
<!-- 订单状态筛选 -->
|
||||
<view class="order-tabs">
|
||||
<scroll-view scroll-x class="tab-scroll" :show-scrollbar="false">
|
||||
<view class="tab-container">
|
||||
<view class="order-tabs-fixed-container">
|
||||
<view
|
||||
:class="['tab-item-fixed', { active: activeTab === 'all' }]"
|
||||
@click="switchTab('all')"
|
||||
>
|
||||
<text class="tab-name">全部</text>
|
||||
<view v-if="activeTab === 'all'" class="active-indicator"></view>
|
||||
</view>
|
||||
<scroll-view scroll-x="true" class="tab-scroll-mobile" :show-scrollbar="false" :scroll-with-animation="true">
|
||||
<view class="tab-container-mobile">
|
||||
<view
|
||||
v-for="tab in orderTabs"
|
||||
v-for="tab in orderTabsMobile"
|
||||
:key="tab.id"
|
||||
:class="['tab-item', { active: activeTab === tab.id }]"
|
||||
:class="['tab-item-mobile', { active: activeTab === tab.id }]"
|
||||
@click="switchTab(tab.id)"
|
||||
>
|
||||
<text class="tab-name">{{ tab.name }}</text>
|
||||
@@ -58,10 +65,15 @@
|
||||
v-for="order in orders"
|
||||
:key="order.id"
|
||||
class="order-card"
|
||||
@click="viewOrderDetail(order.id)"
|
||||
>
|
||||
<!-- 订单头部 -->
|
||||
<view class="order-header">
|
||||
<text class="order-no">订单号:{{ order.order_no }}</text>
|
||||
<!-- 订单头部:显示店铺名称 -->
|
||||
<view class="order-card-header">
|
||||
<view class="shop-info">
|
||||
<text class="shop-icon">🏪</text>
|
||||
<text class="shop-name">{{ order.shop_name != null && order.shop_name != '' ? order.shop_name : '自营店铺' }}</text>
|
||||
<text class="arrow-right">›</text>
|
||||
</view>
|
||||
<text :class="['order-status', getStatusClass(order.status)]">
|
||||
{{ getStatusText(order.status) }}
|
||||
</text>
|
||||
@@ -81,34 +93,29 @@
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="product-info">
|
||||
<text class="product-name">{{ product.name }}</text>
|
||||
<text class="product-spec">{{ product.spec }}</text>
|
||||
<view class="product-top-info">
|
||||
<text class="product-name">{{ product.name }}</text>
|
||||
<text class="product-spec">{{ product.spec }}</text>
|
||||
</view>
|
||||
<view class="product-footer">
|
||||
<text class="product-price">¥{{ product.price }}</text>
|
||||
<text class="product-quantity">×{{ product.quantity }}</text>
|
||||
<text class="product-quantity">x{{ product.quantity }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="order-info">
|
||||
<view class="info-row">
|
||||
<text class="info-label">商品合计</text>
|
||||
<text class="info-value">¥{{ order.product_amount }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="info-label">运费</text>
|
||||
<text class="info-value">¥{{ order.shipping_fee }}</text>
|
||||
</view>
|
||||
<view class="info-row total">
|
||||
<text class="info-label">实付款</text>
|
||||
<text class="info-value total-price">¥{{ order.total_amount }}</text>
|
||||
<!-- 订单汇总信息 -->
|
||||
<view class="order-summary">
|
||||
<text class="order-time">{{ formatDate(order.create_time) }}</text>
|
||||
<view class="summary-right">
|
||||
<text class="summary-label">共{{ order.products.length }}件商品 实付:</text>
|
||||
<text class="summary-price">¥{{ order.total_amount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单操作 -->
|
||||
<view class="order-actions">
|
||||
<view class="order-actions" @click.stop="">
|
||||
<view v-if="order.status === 1" class="action-buttons">
|
||||
<button class="action-btn cancel" @click="cancelOrder(order.id)">取消订单</button>
|
||||
<button class="action-btn pay" @click="payOrder(order.id)">立即支付</button>
|
||||
@@ -162,24 +169,6 @@ import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { onShow, onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||
|
||||
// 拦截返回事件,避免跳回登录页
|
||||
onBackPress((options) => {
|
||||
if (options.from === 'navigateBack') {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
const prevPage = pages[pages.length - 2]
|
||||
// 如果上一页是登录页,则重定向到个人中心
|
||||
if (prevPage.route.includes('login')) {
|
||||
uni.redirectTo({
|
||||
url: '/pages/mall/consumer/profile'
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 定义标签页类型
|
||||
type OrderTabItem = {
|
||||
id: string,
|
||||
@@ -206,6 +195,8 @@ type OrderItem = {
|
||||
product_amount: number,
|
||||
shipping_fee: number,
|
||||
total_amount: number,
|
||||
merchant_id: string,
|
||||
shop_name: string,
|
||||
products: OrderProduct[]
|
||||
}
|
||||
|
||||
@@ -230,7 +221,10 @@ const orderTabs = ref<OrderTabItem[]>([
|
||||
{ id: 'cancelled', name: '已取消', count: 0 }
|
||||
])
|
||||
|
||||
// Removed Mock Data
|
||||
// 模拟状态筛选(除去"全部"后的其余标签)
|
||||
const orderTabsMobile = computed((): OrderTabItem[] => {
|
||||
return orderTabs.value.filter((tab: OrderTabItem) => tab.id !== 'all')
|
||||
})
|
||||
|
||||
|
||||
// 辅助函数:获取状态码
|
||||
@@ -243,59 +237,98 @@ const getStatusByTab = (tabId: string): number => {
|
||||
return 0
|
||||
}
|
||||
|
||||
// 辅助函数:解析规格文本
|
||||
const parseSpecText = (specs: any): string => {
|
||||
if (specs == null) return ''
|
||||
if (typeof specs === 'string') return specs
|
||||
// 对于对象类型,尝试转为JSON字符串或简单处理
|
||||
// 格式化规格对象为友好的文本 - 必须在 parseSpecText 之前定义
|
||||
function formatSpecObj(obj: any): string {
|
||||
if (obj == null) return ''
|
||||
if (typeof obj !== 'object') {
|
||||
// 非对象类型直接返回字符串形式
|
||||
if (typeof obj === 'string') return obj
|
||||
if (typeof obj === 'number') return obj.toString()
|
||||
return ''
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.stringify(specs)
|
||||
const objStr = JSON.stringify(obj)
|
||||
const objParsed = JSON.parse(objStr)
|
||||
if (objParsed == null) return ''
|
||||
|
||||
const specObj = objParsed as UTSJSONObject
|
||||
|
||||
// 使用 JSON.stringify 获取所有键
|
||||
const specObjStr = JSON.stringify(specObj)
|
||||
const specObjForKeys = JSON.parse(specObjStr) as UTSJSONObject
|
||||
|
||||
// 手动提取键值对
|
||||
const parts: string[] = []
|
||||
|
||||
// 尝试获取已知字段
|
||||
const colorVal = specObjForKeys.getString('Color')
|
||||
if (colorVal != null && colorVal != '') {
|
||||
parts.push('Color: ' + colorVal)
|
||||
}
|
||||
|
||||
const sizeVal = specObjForKeys.getString('Size')
|
||||
if (sizeVal != null && sizeVal != '') {
|
||||
parts.push('Size: ' + sizeVal)
|
||||
}
|
||||
|
||||
const defaultVal = specObjForKeys.getString('默认')
|
||||
if (defaultVal != null && defaultVal != '') {
|
||||
parts.push('默认: ' + defaultVal)
|
||||
}
|
||||
|
||||
// 如果没有匹配到已知字段,尝试直接显示 JSON
|
||||
if (parts.length === 0) {
|
||||
// 尝试遍历对象
|
||||
const objAny = specObjForKeys as any
|
||||
if (objAny != null) {
|
||||
return specObjStr.replace(/[{}"]/g, '').replace(/:/g, ': ').replace(/,/g, ' | ')
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join(' | ')
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助函数:解析规格文本
|
||||
function parseSpecText(specs: any): string {
|
||||
if (specs == null) return ''
|
||||
if (typeof specs === 'string') {
|
||||
// 如果是 JSON 字符串,尝试解析
|
||||
if (specs.startsWith('{') || specs.startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(specs)
|
||||
if (parsed == null) return specs
|
||||
return formatSpecObj(parsed)
|
||||
} catch (e) {
|
||||
return specs
|
||||
}
|
||||
}
|
||||
return specs
|
||||
}
|
||||
// 对于对象类型,格式化显示
|
||||
return formatSpecObj(specs)
|
||||
}
|
||||
|
||||
// 辅助函数:更新标签计数
|
||||
const updateTabsCounts = (allOrders: any[]) => {
|
||||
// 直接重新赋值整个数组
|
||||
const tabsData = orderTabs.value
|
||||
const updateTabsCounts = (allOrders: OrderItem[]) => {
|
||||
// 计算各状态数量
|
||||
const countAll = allOrders.length
|
||||
const countPending = allOrders.filter((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['status'] === 1
|
||||
}).length
|
||||
const countShipping = allOrders.filter((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['status'] === 2
|
||||
}).length
|
||||
const countDelivering = allOrders.filter((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['status'] === 3
|
||||
}).length
|
||||
const countCompleted = allOrders.filter((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['status'] === 4
|
||||
}).length
|
||||
const countCancelled = allOrders.filter((o: any) => {
|
||||
const obj = o as Record<string, any>
|
||||
return obj['status'] === 5
|
||||
}).length
|
||||
const countPending = allOrders.filter((o: OrderItem) => o.status === 1).length
|
||||
const countShipping = allOrders.filter((o: OrderItem) => o.status === 2).length
|
||||
const countDelivering = allOrders.filter((o: OrderItem) => o.status === 3).length
|
||||
const countCompleted = allOrders.filter((o: OrderItem) => o.status === 4).length
|
||||
const countCancelled = allOrders.filter((o: OrderItem) => o.status === 5).length
|
||||
|
||||
// 更新数组元素
|
||||
const tabsArr = tabsData as any[]
|
||||
const tab0 = tabsArr[0] as Record<string, any>
|
||||
tab0['count'] = countAll
|
||||
const tab1 = tabsArr[1] as Record<string, any>
|
||||
tab1['count'] = countPending
|
||||
const tab2 = tabsArr[2] as Record<string, any>
|
||||
tab2['count'] = countShipping
|
||||
const tab3 = tabsArr[3] as Record<string, any>
|
||||
tab3['count'] = countDelivering
|
||||
const tab4 = tabsArr[4] as Record<string, any>
|
||||
tab4['count'] = countCompleted
|
||||
const tab5 = tabsArr[5] as Record<string, any>
|
||||
tab5['count'] = countCancelled
|
||||
orderTabs.value[0].count = countAll
|
||||
orderTabs.value[1].count = countPending
|
||||
orderTabs.value[2].count = countShipping
|
||||
orderTabs.value[3].count = countDelivering
|
||||
orderTabs.value[4].count = countCompleted
|
||||
orderTabs.value[5].count = countCancelled
|
||||
}
|
||||
|
||||
// 辅助函数:按标签筛选订单
|
||||
@@ -317,69 +350,124 @@ const loadOrders = async () => {
|
||||
try {
|
||||
// Fetch all orders from Supabase (status=0)
|
||||
const fetchedOrders = await supabaseService.getOrders(0)
|
||||
console.log('[loadOrders] 获取到订单数量:', fetchedOrders.length)
|
||||
|
||||
// Map to View Model
|
||||
const mappedOrders: any[] = []
|
||||
const mappedOrders: OrderItem[] = []
|
||||
for (let i = 0; i < fetchedOrders.length; i++) {
|
||||
const order = fetchedOrders[i]
|
||||
const orderObj = order as Record<string, any>
|
||||
const items = orderObj['ml_order_items'] as any[]
|
||||
const productsList: any[] = []
|
||||
// 使用 JSON 序列化转换
|
||||
const orderStr = JSON.stringify(order)
|
||||
const orderParsed = JSON.parse(orderStr)
|
||||
if (orderParsed == null) continue
|
||||
const orderObj = orderParsed as UTSJSONObject
|
||||
|
||||
if (items != null) {
|
||||
for (let j = 0; j < items.length; j++) {
|
||||
const item = items[j]
|
||||
const itemObj = item as Record<string, any>
|
||||
const specRaw = itemObj['specifications']
|
||||
const specText = specRaw != null ? parseSpecText(specRaw) : ''
|
||||
productsList.push({
|
||||
id: itemObj['product_id'],
|
||||
name: itemObj['product_name'],
|
||||
price: itemObj['price'],
|
||||
image: itemObj['image_url'] ?? '/static/default-product.png',
|
||||
spec: specText,
|
||||
quantity: itemObj['quantity']
|
||||
})
|
||||
const itemsRaw = orderObj.get('ml_order_items')
|
||||
const productsList: OrderProduct[] = []
|
||||
|
||||
console.log('[loadOrders] 订单商品数据:', itemsRaw)
|
||||
|
||||
if (itemsRaw != null) {
|
||||
// 先检查是否为数组
|
||||
if (Array.isArray(itemsRaw)) {
|
||||
const items = itemsRaw as any[]
|
||||
console.log('[loadOrders] 商品数量:', items.length)
|
||||
for (let j = 0; j < items.length; j++) {
|
||||
const item = items[j]
|
||||
const itemStr = JSON.stringify(item)
|
||||
const itemParsed = JSON.parse(itemStr)
|
||||
if (itemParsed == null) continue
|
||||
const itemObj = itemParsed as UTSJSONObject
|
||||
|
||||
const specRaw = itemObj.get('specifications')
|
||||
const specText = specRaw != null ? parseSpecText(specRaw) : ''
|
||||
|
||||
const productId = itemObj.getString('product_id')
|
||||
const productName = itemObj.getString('product_name')
|
||||
const price = itemObj.getNumber('price')
|
||||
const imageUrl = itemObj.getString('image_url')
|
||||
const quantity = itemObj.getNumber('quantity')
|
||||
|
||||
console.log('[loadOrders] 商品:', productName, '图片:', imageUrl, '规格:', specText)
|
||||
|
||||
const productItem: OrderProduct = {
|
||||
id: productId ?? '',
|
||||
name: productName ?? '未知商品',
|
||||
price: price ?? 0,
|
||||
image: imageUrl ?? '/static/default-product.png',
|
||||
spec: specText,
|
||||
quantity: quantity ?? 1
|
||||
}
|
||||
productsList.push(productItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mappedOrders.push({
|
||||
id: orderObj['id'],
|
||||
order_no: orderObj['order_no'],
|
||||
status: orderObj['order_status'],
|
||||
create_time: orderObj['created_at'],
|
||||
product_amount: orderObj['product_amount'] ?? 0,
|
||||
shipping_fee: orderObj['shipping_fee'] ?? 0,
|
||||
total_amount: orderObj['total_amount'] ?? orderObj['paid_amount'] ?? 0,
|
||||
const orderId = orderObj.getString('id')
|
||||
const orderNo = orderObj.getString('order_no')
|
||||
const orderStatus = orderObj.getNumber('order_status')
|
||||
const createdAt = orderObj.getString('created_at')
|
||||
const productAmount = orderObj.getNumber('product_amount')
|
||||
const shippingFee = orderObj.getNumber('shipping_fee')
|
||||
const totalAmount = orderObj.getNumber('total_amount')
|
||||
const paidAmount = orderObj.getNumber('paid_amount')
|
||||
const merchantId = orderObj.getString('merchant_id')
|
||||
|
||||
// 从关联查询的 ml_shops 表获取店铺名称
|
||||
let shopName = '自营店铺'
|
||||
const shopsRaw = orderObj.get('ml_shops')
|
||||
if (shopsRaw != null) {
|
||||
const shopStr = JSON.stringify(shopsRaw)
|
||||
const shopParsed = JSON.parse(shopStr)
|
||||
if (shopParsed != null) {
|
||||
const shopObj = shopParsed as UTSJSONObject
|
||||
const shopNameFromDb = shopObj.getString('shop_name')
|
||||
if (shopNameFromDb != null && shopNameFromDb != '') {
|
||||
shopName = shopNameFromDb
|
||||
}
|
||||
}
|
||||
} else if (merchantId != null && merchantId != '') {
|
||||
shopName = '商家店铺'
|
||||
}
|
||||
|
||||
console.log('[loadOrders] 订单号:', orderNo, '店铺:', shopName, '商品数:', productsList.length)
|
||||
|
||||
// 如果没有商品数据,添加一个占位商品
|
||||
if (productsList.length === 0) {
|
||||
const placeholderProduct: OrderProduct = {
|
||||
id: 'placeholder',
|
||||
name: '订单商品',
|
||||
price: totalAmount ?? paidAmount ?? 0,
|
||||
image: '/static/default-product.png',
|
||||
spec: '',
|
||||
quantity: 1
|
||||
}
|
||||
productsList.push(placeholderProduct)
|
||||
}
|
||||
|
||||
const mappedOrder: OrderItem = {
|
||||
id: orderId ?? '',
|
||||
order_no: orderNo ?? '',
|
||||
status: orderStatus ?? 1,
|
||||
create_time: createdAt ?? '',
|
||||
product_amount: productAmount ?? 0,
|
||||
shipping_fee: shippingFee ?? 0,
|
||||
total_amount: totalAmount ?? paidAmount ?? 0,
|
||||
merchant_id: merchantId ?? '',
|
||||
shop_name: shopName,
|
||||
products: productsList
|
||||
})
|
||||
}
|
||||
mappedOrders.push(mappedOrder)
|
||||
}
|
||||
|
||||
// Sort by created_at desc
|
||||
mappedOrders.sort((a: any, b: any) => {
|
||||
const aObj = a as Record<string, any>
|
||||
const bObj = b as Record<string, any>
|
||||
const timeA = new Date(aObj['create_time'] as string).getTime()
|
||||
const timeB = new Date(bObj['create_time'] as string).getTime()
|
||||
// Sort by created_at desc - 直接使用 OrderItem 类型访问属性
|
||||
mappedOrders.sort((a: OrderItem, b: OrderItem) => {
|
||||
const timeA = new Date(a.create_time).getTime()
|
||||
const timeB = new Date(b.create_time).getTime()
|
||||
return timeB - timeA
|
||||
})
|
||||
|
||||
// 将 mappedOrders 转换为 OrderItem[] 类型
|
||||
const typedOrders: OrderItem[] = []
|
||||
for (let i = 0; i < mappedOrders.length; i++) {
|
||||
const mo = mappedOrders[i] as Record<string, any>
|
||||
typedOrders.push({
|
||||
id: mo['id'] as string,
|
||||
order_no: mo['order_no'] as string,
|
||||
status: mo['status'] as number,
|
||||
create_time: mo['create_time'] as string,
|
||||
product_amount: mo['product_amount'] as number,
|
||||
shipping_fee: mo['shipping_fee'] as number,
|
||||
total_amount: mo['total_amount'] as number,
|
||||
products: mo['products'] as OrderProduct[]
|
||||
})
|
||||
}
|
||||
allOrdersList.value = typedOrders
|
||||
allOrdersList.value = mappedOrders
|
||||
|
||||
// Update tab counts
|
||||
updateTabsCounts(mappedOrders)
|
||||
@@ -397,14 +485,17 @@ const loadOrders = async () => {
|
||||
|
||||
// 生命周期
|
||||
onLoad((options) => {
|
||||
if (options['status'] != null) {
|
||||
const status = options['status'] as string
|
||||
if (options == null) return
|
||||
const statusVal = options['status']
|
||||
if (statusVal != null) {
|
||||
const status = statusVal as string
|
||||
if (['all', 'pending', 'shipping', 'delivering', 'completed', 'cancelled'].includes(status)) {
|
||||
activeTab.value = status
|
||||
}
|
||||
}
|
||||
if (options['type'] != null) {
|
||||
const type = options['type'] as string
|
||||
const typeVal = options['type']
|
||||
if (typeVal != null) {
|
||||
const type = typeVal as string
|
||||
if (type === 'pending') activeTab.value = 'pending'
|
||||
else if (type === 'shipped') activeTab.value = 'delivering' // 映射到待收货
|
||||
else if (type === 'review') activeTab.value = 'completed' // 映射到已完成
|
||||
@@ -500,6 +591,8 @@ const getStatusText = (status: number): string => {
|
||||
if (status == 3) return '待收货'
|
||||
if (status == 4) return '已完成'
|
||||
if (status == 5) return '已取消'
|
||||
if (status == 6) return '退款中'
|
||||
if (status == 7) return '已退款'
|
||||
return '未知状态'
|
||||
}
|
||||
|
||||
@@ -510,6 +603,8 @@ const getStatusClass = (status: number): string => {
|
||||
if (status == 3) return 'status-delivering'
|
||||
if (status == 4) return 'status-completed'
|
||||
if (status == 5) return 'status-cancelled'
|
||||
if (status == 6) return 'status-refunding'
|
||||
if (status == 7) return 'status-refunded'
|
||||
return 'status-unknown'
|
||||
}
|
||||
|
||||
@@ -705,10 +800,12 @@ const goShopping = () => {
|
||||
<style>
|
||||
.orders-page {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #f5f5f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 头部 */
|
||||
@@ -774,62 +871,73 @@ const goShopping = () => {
|
||||
}
|
||||
|
||||
/* 标签页 */
|
||||
.order-tabs {
|
||||
.order-tabs-fixed-container {
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
/* position: sticky; removed */
|
||||
/* top: 50px; removed */
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tab-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 10px;
|
||||
/* width: max-content; removed */
|
||||
/* min-width: 100%; removed */
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
/* 移除 flex: 1,改为自适应宽度或固定最小宽度 */
|
||||
padding: 15px 15px; /* 增加水平内边距 */
|
||||
.tab-item-fixed {
|
||||
padding: 15px 15px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap; /* 防止文字换行 */
|
||||
flex-shrink: 0; /* 防止被压缩 */
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
.tab-item-fixed.active {
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tab-item.active::after {
|
||||
/* content: ''; removed */
|
||||
/* content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background-color: #ff5000; */
|
||||
.tab-scroll-mobile {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
/* 移除 width: 0,改用更稳健的安卓适配方式 */
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.tab-container-mobile {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
/* 加上这个确保容器能够横向撑开 */
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.tab-item-mobile {
|
||||
padding: 15px 15px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row; /* 显式声明 */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0; /* 绝对不能被压缩,否则无法滚动 */
|
||||
}
|
||||
|
||||
.tab-item-mobile.active {
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.active-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
bottom: 0px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
height: 3px;
|
||||
background-color: #ff5000;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tab-name {
|
||||
@@ -850,6 +958,8 @@ const goShopping = () => {
|
||||
/* 内容区 */
|
||||
.orders-content {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
@@ -859,7 +969,6 @@ const goShopping = () => {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
@@ -941,6 +1050,14 @@ const goShopping = () => {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status-refunding {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.status-refunded {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 订单商品 */
|
||||
.order-products {
|
||||
padding: 15px;
|
||||
@@ -948,7 +1065,9 @@ const goShopping = () => {
|
||||
|
||||
.order-product {
|
||||
display: flex;
|
||||
flex-direction: row; /* 显式声明横向排列 */
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.order-product:last-child {
|
||||
@@ -959,30 +1078,46 @@ const goShopping = () => {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
margin-right: 15px;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0; /* 防止图片被压缩 */
|
||||
}
|
||||
|
||||
.product-info {
|
||||
flex: 1;
|
||||
flex: 1; /* 占据右侧剩余所有空间 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 80px;
|
||||
overflow: hidden; /* 防止文字溢出 */
|
||||
}
|
||||
|
||||
.product-top-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 15px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
lines: 2;
|
||||
}
|
||||
|
||||
.product-spec {
|
||||
font-size: 13px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.product-footer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
@@ -992,8 +1127,8 @@ const goShopping = () => {
|
||||
}
|
||||
|
||||
.product-quantity {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 订单信息 */
|
||||
@@ -1128,6 +1263,114 @@ const goShopping = () => {
|
||||
}
|
||||
|
||||
/* 响应式适配 */
|
||||
@media screen and (min-width: 768px) {
|
||||
.order-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.order-card {
|
||||
width: 48%;
|
||||
margin: 0 1% 20px 1%;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
.order-card {
|
||||
width: 31%;
|
||||
margin: 0 1% 20px 1%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 订单卡片新样式 */
|
||||
.order-card-header {
|
||||
padding: 12px 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.shop-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shop-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
lines: 1;
|
||||
}
|
||||
|
||||
.arrow-right {
|
||||
font-size: 14px;
|
||||
color: #ccc;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.product-title-row, .product-spec-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.product-title-row {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.product-spec-row {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.order-summary {
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.order-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.summary-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.summary-price {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
.tab-item {
|
||||
padding: 0 10px;
|
||||
|
||||
Reference in New Issue
Block a user