新增全局 YjyfTabBar 组件,替换各页面自定义导航,实现一致的 tab 切换逻辑与图标样式 新增全局设计令牌与 SCSS 变量系统,统一色彩、间距、字阶、圆角与投影规范 新增 POST 请求防并发锁,避免重复提交 重构多个页面,添加骨架屏、空状态引导、点击反馈类,修复硬编码样式与假数据 删除废弃的后台入口文件与 nginx 配置,清理冗余代码
162 行
7.9 KiB
Vue
162 行
7.9 KiB
Vue
<template>
|
||
<view class="page">
|
||
<view class="header">
|
||
<view class="deco d1"></view>
|
||
<view class="deco d2"></view>
|
||
<view class="h-top">
|
||
<view class="m-info">
|
||
<text class="m-name">{{ info ? info.realname : '师傅' }}</text>
|
||
<text class="m-meta">{{ info ? ('Lv' + info.level + ' · 评分 ' + info.score + ' · 完成 ' + info.order_count + ' 单') : '加载中...' }}</text>
|
||
</view>
|
||
<view class="switch" :class="{ off: info && info.work_status === 'off' }" @tap="toggleWork">
|
||
<text class="switch-t">{{ info && info.work_status === 'off' ? '已暂停' : '接单中' }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="todo">
|
||
<view class="todo-item" @tap="go('/pages/master/orders')">
|
||
<text class="todo-num">{{ info ? info.todo_count : 0 }}</text>
|
||
<text class="todo-lab">待处理</text>
|
||
</view>
|
||
<view class="todo-item" @tap="go('/pages/master/orders')">
|
||
<text class="todo-num">{{ info ? info.verify_count : 0 }}</text>
|
||
<text class="todo-lab">待验收</text>
|
||
</view>
|
||
<view class="todo-item" @tap="go('/pages/master/wallet')">
|
||
<text class="todo-num">{{ info ? info.money : 0 }}</text>
|
||
<text class="todo-lab">可提现</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="grid">
|
||
<view class="grid-item clickable-hover" v-for="g in entries" :key="g.key" @tap="g.action">
|
||
<view class="g-icon" :style="{ background: g.bg }"><image class="g-svg" :src="g.svg" mode="aspectFit"></image></view>
|
||
<text class="g-txt">{{ g.name }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card" v-if="credit">
|
||
<view class="card-head">
|
||
<text class="card-title">信用体系</text>
|
||
<text class="card-more" @tap="go('/pages/master/credit')">明细 ›</text>
|
||
</view>
|
||
<view class="credit-row">
|
||
<view class="c-item"><text class="c-num">{{ credit.score }}</text><text class="c-lab">综合评分</text></view>
|
||
<view class="c-item"><text class="c-num">{{ credit.accept_rate }}%</text><text class="c-lab">接单率</text></view>
|
||
<view class="c-item"><text class="c-num">{{ credit.ontime_rate }}%</text><text class="c-lab">准时率</text></view>
|
||
<view class="c-item"><text class="c-num">{{ credit.good_rate }}%</text><text class="c-lab">好评率</text></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card">
|
||
<view class="card-head"><text class="card-title">我的服务类目</text></view>
|
||
<view class="skills">
|
||
<text class="skill" v-for="s in skills" :key="s.cat_id">{{ s.name }}</text>
|
||
<text class="skill empty" v-if="!skills.length">暂无技能标签</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="tips">接单后请在 2 小时内联系客户,超时订单将自动回流大厅</view>
|
||
|
||
<view class="back" @tap="backToUser">切换到业主身份</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import api from '@/utils/api.js';
|
||
import { isLogin, toLogin } from '@/utils/request.js';
|
||
import { setRole } from '@/utils/role.js';
|
||
|
||
const info = ref(null);
|
||
const credit = ref(null);
|
||
const skills = ref([]);
|
||
|
||
const entries = [
|
||
{ key: 'hall', name: '订单大厅', icon: '厅', bg: '#FFF1E0', color: '#E07A2E', action: () => go('/pages/master/hall') },
|
||
{ key: 'orders', name: '我的接单', icon: '单', bg: '#EAF2FF', color: '#4A7CFF', action: () => go('/pages/master/orders') },
|
||
{ key: 'quotes', name: '我的报价', icon: '价', bg: '#F0EBFF', color: '#7A5AF8', action: () => go('/pages/master/quote') },
|
||
{ key: 'wallet', name: '钱包提现', icon: '钱', bg: '#E8F7EE', color: '#2FA84F', action: () => go('/pages/master/wallet') },
|
||
{ key: 'credit', name: '信用中心', icon: '信', bg: '#FFF0F0', color: '#E8331B', action: () => go('/pages/master/credit') },
|
||
{ key: 'service', name: '联系客服', icon: '服', bg: '#EAF6FF', color: '#2A8FD8', action: callService }
|
||
];
|
||
|
||
function go(url) { uni.navigateTo({ url: url }); }
|
||
function callService() { uni.makePhoneCall({ phoneNumber: '4000000000' }); }
|
||
|
||
async function load() {
|
||
if (!isLogin()) { toLogin(); return; }
|
||
try {
|
||
const res = await api.masterInfo();
|
||
if (!res.applied || res.master.verify_status !== 'pass') {
|
||
uni.redirectTo({ url: '/pages/master/apply' });
|
||
return;
|
||
}
|
||
info.value = res.master;
|
||
skills.value = res.skills || [];
|
||
} catch (e) {}
|
||
try {
|
||
credit.value = await api.masterCredit();
|
||
} catch (e) {}
|
||
}
|
||
|
||
async function toggleWork() {
|
||
if (!info.value) return;
|
||
const next = info.value.work_status === 'off' ? 'on' : 'off';
|
||
try {
|
||
await api.masterToggle({ work_status: next });
|
||
info.value.work_status = next;
|
||
uni.showToast({ title: next === 'on' ? '已开启接单' : '已暂停接单', icon: 'none' });
|
||
} catch (e) {}
|
||
}
|
||
|
||
function backToUser() {
|
||
api.switchRole({ role: 'user' }).then((res) => {
|
||
setRole('user');
|
||
uni.reLaunch({ url: res.redirect || '/pages/index/index' });
|
||
}).catch(() => {});
|
||
}
|
||
|
||
onShow(() => load());
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page { min-height: 100vh; background: #F5F6F8; padding-bottom: 60rpx; }
|
||
.header { position: relative; padding: 34rpx 30rpx 26rpx; overflow: hidden; background: linear-gradient(160deg, #FFB85C 0%, #F98D25 55%, #ED6F0E 100%); }
|
||
.deco { position: absolute; border-radius: 50%; background: rgba(255,255,255,.13); }
|
||
.d1 { width: 320rpx; height: 320rpx; right: -120rpx; top: -130rpx; }
|
||
.d2 { width: 180rpx; height: 180rpx; left: -80rpx; bottom: -60rpx; }
|
||
.h-top { position: relative; display: flex; align-items: center; justify-content: space-between; }
|
||
.m-info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
|
||
.m-name { font-size: 36rpx; font-weight: 700; color: #fff; }
|
||
.m-meta { margin-top: 8rpx; font-size: 22rpx; color: rgba(255,255,255,.9); }
|
||
.switch { flex-shrink: 0; padding: 0 24rpx; line-height: 58rpx; border-radius: 29rpx; background: #fff; }
|
||
.switch.off { background: rgba(255,255,255,.35); }
|
||
.switch-t { font-size: 22rpx; font-weight: 600; color: #E36B0C; }
|
||
.switch.off .switch-t { color: #fff; }
|
||
.todo { position: relative; display: flex; margin-top: 28rpx; padding-top: 22rpx; border-top: 1rpx solid rgba(255,255,255,.25); }
|
||
.todo-item { flex: 1; display: flex; flex-direction: column; align-items: center; }
|
||
.todo-num { font-size: 36rpx; font-weight: 700; color: #fff; }
|
||
.todo-lab { margin-top: 4rpx; font-size: 20rpx; color: rgba(255,255,255,.88); }
|
||
.grid { display: flex; flex-wrap: wrap; margin: 22rpx 28rpx 0; padding: 16rpx 0; background: #fff; border-radius: 22rpx; box-shadow: 0 6rpx 20rpx rgba(240,129,31,.06); }
|
||
.grid-item { width: 33.33%; display: flex; flex-direction: column; align-items: center; padding: 18rpx 0; }
|
||
.g-icon { width: 84rpx; height: 84rpx; border-radius: 24rpx; display: flex; align-items: center; justify-content: center; }
|
||
.g-icon-t { font-size: 30rpx; font-weight: 700; }
|
||
.g-txt { margin-top: 12rpx; font-size: 22rpx; color: #333; }
|
||
.card { margin: 22rpx 28rpx 0; padding: 24rpx; background: #fff; border-radius: 22rpx; box-shadow: 0 6rpx 20rpx rgba(240,129,31,.06); }
|
||
.card-head { display: flex; align-items: center; justify-content: space-between; }
|
||
.card-title { font-size: 28rpx; font-weight: 700; color: #1F1F1F; }
|
||
.card-more { font-size: 22rpx; color: #9A9A9A; }
|
||
.credit-row { display: flex; margin-top: 20rpx; }
|
||
.c-item { flex: 1; display: flex; flex-direction: column; align-items: center; }
|
||
.c-num { font-size: 32rpx; font-weight: 700; color: #F0710D; }
|
||
.c-lab { margin-top: 6rpx; font-size: 20rpx; color: #9A9A9A; }
|
||
.skills { display: flex; flex-wrap: wrap; padding-top: 14rpx; }
|
||
.skill { padding: 0 20rpx; line-height: 54rpx; margin: 0 12rpx 12rpx 0; border-radius: 27rpx; background: #FFF3E6; color: #C4580A; font-size: 22rpx; }
|
||
.skill.empty { background: #F5F5F5; color: #9A9A9A; }
|
||
.tips { margin: 22rpx 28rpx 0; padding: 18rpx 22rpx; background: #FFF7F0; border-radius: 16rpx; font-size: 22rpx; color: #C4580A; line-height: 32rpx; }
|
||
.back { margin: 26rpx 28rpx 0; height: 92rpx; display: flex; align-items: center; justify-content: center; background: #fff; border-radius: 46rpx; font-size: 28rpx; color: #F0710D; font-weight: 600; }
|
||
</style>
|
||
|