骑手端app代码仓库创建

This commit is contained in:
admin
2026-01-06 21:22:12 +08:00
commit 34c63780a8
467 changed files with 65334 additions and 0 deletions

463
pages/index/user.vue Normal file
View File

@@ -0,0 +1,463 @@
<template>
<view class="page">
<!-- header -->
<view class="header-wrap" :style="headerStyle">
<view class="header-bg"></view>
<view class="header-inner">
<image class="avatar" :src="user.avatar || defautAvatar"></image>
<view class="user-meta">
<view class="user-name">{{ user.nickName || '姓名(账号)' }}</view>
<view class="user-status" @click="handleStatusToggle">
{{ user.isOnline ? '在线' : '离线' }}<uni-icons style="margin-left:10rpx;" type="right" size="13" color="#fff"></uni-icons>
</view>
</view>
</view>
</view>
<!-- stats card -->
<view class="stats-card">
<view class="stats-row">
<view class="stats-item">
<text class="stats-title">今日预计收入</text>
<text class="stats-value"> {{ formatMoney(todayIncome) }} </text>
<view class="stats-link" @click="openAccount">
我的账户 <uni-icons type="right" size="13"></uni-icons>
</view>
</view>
<view class="stats-item">
<text class="stats-title">今日完成单量</text>
<text class="stats-value"> {{ todayOrders }} </text>
<view class="stats-link" @click="openOrders">
订单统计 <uni-icons type="right" size="13"></uni-icons>
</view>
</view>
</view>
</view>
<!-- shortcuts -->
<view class="shortcuts">
<view class="shortcut" @click="openAttendance">
<image class="shortcut-icon" src="/static/img/order1.png" mode="aspectFit" />
<text class="shortcut-text">考勤排班</text>
</view>
<view class="shortcut" @click="openSalary">
<image class="shortcut-icon" src="/static/img/order2.png" mode="aspectFit" />
<text class="shortcut-text">薪资助手</text>
</view>
<view class="shortcut" @click="openSetting">
<!-- <image class="shortcut-icon" src="/static/img/edit.png" mode="aspectFit" /> -->
<view class="shortcut-icon">
<uni-icons type="gear" size="43"></uni-icons>
</view>
<text class="shortcut-text">设置</text>
</view>
</view>
</view>
<s-auth-modal />
<su-popup type="center" :show="showStatusPopup" round="14" :showClose="false">
<view class="modal-box">
<view class="modal-body">
<text class="modal-title">{{ modalTitle }}</text>
<text v-if="modalMsg" class="modal-msg">{{ modalMsg }}</text>
</view>
<view class="modal-footer">
<view class="modal-btn cancel" @click="cancelConfirm">取消</view>
<view class="modal-btn confirm" @click="confirmAction">确认</view>
</view>
</view>
</su-popup>
</template>
<script setup>
import {
computed,
ref
} from 'vue';
import {
onShow,
onPageScroll,
onPullDownRefresh
} from '@dcloudio/uni-app';
import sheep from '@/sheep';
// 现有 store / 模板数据
const template = computed(() => sheep.$store('app').template.user);
const isLogin = computed(() => sheep.$store('user').isLogin);
const user = ref({});
const todayIncome = ref(0);
const todayOrders = ref(0);
const showBind = ref(false);
// 动态 header 内联样式,用于兼容不同平台的状态栏高度
const headerStyle = ref({});
const defautAvatar =
'https://huichibao.oss-cn-guangzhou.aliyuncs.com/1/material/348b8223-8d03-46aa-8836-6757e8beebd2.png';
// 格式化金额显示
function formatMoney(val) {
if (val == null) return '0';
return (Number(val) || 0).toFixed(1);
}
// 微信小程序的“手机号快速验证”
const getPhoneNumber = async (e) => {
if (e.detail.errMsg !== 'getPhoneNumber:ok') {
sheep.$helper.toast('快捷登录失败');
return;
}
let result = await sheep.$platform.useProvider().mobileLogin(e.detail);
if (result) {
showBind.value = false;
}
};
// 页面显示时拉取用户信息并填充统计数据(从 store 获取或使用占位)
onShow(async () => {
const data = await sheep.$store('user').getInfo();
if (data) {
user.value = data;
// 兼容后端字段名,优先使用 data.todayIncome / data.income / placeholder
todayIncome.value = data.todayIncome ?? data.income ?? 137.9;
todayOrders.value = data.todayOrders ?? data.orders ?? 39;
if (data?.status == 1) {
console.log("清空缓存");
uni.clearStorageSync();
}
}
// 兼容处理:读取原生状态栏高度并设置 header 的 padding-toppx与 CSS 变量 --statusbarrpx
try {
const sys = uni.getSystemInfoSync();
const statusBarHeightPx = sys?.statusBarHeight || 0;
const windowWidth = sys?.windowWidth || 375;
// 将 px 转为 rpx rpx = px / windowWidth * 750
const statusBarHeightRpx = Math.round((statusBarHeightPx / windowWidth) * 750);
headerStyle.value = {
paddingTop: statusBarHeightPx + 'px',
'--statusbar': statusBarHeightRpx + 'rpx'
};
} catch (e) {
// ignore
}
});
// 确认弹框状态(上线/下线/禁止)
const showStatusPopup = ref(false);
const confirmType = ref(''); // 'online' | 'offline' | 'forbidden'
const modalTitle = ref('');
const modalMsg = ref('');
// 判断用户是否被禁止接单(兼容多种字段)
function isUserForbidden() {
const u = user.value || {};
return !!(u.forbidden || u.isForbidden || u.forbid || u.forbidReceive || u.disableReceive || u.receive === false);
}
// 点击状态:根据当前状态弹不同的确认框
function handleStatusToggle() {
if (user.value.isOnline) {
confirmType.value = 'offline';
modalTitle.value = '确认下线?';
modalMsg.value = '下线需平台进行核准\n此时正常接单请留意核准信息';
showStatusPopup.value = true;
return;
}
// 如果被禁止接单
if (isUserForbidden()) {
confirmType.value = 'forbidden';
modalTitle.value = '您处于禁止接单状态';
modalMsg.value = '暂无法上线上线';
showStatusPopup.value = true;
return;
}
// 普通从离线 -> 上线
confirmType.value = 'online';
modalTitle.value = '确认上线?';
modalMsg.value = '';
showStatusPopup.value = true;
}
function cancelConfirm() {
showStatusPopup.value = false;
}
async function confirmAction() {
const type = confirmType.value;
showStatusPopup.value = false;
if (type === 'online') {
// TODO: 调用后端接口变更上线状态
user.value.isOnline = true;
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已上线');
} else if (type === 'offline') {
user.value.isOnline = false;
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已下线');
} else if (type === 'forbidden') {
// 仅展示信息,无操作
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('无法上线(禁止接单)');
}
// 可在此处调用 store 或 API 同步服务端状态,例如:
// await sheep.$store('user').setOnline(user.value.isOnline);
}
onPullDownRefresh(() => {
sheep.$store('user').updateUserData();
setTimeout(function() {
uni.stopPullDownRefresh();
}, 800);
});
onPageScroll(() => {});
// 跳转/交互方法(保留路由调用位置,用户可按需实现)
function goBack() {
uni.navigateBack();
}
function openAccount() {
uni.navigateTo({
url: '/pages/public/webview?type=account'
});
}
function openOrders() {
uni.navigateTo({
url: '/pages/index/order-list'
});
}
function openAttendance() {
uni.navigateTo({
url: '/pages/public/faq'
});
}
function openSalary() {
uni.navigateTo({
url: '/pages/public/richtext'
});
}
function openSetting() {
uni.navigateTo({
url: '/pages/public/setting'
});
}
</script>
<style scoped>
.page {
background: #ffffff;
min-height: 100vh;
}
/* header */
.header-wrap {
position: relative;
/* 适配刘海屏安全区:优先使用 constant/env兼容小程序与 iOS/Android 安全区 */
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
}
.header-bg {
/* 背景放置为绝对定位以覆盖安全区(兼容 env/constant 回退与 JS 动态 --statusbar */
position: absolute;
top: 0;
left: 0;
right: 0;
/* 背景高度需要包含安全区高度,优先使用 env/constant最后一行为回退值 */
height: 250rpx;
height: calc(250rpx + constant(safe-area-inset-top));
height: calc(250rpx + env(safe-area-inset-top));
/* JS 动态变量回退(当 env/constant 不可用时,使用 --statusbar单位 rpx */
height: calc(250rpx + var(--statusbar, 0rpx));
background: #9ad6f0;
/* 浅蓝 */
border-bottom-left-radius: 24rpx;
border-bottom-right-radius: 24rpx;
z-index: 0;
}
.header-inner {
position: absolute;
left: 32rpx;
/* 使用 CSS 变量保证当我们通过 JS 设置 --statusbar 时,内容会相对下移(单位 rpx */
top: calc(var(--statusbar, 0rpx) + 40rpx);
z-index: 1;
flex-direction: row;
display: flex;
align-items: center;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 60rpx;
border: 4rpx solid rgba(255, 255, 255, 0.6);
}
.user-meta {
margin-left: 20rpx;
}
.user-name {
font-size: 30rpx;
color: #fff;
font-weight: 600;
}
.user-status {
margin-top: 8rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
}
.back {
position: absolute;
left: 18rpx;
top: calc(var(--statusbar, 0rpx) + 12rpx);
z-index: 2;
color: #fff;
font-size: 36rpx;
}
/* stats card */
.stats-card {
padding: 0 24rpx;
margin-top: 190rpx;
position: relative;
z-index: 3;
}
.stats-row {
background: #f6c98b;
/* 浅橙色 */
border-radius: 20rpx;
padding: 30rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.stats-item {
width: 48%;
}
.stats-title {
font-size: 24rpx;
color: #5b4a32;
}
.stats-value {
display: block;
margin-top: 12rpx;
font-size: 42rpx;
color: #222;
font-weight: 700;
}
.stats-link {
display: block;
margin-top: 12rpx;
font-size: 24rpx;
color: #7a5a3a;
}
/* shortcuts */
.shortcuts {
margin-top: 30rpx;
padding: 30rpx 40rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
position: relative;
z-index: 3;
}
.shortcut {
flex: 1;
align-items: center;
display: flex;
flex-direction: column;
}
.shortcut-icon {
width: 80rpx;
height: 80rpx;
margin-bottom: 12rpx;
}
.shortcut-text {
font-size: 24rpx;
color: #333;
}
/* popup 原有样式 */
.popup {
padding: 80rpx 0 50rpx;
}
.tip-text {
margin-bottom: 30rpx;
font-weight: 400;
font-size: 24rpx;
color: #999999;
line-height: 44rpx;
text-align: center;
}
.bind-btn {
width: 630rpx;
height: 96rpx;
font-weight: 400;
font-size: 28rpx;
color: #FFFFFF;
line-height: 96rpx;
text-align: center;
font-style: normal;
background: #00B85B;
border-radius: 64rpx 64rpx 64rpx 64rpx;
}
/* 确认弹框样式su-popup 内部内容) */
.modal-box {
width: 640rpx;
background: #fff;
border-radius: 14rpx;
overflow: hidden;
}
.modal-body {
padding: 40rpx 30rpx;
text-align: center;
}
.modal-title {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 10rpx;
}
.modal-msg {
display: block;
font-size: 24rpx;
color: #999;
line-height: 34rpx;
white-space: pre-line;
}
.modal-footer {
display: flex;
flex-direction: row;
border-top: 1rpx solid #eee;
}
.modal-btn {
flex: 1;
padding: 26rpx 0;
text-align: center;
font-size: 28rpx;
}
.modal-btn.cancel {
color: #666;
border-right: 1rpx solid #eee;
}
.modal-btn.confirm {
color: #1e9fff;
}
</style>