Files
delivery-uniapp/pages/index/user.vue

474 lines
12 KiB
Vue
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="page">
<!-- header -->
<view class="header-wrap" :style="headerStyle">
<view class="header-bg"></view>
<view class="header-inner">
<image class="avatar" :src="userInfo.avatar || defautAvatar"></image>
<view class="user-meta" v-if="userInfo.nickname">
<view class="user-name">{{ userInfo.nickname + `(${userInfo.mobile})` }}</view>
<view class="user-status" @click="handleStatusToggle">
{{ userInfo.isOnline ? '在线' : '离线' }}<uni-icons style="margin-left:10rpx;" type="right" size="13" color="#fff"></uni-icons>
</view>
</view>
<view class="user-meta" v-else>
<view class="user-name" @tap="login">请登录</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,
watch
} from 'vue';
import {
onShow,
onPageScroll,
onPullDownRefresh
} from '@dcloudio/uni-app';
import sheep from '@/sheep';
import {
showAuthModal,
} from '@/sheep/hooks/useModal';
// 现有 store / 模板数据
const template = computed(() => sheep.$store('app').template.user);
const isLogin = computed(() => sheep.$store('user').isLogin);
const userInfo = computed(() => sheep.$store('user').userInfo);
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 = userInfo.value;
if (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 = userInfo.value || {};
return !!(u.forbidden || u.isForbidden || u.forbid || u.forbidReceive || u.disableReceive || u.receive === false);
}
// 点击状态:根据当前状态弹不同的确认框
function handleStatusToggle() {
if (userInfo.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: 调用后端接口变更上线状态
userInfo.value.isOnline = true;
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已上线');
} else if (type === 'offline') {
userInfo.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'
});
}
function login() {
showAuthModal();
}
</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>