Files
medical-mall/pages/mall/merchant/profile.uvue

770 lines
17 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="merchant-profile">
<!-- #ifdef MP-WEIXIN -->
<view style="padding-top: var(--status-bar-height); background-color: #ffffff; display: flex; flex-direction: row; align-items: flex-end; border-bottom: 1rpx solid #eeeeee; box-sizing: border-box; height: calc(88rpx + var(--status-bar-height));">
<view style="display: flex; flex-direction: row; align-items: center; padding: 0 30rpx; height: 88rpx;" @click="uni.navigateBack()">
<text style="font-size: 44rpx; color: #333333; line-height: 1; margin-right: 6rpx;"></text>
<text style="font-size: 28rpx; color: #333333;">返回</text>
</view>
</view>
<!-- #endif -->
<!-- 店铺信息头部 -->
<view class="profile-header">
<image :src="shopInfo.shop_logo || '/static/default-shop.png'" class="shop-logo" @click="editShop" />
<view class="shop-info">
<text class="shop-name">{{ shopInfo.shop_name }}</text>
<text class="shop-status">{{ getShopStatus() }}</text>
<view class="shop-stats">
<text class="stat-item">评分: {{ shopInfo.rating }}/5.0</text>
<text class="stat-item">总销量: {{ shopInfo.total_sales }}</text>
</view>
</view>
<view class="settings-icon" @click="goToSettings">⚙️</view>
</view>
<!-- 订单管理快捷入口 -->
<view class="order-shortcuts">
<view class="section-title">订单管理</view>
<view class="order-tabs">
<view class="order-tab" @click="goToOrders('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" @click="goToOrders('pending')">
<text class="tab-icon">💰</text>
<text class="tab-text">待发货</text>
<text v-if="orderCounts.pending > 0" class="tab-badge alert">{{ orderCounts.pending }}</text>
</view>
<view class="order-tab" @click="goToOrders('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 class="order-tab" @click="goToOrders('refund')">
<text class="tab-icon">↩️</text>
<text class="tab-text">退款</text>
<text v-if="orderCounts.refund > 0" class="tab-badge alert">{{ orderCounts.refund }}</text>
</view>
</view>
</view>
<!-- 今日经营数据 -->
<view class="today-stats">
<view class="section-title">今日经营</view>
<view class="stats-grid">
<view class="stat-card">
<text class="stat-value">¥{{ todayStats.revenue }}</text>
<text class="stat-label">营业额</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ todayStats.orders }}</text>
<text class="stat-label">订单数</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ todayStats.visitors }}</text>
<text class="stat-label">访客数</text>
</view>
<view class="stat-card">
<text class="stat-value">{{ todayStats.conversion }}%</text>
<text class="stat-label">转化率</text>
</view>
</view>
</view>
<!-- 最近订单 -->
<view class="recent-orders">
<view class="section-header">
<text class="section-title">最近订单</text>
<text class="view-all" @click="goToOrders('all')">查看全部 ></text>
</view>
<view v-if="recentOrders.length > 0" class="order-list">
<view v-for="order in recentOrders" :key="order.id" class="order-item" @click="viewOrderDetail(order.id)">
<view class="order-header">
<text class="order-no">订单号: {{ order.order_no }}</text>
<text class="order-status" :class="'status-' + order.status">{{ getOrderStatusText(order.status) }}</text>
</view>
<view class="order-info">
<text class="order-amount">¥{{ order.actual_amount }}</text>
<text class="order-time">{{ formatTime(order.created_at) }}</text>
</view>
</view>
</view>
<view v-else class="no-data">
<text class="no-data-text">暂无最近订单</text>
</view>
</view>
<!-- 商品管理 -->
<view class="product-management">
<view class="section-title">商品管理</view>
<view class="management-grid">
<view class="management-item" @click="goToProducts">
<text class="item-icon">📦</text>
<text class="item-label">商品管理</text>
<text class="item-count">{{ productStats.total }}</text>
</view>
<view class="management-item" @click="goToInventory">
<text class="item-icon">📊</text>
<text class="item-label">库存管理</text>
<text class="item-count">{{ productStats.lowStock }}</text>
</view>
<view class="management-item" @click="goToPromotions">
<text class="item-icon">🎉</text>
<text class="item-label">促销活动</text>
<text class="item-count">{{ promotionStats.active }}</text>
</view>
<view class="management-item" @click="goToReviews">
<text class="item-icon">⭐</text>
<text class="item-label">评价管理</text>
<text class="item-count">{{ reviewStats.pending }}</text>
</view>
</view>
</view>
<!-- 经营分析 -->
<view class="business-analysis">
<view class="section-header">
<text class="section-title">经营分析</text>
<text class="view-more" @click="goToAnalytics">详细报表 ></text>
</view>
<view class="analysis-chart">
<view class="chart-item">
<text class="chart-label">本周销售趋势</text>
<view class="chart-bar">
<view v-for="(day, index) in weeklyData" :key="index"
class="bar-item"
:style="{ height: (day.amount / maxWeeklyAmount * 100) + '%' }">
</view>
</view>
</view>
</view>
</view>
<!-- 功能菜单 -->
<view class="function-menu">
<view class="menu-group">
<view class="menu-item" @click="goToFinance">
<text class="menu-icon">💳</text>
<text class="menu-label">财务管理</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToCustomers">
<text class="menu-icon">👥</text>
<text class="menu-label">客户管理</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToMarketing">
<text class="menu-icon">📢</text>
<text class="menu-label">营销工具</text>
<text class="menu-arrow">></text>
</view>
</view>
<view class="menu-group">
<view class="menu-item" @click="goToHelp">
<text class="menu-icon">❓</text>
<text class="menu-label">帮助中心</text>
<text class="menu-arrow">></text>
</view>
<view class="menu-item" @click="goToFeedback">
<text class="menu-icon">💬</text>
<text class="menu-label">意见反馈</text>
<text class="menu-arrow">></text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, onMounted, computed } from 'vue'
import type { MerchantType, OrderType, ApiResponseType } from '@/types/mall-types'
// 响应式数据
const shopInfo = ref({
id: '',
shop_name: '我的店铺',
shop_logo: '',
rating: 4.8,
total_sales: 1289,
shop_status: 1
} as MerchantType)
const orderCounts = ref({
total: 0,
pending: 0,
shipped: 0,
refund: 0
})
const todayStats = ref({
revenue: '2,368.50',
orders: 45,
visitors: 238,
conversion: 18.9
})
const productStats = ref({
total: 156,
lowStock: 8
})
const promotionStats = ref({
active: 3
})
const reviewStats = ref({
pending: 12
})
const recentOrders = ref([] as Array<OrderType>)
const weeklyData = ref([
{ day: '周一', amount: 1200 },
{ day: '周二', amount: 1800 },
{ day: '周三', amount: 2400 },
{ day: '周四', amount: 1600 },
{ day: '周五', amount: 2800 },
{ day: '周六', amount: 3200 },
{ day: '周日', amount: 2600 }
])
// 计算属性
const maxWeeklyAmount = computed(() => {
return Math.max(...weeklyData.value.map(item => item.amount))
})
// 生命周期
onMounted(() => {
loadShopInfo()
loadOrderCounts()
loadRecentOrders()
})
// 方法
function loadShopInfo() {
// 模拟加载店铺信息
shopInfo.value = {
id: 'shop001',
user_id: 'user001',
shop_name: '时尚服饰专营店',
shop_logo: '/static/shop-logo.png',
shop_banner: '',
shop_description: '专业的时尚服饰店铺',
contact_name: '张老板',
contact_phone: '13888888888',
shop_status: 1,
rating: 4.8,
total_sales: 1289,
created_at: '2024-01-01'
}
}
function loadOrderCounts() {
// 模拟加载订单统计
orderCounts.value = {
total: 156,
pending: 8,
shipped: 12,
refund: 3
}
}
function loadRecentOrders() {
// 模拟加载最近订单
recentOrders.value = [
{
id: 'order001',
order_no: 'ORD20241201001',
user_id: 'user001',
merchant_id: 'shop001',
status: 2,
total_amount: 299.0,
discount_amount: 0,
delivery_fee: 10.0,
actual_amount: 309.0,
payment_method: 1,
payment_status: 1,
delivery_address: {},
created_at: '2024-12-01 14:30:00'
},
{
id: 'order002',
order_no: 'ORD20241201002',
user_id: 'user002',
merchant_id: 'shop001',
status: 1,
total_amount: 599.0,
discount_amount: 50.0,
delivery_fee: 0,
actual_amount: 549.0,
payment_method: null,
payment_status: 0,
delivery_address: {},
created_at: '2024-12-01 15:20:00'
}
]
}
function getShopStatus(): string {
const statusMap = {
0: '待审核',
1: '正常营业',
2: '暂停营业',
3: '已关闭'
}
return statusMap[shopInfo.value.shop_status] || '未知状态'
}
function getOrderStatusText(status: number): string {
const statusMap = {
1: '待付款',
2: '待发货',
3: '已发货',
4: '已送达',
5: '已完成',
6: '已取消'
}
return statusMap[status] || '未知'
}
function formatTime(dateStr: string): string {
const date = new Date(dateStr)
const now = new Date()
const diff = now.getTime() - date.getTime()
const hours = Math.floor(diff / (1000 * 60 * 60))
if (hours < 1) {
return '刚刚'
} else if (hours < 24) {
return `${hours}小时前`
} else {
return `${Math.floor(hours / 24)}天前`
}
}
// 导航方法
function editShop() {
uni.navigateTo({
url: '/pages/mall/merchant/shop-edit'
})
}
function goToSettings() {
uni.navigateTo({
url: '/pages/mall/merchant/settings'
})
}
function goToOrders(type: string) {
uni.navigateTo({
url: `/pages/mall/merchant/orders?type=${type}`
})
}
function viewOrderDetail(orderId: string) {
uni.navigateTo({
url: `/pages/mall/merchant/order-detail?id=${orderId}`
})
}
function goToProducts() {
uni.navigateTo({
url: '/pages/mall/merchant/products'
})
}
function goToInventory() {
uni.navigateTo({
url: '/pages/mall/merchant/inventory'
})
}
function goToPromotions() {
uni.navigateTo({
url: '/pages/mall/merchant/promotions'
})
}
function goToReviews() {
uni.navigateTo({
url: '/pages/mall/merchant/reviews'
})
}
function goToAnalytics() {
uni.navigateTo({
url: '/pages/mall/merchant/analytics'
})
}
function goToFinance() {
uni.navigateTo({
url: '/pages/mall/merchant/finance'
})
}
function goToCustomers() {
uni.navigateTo({
url: '/pages/mall/merchant/customers'
})
}
function goToMarketing() {
uni.navigateTo({
url: '/pages/mall/merchant/marketing'
})
}
function goToHelp() {
uni.navigateTo({
url: '/pages/mall/common/help'
})
}
function goToFeedback() {
uni.navigateTo({
url: '/pages/mall/common/feedback'
})
}
</script>
<style scoped>
.merchant-profile {
padding: 0 0 120rpx 0;
background-color: #f5f5f5;
min-height: 100vh;
}
.profile-header {
display: flex;
align-items: center;
padding: 40rpx 30rpx;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
position: relative;
}
.shop-logo {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
margin-right: 30rpx;
border: 4rpx solid rgba(255, 255, 255, 0.3);
}
.shop-info {
flex: 1;
}
.shop-name {
font-size: 36rpx;
font-weight: bold;
color: white;
margin-bottom: 10rpx;
}
.shop-status {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 15rpx;
}
.shop-stats {
display: flex;
gap: 30rpx;
}
.stat-item {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.9);
}
.settings-icon {
font-size: 36rpx;
color: white;
padding: 10rpx;
}
.order-shortcuts, .today-stats, .recent-orders, .product-management, .business-analysis, .function-menu {
margin: 20rpx 30rpx;
background: white;
border-radius: 20rpx;
padding: 30rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 30rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.view-all, .view-more {
font-size: 24rpx;
color: #667eea;
}
.order-tabs {
display: flex;
justify-content: space-between;
}
.order-tab {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
position: relative;
}
.tab-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.tab-text {
font-size: 24rpx;
color: #666;
}
.tab-badge {
position: absolute;
top: -10rpx;
right: 20rpx;
background: #ff6b6b;
color: white;
font-size: 20rpx;
padding: 4rpx 8rpx;
border-radius: 10rpx;
min-width: 30rpx;
text-align: center;
}
.tab-badge.alert {
background: #ff4757;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.stats-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.stat-card {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 150rpx;
padding: 20rpx;
background: #f8f9ff;
border-radius: 15rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: bold;
color: #667eea;
margin-bottom: 5rpx;
}
.stat-label {
font-size: 24rpx;
color: #666;
}
.order-list {
display: flex;
flex-direction: column;
gap: 20rpx;
}
.order-item {
padding: 25rpx;
background: #f8f9ff;
border-radius: 15rpx;
border-left: 6rpx solid #667eea;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.order-no {
font-size: 26rpx;
color: #333;
font-weight: 500;
}
.order-status {
font-size: 22rpx;
padding: 6rpx 12rpx;
border-radius: 20rpx;
background: #e3f2fd;
color: #1976d2;
}
.status-1 {
background: #fff3e0;
color: #f57c00;
}
.status-2 {
background: #e8f5e8;
color: #388e3c;
}
.status-3 {
background: #e3f2fd;
color: #1976d2;
}
.order-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.order-amount {
font-size: 28rpx;
font-weight: bold;
color: #ff6b6b;
}
.order-time {
font-size: 22rpx;
color: #999;
}
.management-grid {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 20rpx;
}
.management-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
min-width: 140rpx;
padding: 25rpx 15rpx;
background: #f8f9ff;
border-radius: 15rpx;
position: relative;
}
.item-icon {
font-size: 48rpx;
margin-bottom: 10rpx;
}
.item-label {
font-size: 24rpx;
color: #333;
margin-bottom: 5rpx;
}
.item-count {
font-size: 20rpx;
color: #667eea;
font-weight: bold;
}
.analysis-chart {
padding: 20rpx 0;
}
.chart-item {
text-align: center;
}
.chart-label {
font-size: 24rpx;
color: #666;
margin-bottom: 20rpx;
}
.chart-bar {
display: flex;
justify-content: space-between;
align-items: end;
height: 200rpx;
gap: 10rpx;
}
.bar-item {
flex: 1;
background: linear-gradient(180deg, #667eea 0%, #764ba2 100%);
border-radius: 8rpx 8rpx 0 0;
min-height: 20rpx;
}
.menu-group {
margin-bottom: 30rpx;
}
.menu-group:last-child {
margin-bottom: 0;
}
.menu-item {
display: flex;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-icon {
font-size: 36rpx;
width: 60rpx;
margin-right: 25rpx;
}
.menu-label {
flex: 1;
font-size: 28rpx;
color: #333;
}
.menu-arrow {
font-size: 24rpx;
color: #ccc;
}
.no-data {
text-align: center;
padding: 60rpx 0;
}
.no-data-text {
font-size: 24rpx;
color: #999;
}
</style>