368 lines
6.9 KiB
Vue
368 lines
6.9 KiB
Vue
<template>
|
|
<view class="container">
|
|
<!-- 技师状态卡片 -->
|
|
<view class="status-card">
|
|
<view class="status-info">
|
|
<view class="avatar">
|
|
<image :src="technicianInfo.avatar || '/static/default-avatar.png'" mode="aspectFill"></image>
|
|
<view class="status-dot" :class="{ online: isOnline }"></view>
|
|
</view>
|
|
<view class="info">
|
|
<text class="name">{{ technicianInfo.name || '未认证技师' }}</text>
|
|
<text class="level">{{ technicianInfo.level || '初级技师' }}</text>
|
|
</view>
|
|
</view>
|
|
<button class="status-btn" :class="{ online: isOnline }" @click="toggleStatus">
|
|
{{ isOnline ? '上线中' : '已下线' }}
|
|
</button>
|
|
</view>
|
|
|
|
<!-- 今日数据 -->
|
|
<view class="data-grid">
|
|
<view class="data-item">
|
|
<text class="number">{{ todayData.orders }}</text>
|
|
<text class="label">今日订单</text>
|
|
</view>
|
|
<view class="data-item">
|
|
<text class="number">¥{{ todayData.earnings }}</text>
|
|
<text class="label">今日收入</text>
|
|
</view>
|
|
<view class="data-item">
|
|
<text class="number">{{ todayData.rating }}</text>
|
|
<text class="label">服务评分</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 待处理订单 -->
|
|
<view class="section">
|
|
<view class="section-title">
|
|
<text>待处理订单</text>
|
|
<text class="more" @click="goToOrders">查看全部</text>
|
|
</view>
|
|
<view class="order-list">
|
|
<view class="order-item" v-for="(order, index) in pendingOrders" :key="index" @click="handleOrder(order)">
|
|
<view class="order-info">
|
|
<text class="service">{{ order.serviceName }}</text>
|
|
<text class="address">{{ order.address }}</text>
|
|
<text class="time">{{ order.appointmentTime }}</text>
|
|
</view>
|
|
<view class="order-action">
|
|
<text class="price">¥{{ order.price }}</text>
|
|
<button class="accept-btn" @click.stop="acceptOrder(order)">接单</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 快捷功能 -->
|
|
<view class="quick-actions">
|
|
<view class="action-item" @click="goToCertification">
|
|
<image src="/static/icon-cert.png" mode="aspectFit"></image>
|
|
<text>技师认证</text>
|
|
</view>
|
|
<view class="action-item" @click="goToEarnings">
|
|
<image src="/static/icon-money.png" mode="aspectFit"></image>
|
|
<text>收入明细</text>
|
|
</view>
|
|
<view class="action-item" @click="goToService">
|
|
<image src="/static/icon-service.png" mode="aspectFit"></image>
|
|
<text>服务记录</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
isOnline: false,
|
|
technicianInfo: {},
|
|
todayData: {
|
|
orders: 0,
|
|
earnings: 0,
|
|
rating: 5.0
|
|
},
|
|
pendingOrders: []
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.loadData()
|
|
},
|
|
onShow() {
|
|
this.loadPendingOrders()
|
|
},
|
|
methods: {
|
|
loadData() {
|
|
// 加载技师信息
|
|
this.technicianInfo = uni.getStorageSync('technicianInfo') || {}
|
|
// 加载今日数据
|
|
this.loadTodayData()
|
|
},
|
|
|
|
loadTodayData() {
|
|
this.$api.get('/api/technician/today-data').then(res => {
|
|
this.todayData = res.data
|
|
})
|
|
},
|
|
|
|
loadPendingOrders() {
|
|
this.$api.get('/api/technician/pending-orders').then(res => {
|
|
this.pendingOrders = res.data
|
|
})
|
|
},
|
|
|
|
toggleStatus() {
|
|
this.isOnline = !this.isOnline
|
|
this.$api.post('/api/technician/status', { online: this.isOnline }).then(res => {
|
|
uni.showToast({
|
|
title: this.isOnline ? '已上线' : '已下线',
|
|
icon: 'success'
|
|
})
|
|
})
|
|
},
|
|
|
|
acceptOrder(order) {
|
|
uni.showModal({
|
|
title: '确认接单',
|
|
content: `确定接受这个订单吗?`,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.$api.post('/api/technician/accept-order', { orderId: order.id }).then(res => {
|
|
uni.showToast({
|
|
title: '接单成功',
|
|
icon: 'success'
|
|
})
|
|
this.loadPendingOrders()
|
|
})
|
|
}
|
|
}
|
|
})
|
|
},
|
|
|
|
handleOrder(order) {
|
|
uni.navigateTo({
|
|
url: `/pages/order-detail/order-detail?id=${order.id}`
|
|
})
|
|
},
|
|
|
|
goToOrders() {
|
|
uni.switchTab({
|
|
url: '/pages/orders/orders'
|
|
})
|
|
},
|
|
|
|
goToCertification() {
|
|
uni.navigateTo({
|
|
url: '/pages/certification/certification'
|
|
})
|
|
},
|
|
|
|
goToEarnings() {
|
|
uni.switchTab({
|
|
url: '/pages/earnings/earnings'
|
|
})
|
|
},
|
|
|
|
goToService() {
|
|
uni.navigateTo({
|
|
url: '/pages/service-record/service-record'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.container {
|
|
padding: 0;
|
|
}
|
|
|
|
.status-card {
|
|
background: linear-gradient(135deg, #ff6600 0%, #ff8533 100%);
|
|
padding: 20px;
|
|
color: white;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.status-info {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.avatar {
|
|
position: relative;
|
|
margin-right: 15px;
|
|
}
|
|
|
|
.avatar image {
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: 30px;
|
|
border: 2px solid white;
|
|
}
|
|
|
|
.status-dot {
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 0;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 8px;
|
|
background: #ccc;
|
|
border: 2px solid white;
|
|
}
|
|
|
|
.status-dot.online {
|
|
background: #4cd964;
|
|
}
|
|
|
|
.info .name {
|
|
display: block;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.info .level {
|
|
display: block;
|
|
font-size: 14px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.status-btn {
|
|
padding: 8px 20px;
|
|
border-radius: 20px;
|
|
background: rgba(255, 255, 255, 0.2);
|
|
color: white;
|
|
border: 1px solid white;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.status-btn.online {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
color: #ff6600;
|
|
}
|
|
|
|
.data-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
background: white;
|
|
margin: 15px;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.data-item {
|
|
padding: 20px;
|
|
text-align: center;
|
|
border-right: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.data-item:last-child {
|
|
border-right: none;
|
|
}
|
|
|
|
.data-item .number {
|
|
display: block;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.data-item .label {
|
|
display: block;
|
|
font-size: 12px;
|
|
color: #999;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.section {
|
|
margin: 15px;
|
|
}
|
|
|
|
.section-title {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.more {
|
|
font-size: 14px;
|
|
color: #ff6600;
|
|
}
|
|
|
|
.order-item {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin-bottom: 10px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.order-info .service {
|
|
display: block;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.order-info .address,
|
|
.order-info .time {
|
|
display: block;
|
|
font-size: 12px;
|
|
color: #999;
|
|
margin-bottom: 3px;
|
|
}
|
|
|
|
.order-action {
|
|
text-align: right;
|
|
}
|
|
|
|
.price {
|
|
display: block;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
color: #ff6600;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.accept-btn {
|
|
padding: 6px 15px;
|
|
background: #ff6600;
|
|
color: white;
|
|
border-radius: 15px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.quick-actions {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 15px;
|
|
margin: 15px;
|
|
}
|
|
|
|
.action-item {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.action-item image {
|
|
width: 30px;
|
|
height: 30px;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.action-item text {
|
|
display: block;
|
|
font-size: 14px;
|
|
}
|
|
</style> |