Files
medical-mall/doc_mall/consumer/backup_pages/profile copy.uvue

1337 lines
37 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 娑堣垂鑰呯 - 涓汉涓績 -->
<template>
<view class="consumer-profile">
<!-- 鏅鸿兘椤堕儴瀵艰埅鏍?- 涓庢秷鎭〉淇濇寔涓€鑷?-->
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-container">
<!-- 澶村儚 -->
<image
:src="userInfo.avatar_url != '' ? userInfo.avatar_url : '/static/default-avatar.png'"
class="nav-avatar"
@click="editProfile"
/>
<!-- 鐢ㄦ埛淇℃伅妯悜鎺掑垪 (鍚嶅瓧銆佺Н鍒嗐€佷綑棰濄€佷紭鎯犲埜) -->
<view class="nav-user-stats">
<text class="nav-user-name">{{ userInfo.nickname != '' ? userInfo.nickname : userInfo.phone }}</text>
<view class="nav-stat-item" @click="goToPoints">
<text class="nav-stat-label">绉垎</text>
<text class="nav-stat-value">{{ userStats.points }}</text>
</view>
<view class="nav-stat-item">
<text class="nav-stat-label">浣欓</text>
<text class="nav-stat-value" @click="goToWallet">楼{{ userStats.balance }}</text>
</view>
<view class="nav-stat-item" @click="goToCoupons">
<text class="nav-stat-label">鍒?/text>
<text class="nav-stat-value">{{ serviceCounts.coupons }}</text>
</view>
</view>
<!-- 璁剧疆鎸夐挳 (鍙充晶) -->
<view class="nav-actions">
<view class="action-btn" @click="goToSettings">
<text class="action-icon">鈿欙笍</text>
</view>
</view>
</view>
</view>
<scroll-view class="profile-scroll-content" :scroll-y="true">
<!-- 瀵艰埅鏍忓崰浣嶇 - 闇€瑕佸寘鍚玸tatusBarHeight + 瀵艰埅鏍忛珮搴?4px -->
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 鎴戠殑鏈嶅姟 (绉诲埌璁㈠崟涓婃柟) -->
<view class="my-services" style="margin-top: 10px;">
<view class="section-title">鎴戠殑鏈嶅姟</view>
<view class="service-grid">
<view class="service-item" @click="goToCoupons">
<text class="service-icon">馃帿</text>
<text class="service-text">浼樻儬鍒?/text>
<text v-if="serviceCounts.coupons > 0" class="service-badge">{{ serviceCounts.coupons }}</text>
</view>
<view class="service-item" @click="goToAddress">
<text class="service-icon">馃搷</text>
<text class="service-text">鏀惰揣鍦板潃</text>
</view>
<view class="service-item" @click="goToFavorites">
<text class="service-icon">鉂わ笍</text>
<text class="service-text">鎴戠殑鏀惰棌</text>
<text v-if="serviceCounts.favorites > 0" class="service-badge">{{ serviceCounts.favorites }}</text>
</view>
<view class="service-item" @click="goToFootprint">
<text class="service-icon">馃懀</text>
<text class="service-text">娴忚瓒宠抗</text>
</view>
<view class="service-item" @click="goToRefund">
<text class="service-icon">馃攧</text>
<text class="service-text">閫€娆?鍞悗</text>
</view>
<view class="service-item" @click="goToOrderReviews">
<text class="service-icon">馃摑</text>
<text class="service-text">璇勪环</text>
</view>
<view class="service-item" @click="goToFollowedShops">
<text class="service-icon">猸?/text>
<text class="service-text">鍏虫敞搴楅摵</text>
</view>
<view class="service-item" @click="goToSubscriptions">
<text class="service-icon">馃摫</text>
<text class="service-text">杞欢璁㈤槄</text>
</view>
</view>
</view>
<!-- 璁㈠崟鐘舵€佸揩鎹峰叆鍙?-->
<view class="order-shortcuts">
<view class="section-title">鎴戠殑璁㈠崟</view>
<view class="order-tabs">
<view class="order-tab" :class="{ active: currentOrderTab === 'all' }" @click="switchOrderTab('all')">
<text class="tab-icon">馃搵</text>
<text class="tab-text">鍏ㄩ儴</text>
<text v-if="orderCounts.total > 0" class="tab-badge">{{ orderCounts.total }}</text>
</view>
<view class="order-tab" :class="{ active: currentOrderTab === 'pending' }" @click="switchOrderTab('pending')">
<text class="tab-icon">馃挵</text>
<text class="tab-text">寰呮敮浠?/text>
<text v-if="orderCounts.pending > 0" class="tab-badge">{{ orderCounts.pending }}</text>
</view>
<view class="order-tab" :class="{ active: currentOrderTab === 'toship' }" @click="switchOrderTab('toship')">
<text class="tab-icon">馃殮</text>
<text class="tab-text">寰呭彂璐?/text>
<text v-if="orderCounts.toship > 0" class="tab-badge">{{ orderCounts.toship }}</text>
</view>
<view class="order-tab" :class="{ active: currentOrderTab === 'shipped' }" @click="switchOrderTab('shipped')">
<text class="tab-icon">馃摝</text>
<text class="tab-text">寰呮敹璐?/text>
<text v-if="orderCounts.shipped > 0" class="tab-badge">{{ orderCounts.shipped }}</text>
</view>
</view>
</view>
<!-- 鏈€杩戣鍗曞垪琛?(鏍规嵁Tab鍒囨崲鏄剧ず) -->
<view class="recent-orders">
<view class="section-header">
<text class="section-title">{{ getOrderSectionTitle() }}</text>
<text class="view-all" @click="goToOrders(currentOrderTab)">鏌ョ湅鏇村 ></text>
</view>
<view v-if="filteredOrders.length === 0" class="empty-orders">
<text class="empty-text">鏆傛棤鐩稿叧璁㈠崟璁板綍</text>
<button class="start-shopping" @click="goShopping">鍘婚€涢€?/button>
</view>
<view v-for="order in filteredOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order)">
<view class="order-header">
<text class="order-no">璁㈠崟鍙? {{ order.order_no }}</text>
<text class="order-status" :class="getOrderStatusClass(order.status)">{{ getOrderStatusText(order.status) }}</text>
</view>
<view class="order-content">
<image :src="getOrderMainImage(order)" class="order-image" mode="aspectFill" />
<view class="order-info">
<text class="order-title">{{ getOrderTitle(order) }}</text>
<text class="order-amount">楼{{ order.actual_amount }}</text>
<text class="order-time">{{ formatTime(order.created_at) }}</text>
</view>
</view>
<view class="order-actions">
<button v-if="order.status === 1" class="action-btn pay" @click.stop="payOrder(order)">绔嬪嵆鏀粯</button>
<button v-if="order.status === 3" class="action-btn confirm" @click.stop="confirmReceive(order)">纭鏀惰揣</button>
<button v-if="order.status === 4" class="action-btn review" @click.stop="reviewOrder(order)">璇勪环</button>
</view>
</view>
</view>
<!-- 鎴戠殑鏈嶅姟 -->
<!-- <view class="my-services">
<view class="section-title">鎴戠殑鏈嶅姟</view>
<view class="service-grid">
<view class="service-item" @click="goToCoupons">
<text class="service-icon">馃帿</text>
<text class="service-text">浼樻儬鍒?/text>
<text v-if="serviceCounts.coupons > 0" class="service-badge">{{ serviceCounts.coupons }}</text>
</view>
<view class="service-item" @click="goToAddress">
<text class="service-icon">馃搷</text>
<text class="service-text">鏀惰揣鍦板潃</text>
</view>
<view class="service-item" @click="goToFavorites">
<text class="service-icon">鉂わ笍</text>
<text class="service-text">鎴戠殑鏀惰棌</text>
<text v-if="serviceCounts.favorites > 0" class="service-badge">{{ serviceCounts.favorites }}</text>
</view>
<view class="service-item" @click="goToFootprint">
<text class="service-icon">馃懀</text>
<text class="service-text">娴忚瓒宠抗</text>
</view>
<view class="service-item" @click="goToRefund">
<text class="service-icon">馃攧</text>
<text class="service-text">閫€娆?鍞悗</text>
</view>
<view class="service-item" @click="contactService">
<text class="service-icon">馃挰</text>
<text class="service-text">鍦ㄧ嚎瀹㈡湇</text>
</view>
<view class="service-item" @click="goToMySubscriptions">
<text class="service-icon">馃З</text>
<text class="service-text">鎴戠殑璁㈤槄</text>
</view>
<view class="service-item" @click="goToSubscriptions">
<text class="service-icon">馃З</text>
<text class="service-text">杞欢璁㈤槄</text>
</view>
</view>
</view> -->
<!-- 娑堣垂缁熻 -->
<view class="consumption-stats">
<view class="section-title">娑堣垂缁熻</view>
<view class="stats-period">
<text v-for="period in statsPeriods" :key="period.key"
class="period-tab"
:class="{ active: activeStatsPeriod === period.key }"
@click="switchStatsPeriod(period.key)">{{ period.label }}</text>
</view>
<view class="stats-content">
<view class="stat-card">
<text class="stat-value">楼{{ currentStats.total_amount }}</text>
<text class="stat-label">鎬绘秷璐?/text>
</view>
<view class="stat-card">
<text class="stat-value">{{ currentStats.order_count }}</text>
<text class="stat-label">璁㈠崟鏁?/text>
</view>
<view class="stat-card">
<text class="stat-value">楼{{ currentStats.avg_amount }}</text>
<text class="stat-label">骞冲潎娑堣垂</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ currentStats.save_amount }}</text>
<text class="stat-label">鑺傜渷閲戦</text>
</view>
</view>
</view>
<!-- 璐︽埛瀹夊叏 -->
<!-- <view class="account-security">
<view class="section-title">璐︽埛瀹夊叏</view>
<view class="security-items">
<view class="security-item" @click="changePassword">
<text class="security-icon">馃敀</text>
<text class="security-text">淇敼瀵嗙爜</text>
<text class="security-arrow">></text>
</view>
<view class="security-item" @click="bindPhone">
<text class="security-icon">馃摫</text>
<text class="security-text">鎵嬫満缁戝畾</text>
<view class="security-status">
<text class="status-text" :class="{ bound: userInfo.phone }">{{ userInfo.phone ? '宸茬粦瀹? : '鏈粦瀹? }}</text>
<text class="security-arrow">></text>
</view>
</view>
<view class="security-item" @click="bindEmail">
<text class="security-icon">馃摟</text>
<text class="security-text">閭缁戝畾</text>
<view class="security-status">
<text class="status-text" :class="{ bound: userInfo.email }">{{ userInfo.email ? '宸茬粦瀹? : '鏈粦瀹? }}</text>
<text class="security-arrow">></text>
</view>
</view>
</view>
</view> -->
</scroll-view>
</view>
</template>
<script>
import { UserType } from '@/types/mall-types.uts'
import supabaseService from '@/utils/supabaseService.uts'
type UserStatsType = {
points: number
balance: number
level: number
}
type OrderCountsType = {
total: number
pending: number
toship: number
shipped: number
review: number
}
type ServiceCountsType = {
coupons: number
favorites: number
}
type ConsumptionStatsType = {
total_amount: number
order_count: number
avg_amount: number
save_amount: number
}
type StatsPeriodType = {
key: string
label: string
}
type OrderItemType = {
id: string
order_no: string
status: number
actual_amount: number
created_at: string
ml_order_items: any | null
}
export default {
data() {
return {
userInfo: {
id: '',
phone: '',
email: '',
nickname: '',
avatar_url: '',
gender: 0,
user_type: 0,
status: 0,
created_at: ''
} as UserType,
userStats: {
points: 0,
balance: 0,
level: 1
} as UserStatsType,
orderCounts: {
total: 0,
pending: 0,
toship: 0,
shipped: 0,
review: 0
} as OrderCountsType,
serviceCounts: {
coupons: 0,
favorites: 0
} as ServiceCountsType,
recentOrders: [] as Array<OrderItemType>,
statsPeriods: [
{ key: 'month', label: '鏈湀' },
{ key: 'quarter', label: '鏈搴? },
{ key: 'year', label: '鏈勾' },
{ key: 'all', label: '鍏ㄩ儴' }
] as Array<StatsPeriodType>,
activeStatsPeriod: 'month',
currentStats: {
total_amount: 0,
order_count: 0,
avg_amount: 0,
save_amount: 0
} as ConsumptionStatsType,
statusBarHeight: 0,
currentOrderTab: 'all' as string,
allOrders: [] as Array<OrderItemType>
}
},
onLoad() {
this.initPage()
this.loadUserProfile()
this.loadOrders()
// 鐩戝惉璁㈠崟鏇存柊浜嬩欢
uni.$on('orderUpdated', this.handleOrderUpdated)
},
onShow() {
this.refreshData()
},
onUnload() {
// 绉婚櫎浜嬩欢鐩戝惉
uni.$off('orderUpdated', this.handleOrderUpdated)
},
computed: {
filteredOrders(): Array<OrderItemType> {
const result: Array<OrderItemType> = []
if (this.currentOrderTab === 'all') {
for (let i: number = 0; i < this.allOrders.length; i++) {
result.push(this.allOrders[i])
}
return result
}
let targetStatus: number = 0
if (this.currentOrderTab === 'pending') {
targetStatus = 1
} else if (this.currentOrderTab === 'toship') {
targetStatus = 2
} else if (this.currentOrderTab === 'shipped') {
targetStatus = 3
} else if (this.currentOrderTab === 'review') {
targetStatus = 4
} else {
return result
}
for (let i: number = 0; i < this.allOrders.length; i++) {
if (this.allOrders[i].status === targetStatus) {
result.push(this.allOrders[i])
}
}
return result
}
},
methods: {
async loadOrders() {
try {
const orders = await supabaseService.getOrders()
const mappedOrders: Array<OrderItemType> = []
for (let i: number = 0; i < orders.length; i++) {
const rawItem = orders[i]
const o = JSON.parse(JSON.stringify(rawItem)) as UTSJSONObject
let status = o.getNumber('status')
if (status == null) {
const orderStatus = o.getNumber('order_status')
status = orderStatus != null ? orderStatus : 0
}
let actualAmount = o.getNumber('actual_amount')
if (actualAmount == null) {
const totalAmount = o.getNumber('total_amount')
actualAmount = totalAmount != null ? totalAmount : 0
}
const orderItem: OrderItemType = {
id: o.getString('id') ?? '',
order_no: o.getString('order_no') ?? '',
status: status,
actual_amount: actualAmount,
created_at: o.getString('created_at') ?? '',
ml_order_items: o.get('ml_order_items')
}
mappedOrders.push(orderItem)
}
for (let i: number = 0; i < mappedOrders.length; i++) {
for (let j: number = i + 1; j < mappedOrders.length; j++) {
const dateA = mappedOrders[i]['created_at'] as string
const dateB = mappedOrders[j]['created_at'] as string
const timeA = new Date(dateA != null ? dateA : '1970-01-01').getTime()
const timeB = new Date(dateB != null ? dateB : '1970-01-01').getTime()
if (timeA < timeB) {
const temp = mappedOrders[i]
mappedOrders[i] = mappedOrders[j]
mappedOrders[j] = temp
}
}
}
this.allOrders = mappedOrders
const recentList: Array<OrderItemType> = []
const limit = mappedOrders.length < 5 ? mappedOrders.length : 5
for (let i: number = 0; i < limit; i++) {
recentList.push(mappedOrders[i])
}
this.recentOrders = recentList
let total = 0
let pending = 0
let toship = 0
let shipped = 0
let review = 0
for (let i: number = 0; i < mappedOrders.length; i++) {
total++
const status = mappedOrders[i].status
if (status === 1) pending++
else if (status === 2) toship++
else if (status === 3) shipped++
else if (status === 4) review++
}
this.orderCounts = {
total: total,
pending: pending,
toship: toship,
shipped: shipped,
review: review
}
} catch (e) {
console.error('鍔犺浇璁㈠崟寮傚父', e)
}
},
// 鍒囨崲璁㈠崟Tab
switchOrderTab(tab: string) {
this.currentOrderTab = tab
},
// 鑾峰彇褰撳墠璁㈠崟閮ㄥ垎鏍囬
getOrderSectionTitle(): string {
if (this.currentOrderTab === 'all') return '鍏ㄩ儴璁㈠崟'
if (this.currentOrderTab === 'pending') return '寰呮敮浠樿鍗?
if (this.currentOrderTab === 'shipped') return '寰呮敹璐ц鍗?
if (this.currentOrderTab === 'review') return '寰呰瘎浠疯鍗?
return '鎴戠殑璁㈠崟'
},
initPage() {
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight ?? 0
},
async loadUserProfile() {
try {
// 鑾峰彇鐢ㄦ埛璧勬枡
const profile = await supabaseService.getUserProfile()
if (profile != null) {
// 鏄犲皠瀛楁
let uId = ''
let uPhone = ''
let uEmail = ''
let uNickname = ''
let uAvatar = ''
let uGender = 0
if (profile instanceof UTSJSONObject) {
uId = profile.getString('user_id') ?? ''
uPhone = profile.getString('phone') ?? ''
uEmail = profile.getString('email') ?? ''
uNickname = profile.getString('nickname') ?? ''
uAvatar = profile.getString('avatar_url') ?? ''
uGender = profile.getNumber('gender') ?? 0
} else {
// 蹇呴』浣跨敤 JSON.parse(JSON.stringify()) 杞崲涓?UTSJSONObject
const profileObj = JSON.parse(JSON.stringify(profile)) as UTSJSONObject
uId = profileObj.getString('user_id') ?? ''
uPhone = profileObj.getString('phone') ?? ''
uEmail = profileObj.getString('email') ?? ''
uNickname = profileObj.getString('nickname') ?? ''
uAvatar = profileObj.getString('avatar_url') ?? ''
uGender = profileObj.getNumber('gender') ?? 0
}
if (uNickname === '' && uPhone !== '') {
uNickname = uPhone.substring(0, 3) + '****' + uPhone.substring(7)
}
this.userInfo = {
id: uId,
phone: uPhone,
email: uEmail,
nickname: uNickname != '' ? uNickname : '寰俊鐢ㄦ埛',
avatar_url: uAvatar != '' ? uAvatar : '/static/default-avatar.png',
gender: uGender,
user_type: 1,
status: 1,
created_at: new Date().toISOString()
} as UserType
} else {
// 濡傛灉鑾峰彇澶辫触锛堟湭鐧诲綍鎴栨棤妗锛夛紝灏濊瘯鑾峰彇褰撳墠鐧诲綍ID
const userId = supabaseService.getCurrentUserId()
if (userId != null) {
this.userInfo.id = userId
this.userInfo.nickname = '鐢ㄦ埛' + userId.substring(0, 4)
} else {
this.userInfo.nickname = '鏈櫥褰?
}
}
// 鑾峰彇绉垎鍜屼綑棰濓紙骞惰鑾峰彇锛?
const [balance, points] = await Promise.all([
supabaseService.getUserBalance(),
supabaseService.getUserPoints()
])
this.userStats = {
points: points,
balance: balance,
level: this.calculateLevel(points) // 鏍规嵁绉垎璁$畻绛夌骇
} as UserStatsType
} catch (e) {
console.error('鍔犺浇鐢ㄦ埛淇℃伅澶辫触', e)
// 淇濇寔榛樿鎴栨樉绀洪敊璇?
}
},
calculateLevel(points: number): number {
if (points < 1000) return 0
if (points < 5000) return 1
if (points < 20000) return 2
if (points < 50000) return 3
return 4
},
loadConsumptionStats() {
if (this.activeStatsPeriod === 'month') {
this.currentStats = {
total_amount: 1280.50,
order_count: 8,
avg_amount: 160.06,
save_amount: 85.20
} as ConsumptionStatsType
} else if (this.activeStatsPeriod === 'quarter') {
this.currentStats = {
total_amount: 3680.80,
order_count: 18,
avg_amount: 204.49,
save_amount: 256.30
} as ConsumptionStatsType
} else if (this.activeStatsPeriod === 'year') {
this.currentStats = {
total_amount: 15680.90,
order_count: 56,
avg_amount: 280.02,
save_amount: 986.50
} as ConsumptionStatsType
} else {
this.currentStats = {
total_amount: 25680.50,
order_count: 89,
avg_amount: 288.55,
save_amount: 1580.20
} as ConsumptionStatsType
}
},
refreshData() {
// 鍒锋柊椤甸潰鏁版嵁
this.loadUserProfile()
this.loadOrders()
this.updateCouponCount() // 鏇存柊浼樻儬鍒告暟閲?
},
async updateCouponCount() {
// 浠?Supabase 鑾峰彇鐪熷疄鐨勪紭鎯犲埜鏁伴噺
try {
const count = await supabaseService.getUserCouponCount()
this.serviceCounts.coupons = count
} catch (e) {
console.error('鑾峰彇浼樻儬鍒告暟閲忓け璐?, e)
this.serviceCounts.coupons = 0
}
},
getUserLevel(): string {
const levels = ['鏂版墜', '閾滅墝浼氬憳', '閾剁墝浼氬憳', '閲戠墝浼氬憳', '閽荤煶浼氬憳']
if (this.userStats.level >= 0 && this.userStats.level < levels.length) {
return levels[this.userStats.level]
}
return '鏂版墜'
},
getOrderStatusText(status: number): string {
const statusTexts = ['寮傚父', '寰呮敮浠?, '寰呭彂璐?, '寰呮敹璐?, '宸插畬鎴?, '宸插彇娑?]
if (status >= 0 && status < statusTexts.length) {
return statusTexts[status]
}
return '鏈煡'
},
getOrderStatusClass(status: number): string {
const statusClasses = ['error', 'pending', 'processing', 'shipping', 'completed', 'cancelled']
if (status >= 0 && status < statusClasses.length) {
return statusClasses[status]
}
return 'error'
},
getOrderMainImage(order: OrderItemType): string {
const itemsRaw = order.ml_order_items
if (itemsRaw == null) return '/static/product1.jpg'
const items = itemsRaw as any[]
if (items.length > 0) {
const firstItem = items[0]
const itemStr = JSON.stringify(firstItem)
const itemParsed = JSON.parse(itemStr)
if (itemParsed == null) return '/static/product1.jpg'
const itemObj = itemParsed as UTSJSONObject
const imgUrl = itemObj.getString('image_url')
const prodImg = itemObj.getString('product_image')
const img = (imgUrl != null && imgUrl !== '') ? imgUrl : prodImg
if (img != null && img !== '') return img
}
return '/static/product1.jpg'
},
getOrderTitle(order: OrderItemType): string {
const itemsRaw = order.ml_order_items
if (itemsRaw == null) return '绮鹃€夊晢鍝?
const items = itemsRaw as any[]
if (items.length > 0) {
const firstItem = items[0]
const itemStr = JSON.stringify(firstItem)
const itemParsed = JSON.parse(itemStr)
if (itemParsed == null) return '绮鹃€夊晢鍝?
const itemObj = itemParsed as UTSJSONObject
const pName = itemObj.getString('product_name')
const name = (pName != null && pName !== '') ? pName : '鍟嗗搧'
if (items.length > 1) {
return name + ' 绛? + items.length + '浠跺晢鍝?
}
return name
}
return '绮鹃€夊晢鍝?
},
formatTime(timeStr: string): string {
const date = new Date(timeStr)
const now = new Date()
const diff = now.getTime() - date.getTime()
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (days === 0) {
return '浠婂ぉ'
} else if (days === 1) {
return '鏄ㄥぉ'
} else {
return `${days}澶╁墠`
}
},
switchStatsPeriod(period: string) {
this.activeStatsPeriod = period
this.loadConsumptionStats()
},
editProfile() {
uni.navigateTo({
url: '/pages/mall/consumer/edit-profile'
})
},
// 璺宠浆璁剧疆
goToSettings() {
uni.navigateTo({
url: '/pages/mall/consumer/settings'
})
},
// 璺宠浆閽卞寘
goToWallet() {
uni.navigateTo({
url: '/pages/mall/consumer/wallet'
})
},
goToOrders(type: string) {
uni.navigateTo({
url: `/pages/mall/consumer/orders?type=${type}`
})
},
goShopping() {
uni.switchTab({
url: '/pages/mall/consumer/index'
})
},
viewOrderDetail(order: OrderItemType) {
uni.navigateTo({
url: `/pages/mall/consumer/order-detail?orderId=${order.id}`
})
},
payOrder(order: OrderItemType) {
uni.navigateTo({
url: `/pages/mall/consumer/payment?orderId=${order.id}`
})
},
confirmReceive(order: OrderItemType) {
uni.showModal({
title: '纭鏀惰揣',
content: '纭宸叉敹鍒板晢鍝佸悧锛?,
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '纭鏀惰揣鎴愬姛',
icon: 'success'
})
this.refreshData()
}
}
})
},
reviewOrder(order: OrderItemType) {
uni.navigateTo({
url: `/pages/mall/consumer/review?orderId=${order.id}`
})
},
goToCoupons() {
uni.navigateTo({
url: '/pages/mall/consumer/coupons'
})
},
goToPoints() {
uni.navigateTo({
url: '/pages/mall/consumer/points/index'
})
},
goToAddress() {
// 鏆傛椂璺宠浆鍒拌缃〉鐨勫湴鍧€绠$悊
uni.navigateTo({
url: '/pages/mall/consumer/address-list'
})
},
goToFavorites() {
uni.navigateTo({
url: '/pages/mall/consumer/favorites'
})
},
goToFootprint() {
uni.navigateTo({
url: '/pages/mall/consumer/footprint'
})
},
goToRefund() {
uni.navigateTo({
url: '/pages/mall/consumer/orders?type=refund'
})
},
contactService() {
uni.navigateTo({
url: '/pages/mall/service/chat'
})
},
goToOrderReviews() {
uni.navigateTo({
url: '/pages/mall/consumer/orders?type=review'
})
},
goToMySubscriptions() {
uni.navigateTo({
url: '/pages/mall/consumer/subscription/my-subscriptions'
})
},
goToFollowedShops() {
uni.navigateTo({
url: '/pages/mall/consumer/subscription/followed-shops'
})
},
goToSubscriptions() {
uni.navigateTo({
url: '/pages/mall/consumer/subscription/plan-list'
})
},
changePassword() {
uni.navigateTo({
url: '/pages/mall/consumer/change-password'
})
},
bindPhone() {
uni.navigateTo({
url: '/pages/mall/consumer/bind-phone'
})
},
bindEmail() {
uni.navigateTo({
url: '/pages/mall/consumer/bind-email'
})
},
handleOrderUpdated(data: any) {
console.log('鏀跺埌璁㈠崟鏇存柊浜嬩欢:', data)
this.refreshData()
const dataObj = data as UTSJSONObject
const status = dataObj.getNumber('status')
if (status === 1) {
uni.showToast({
title: '璁㈠崟宸蹭繚瀛樺埌寰呮敮浠?,
icon: 'success'
})
} else if (status === 2) {
uni.showToast({
title: '鏀粯鎴愬姛锛岃鍗曞緟鍙戣揣',
icon: 'success'
})
}
}
}
}
</script>
<style>
.consumer-profile {
background-color: #f5f5f5;
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.profile-scroll-content {
flex: 1;
}
/* 鏅鸿兘椤堕儴瀵艰埅鏍?*/
.smart-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #4CAF50;
z-index: 1000;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.nav-container {
padding: 0 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1400px;
margin: 0 auto;
height: 44px;
}
/* 瀵艰埅鏍忕敤鎴蜂俊鎭尯鍩?*/
.nav-user-stats {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start; /* 闈犲乏瀵归綈锛岀揣璺熷ご鍍?*/
margin-right: 12px;
overflow: hidden; /* 闃叉婧㈠嚭 */
}
.nav-user-name {
font-size: 16px;
font-weight: bold;
color: white;
margin-right: 12px;
/* max-width: 30%; REMOVED */
width: 100px; /* Use fixed width approx */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.nav-stat-item {
display: flex;
flex-direction: row;
align-items: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 2px 8px;
margin-right: 8px;
flex-shrink: 0; /* 闃叉琚帇缂?*/
}
.nav-stat-label {
font-size: 11px;
color: rgba(255, 255, 255, 0.9);
margin-right: 4px;
}
.nav-stat-value {
font-size: 12px;
font-weight: bold;
color: white;
}
.nav-avatar {
width: 36px;
height: 36px;
border-radius: 18px;
border: 2px solid rgba(255, 255, 255, 0.8);
margin-right: 12px;
flex-shrink: 0;
}
.nav-actions {
display: flex;
flex-direction: row;
align-items: center;
flex-shrink: 0;
}
.action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.2);
border-radius: 16px;
/* cursor: pointer; REMOVED */
}
.action-icon {
font-size: 18px;
color: white;
}
/* 瀵艰埅鏍忓崰浣嶇 */
.navbar-placeholder {
width: 100%;
flex-shrink: 0;
}
.order-shortcuts, .recent-orders, .my-services, .consumption-stats, .account-security {
background-color: #fff;
margin: 15px 15px; /* 椤堕儴鎭㈠ margin */
border-radius: 12px; /* 缁熶竴鍦嗚 */
padding: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 16px;
}
.order-tabs {
display: flex;
flex-direction: row; /* 鏄惧紡妯悜鎺掑垪 */
justify-content: space-between;
width: 100%;
}
.order-tab {
flex: 1;
display: flex;
flex-direction: row; /* 鍏抽敭锛氭敼涓烘í鍚戞帓鍒?*/
align-items: center;
justify-content: center; /* 灞呬腑 */
position: relative;
padding: 8px 0;
}
.tab-icon {
font-size: 20px;
margin-right: 6px; /* 鍥炬爣鍜屾枃瀛楅棿璺?*/
margin-bottom: 0; /* 绉婚櫎搴曢儴闂磋窛 */
}
.tab-text {
font-size: 14px;
color: #333;
}
/* 閫変腑鐘舵€佺殑Tab */
.order-tab.active .tab-icon,
.order-tab.active .tab-text {
color: #4CAF50;
font-weight: bold;
}
.order-tab.active {
background-color: #f0f9f0;
border-radius: 8px;
}
.tab-badge {
position: absolute;
top: 0;
right: 10%; /* 璋冩暣浣嶇疆 */
background-color: #ff5000;
color: #fff;
font-size: 10px;
padding: 1px 5px;
border-radius: 8px;
min-width: 14px;
text-align: center;
line-height: 1.2;
}
.empty-orders {
text-align: center;
padding: 80rpx 0;
}
.empty-text {
font-size: 28rpx;
color: #999;
margin-bottom: 30rpx;
}
.start-shopping {
background-color: #007aff;
color: #fff;
padding: 20rpx 40rpx;
border-radius: 25rpx;
font-size: 26rpx;
border: none;
}
.order-item {
padding: 25rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.order-item:last-child {
border-bottom: none;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.order-no {
font-size: 26rpx;
color: #333;
}
.order-status {
font-size: 24rpx;
padding: 6rpx 12rpx;
border-radius: 10rpx;
color: #fff;
}
.order-status.pending {
background-color: #ff5000;
}
.order-status.processing {
background-color: #2196f3;
}
.order-status.shipping {
background-color: #9c27b0;
}
.order-status.completed {
background-color: #4caf50;
}
.order-content {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.order-image {
width: 100rpx;
height: 100rpx;
border-radius: 8rpx;
margin-right: 20rpx;
}
.order-info {
flex: 1;
}
.order-title {
font-size: 26rpx;
color: #333;
margin-bottom: 8rpx;
}
.order-amount {
font-size: 28rpx;
color: #ff5000;
font-weight: bold;
margin-bottom: 5rpx;
}
.order-time {
font-size: 22rpx;
color: #999;
}
.order-actions {
display: flex;
justify-content: flex-end;
/* gap: 15rpx; REMOVED */
}
.order-actions .action-btn {
margin-left: 15px; /* Replace gap */
}
.action-btn {
padding: 12rpx 25rpx;
border-radius: 20rpx;
font-size: 24rpx;
border: none;
}
.action-btn.pay {
background-color: #ff5000;
color: #fff;
}
.action-btn.confirm {
background-color: #4caf50;
color: #fff;
}
.action-btn.review {
background-color: #ff5000;
color: #fff;
}
.service-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap; /* 鍏佽鎹㈣ */
/* gap: 16px 0; REMOVED */
justify-content: flex-start; /* 浠庡乏寮€濮嬫帓鍒?*/
}
.service-item {
width: 25%; /* 姣忚4涓?*/
display: flex;
flex-direction: column;
align-items: center;
position: relative;
box-sizing: border-box; /* 纭繚 padding 涓嶅奖鍝嶅搴?*/
margin-bottom: 16px; /* Replace gap row */
}
.service-icon {
font-size: 48rpx;
margin-bottom: 15rpx;
}
.service-text {
font-size: 24rpx;
color: #333;
}
.service-badge {
position: absolute;
top: -5rpx;
right: 10rpx;
background-color: #ff5000;
color: #fff;
font-size: 18rpx;
padding: 4rpx 6rpx;
border-radius: 8rpx;
min-width: 24rpx;
text-align: center;
}
.stats-period {
display: flex;
/* gap: 30rpx; REMOVED */
margin-bottom: 30rpx;
}
.period-tab {
font-size: 26rpx;
color: #666;
padding: 12rpx 24rpx;
border-radius: 20rpx;
margin-right: 30rpx; /* Replace gap */
background-color: #f0f0f0;
}
.period-tab.active {
background-color: #007aff;
color: #fff;
}
.stats-content {
display: flex;
/* gap: 20rpx; REMOVED */
}
.stat-card {
flex: 1;
text-align: center;
padding: 30rpx 0;
background-color: #f8f9fa;
border-radius: 10rpx;
margin-right: 20rpx; /* Replace gap */
}
.stat-card:last-child {
margin-right: 0;
}
.stat-value {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 22rpx;
color: #666;
}
.security-items {
margin-top: 25rpx;
}
.security-item {
display: flex;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f5f5f5;
}
.security-item:last-child {
border-bottom: none;
}
.security-icon {
font-size: 32rpx;
margin-right: 20rpx;
}
.security-text {
flex: 1;
font-size: 28rpx;
color: #333;
}
.security-status {
display: flex;
align-items: center;
}
.status-text {
font-size: 24rpx;
color: #999;
margin-right: 10rpx;
}
.status-text.bound {
color: #4caf50;
}
.security-arrow {
font-size: 24rpx;
color: #999;
}
</style>