feat: 引入uView Plus组件库,新增部分静态页面
This commit is contained in:
@@ -28,39 +28,41 @@
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="order-list" scroll-y="true" :style="{ height: listHeight + 'px' }">
|
||||
<view v-for="order in filteredOrders" :key="order.id" class="order-card">
|
||||
<!-- 头部编号 -->
|
||||
<view class="order-header">
|
||||
<view class="order-badge">{{ order.type === 'pickup' ? '取' : '送' }}</view>
|
||||
<view class="order-title">
|
||||
<text class="shop-name">{{ order.shopName }}</text>
|
||||
<text class="order-id">#{{ order.id }}</text>
|
||||
</view>
|
||||
<view class="order-status">{{ order.statusText }}</view>
|
||||
</view>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<view class="order-info">
|
||||
<view class="address-row">
|
||||
<view class="icon pickup">取</view>
|
||||
<view class="address-content">
|
||||
<text class="address-title">{{ order.pickupAddress }}</text>
|
||||
<text class="address-sub">商家已出餐 · {{ order.pickupNote || '' }}</text>
|
||||
<view @click="toDetail(order.id)">
|
||||
<!-- 头部编号 -->
|
||||
<view class="order-header">
|
||||
<view class="order-badge">{{ order.type === 'pickup' ? '取' : '送' }}</view>
|
||||
<view class="order-title">
|
||||
<text class="shop-name">{{ order.shopName }}</text>
|
||||
<text class="order-id">#{{ order.id }}</text>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order.pickupLat, order.pickupLng, order.pickupAddress)">导航</view>
|
||||
<view class="order-status">{{ order.statusText }}</view>
|
||||
</view>
|
||||
<view class="address-row">
|
||||
<view class="icon deliver">送</view>
|
||||
<view class="address-content">
|
||||
<text class="address-title">{{ order.deliveryAddress }}</text>
|
||||
<text class="address-sub">收货人:{{ order.receiverName }} {{ order.receiverPhone ? ('尾号' + (order.receiverPhone + '').slice(-4)) : ''}}</text>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<view class="order-info">
|
||||
<view class="address-row">
|
||||
<view class="icon pickup">取</view>
|
||||
<view class="address-content">
|
||||
<text class="address-title">{{ order.pickupAddress }}</text>
|
||||
<text class="address-sub">商家已出餐 · {{ order.pickupNote || '' }}</text>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order.pickupLat, order.pickupLng, order.pickupAddress)">导航</view>
|
||||
</view>
|
||||
<view class="address-row">
|
||||
<view class="icon deliver">送</view>
|
||||
<view class="address-content">
|
||||
<text class="address-title">{{ order.deliveryAddress }}</text>
|
||||
<text class="address-sub">收货人:{{ order.receiverName }} {{ order.receiverPhone ? ('尾号' + (order.receiverPhone + '').slice(-4)) : ''}}</text>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order.deliveryLat, order.deliveryLng, order.deliveryAddress)">导航</view>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order.deliveryLat, order.deliveryLng, order.deliveryAddress)">导航</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="order-note" v-if="order.note">
|
||||
<text>顾客:{{ order.note }}</text>
|
||||
|
||||
<!-- 备注 -->
|
||||
<view class="order-note" v-if="order.note">
|
||||
<text>顾客:{{ order.note }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作区 -->
|
||||
@@ -86,6 +88,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import sheep from '@/sheep';
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
|
||||
// 驿站/骑手信息(从 store 获取或 mock)
|
||||
const driverInfo = ref({
|
||||
@@ -214,20 +217,6 @@ function openManualInput() {
|
||||
});
|
||||
}
|
||||
|
||||
// 入口:计算列表高度适配底部栏
|
||||
onMounted(() => {
|
||||
try {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
const windowHeight = sys.windowHeight || 667;
|
||||
// 留出顶部和底部空间
|
||||
listHeight.value = windowHeight - 200;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
// 顶部安全区处理:参考 pages/index/user.vue 的实现
|
||||
import { onShow } from '@dcloudio/uni-app';
|
||||
|
||||
const headerStyle = ref({});
|
||||
|
||||
function setHeaderSafeArea() {
|
||||
@@ -246,17 +235,15 @@ function setHeaderSafeArea() {
|
||||
}
|
||||
}
|
||||
|
||||
//跳转订单详情
|
||||
function toDetail(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/detail?orderId=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setHeaderSafeArea();
|
||||
// 计算列表高度适配底部栏
|
||||
try {
|
||||
const sys = uni.getSystemInfoSync();
|
||||
const windowHeight = sys.windowHeight || 667;
|
||||
// 留出顶部和底部空间
|
||||
listHeight.value = windowHeight - 200;
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
// setHeaderSafeArea();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
<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>
|
||||
<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">
|
||||
{{ user.isOnline ? '在线' : '离线' }}<uni-icons style="margin-left:10rpx;" type="right" size="13" color="#fff"></uni-icons>
|
||||
{{ 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>
|
||||
|
||||
@@ -73,7 +76,8 @@
|
||||
<script setup>
|
||||
import {
|
||||
computed,
|
||||
ref
|
||||
ref,
|
||||
watch
|
||||
} from 'vue';
|
||||
import {
|
||||
onShow,
|
||||
@@ -81,12 +85,15 @@
|
||||
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 user = ref({});
|
||||
const userInfo = computed(() => sheep.$store('user').userInfo);
|
||||
|
||||
const todayIncome = ref(0);
|
||||
const todayOrders = ref(0);
|
||||
const showBind = ref(false);
|
||||
@@ -115,9 +122,8 @@
|
||||
|
||||
// 页面显示时拉取用户信息并填充统计数据(从 store 获取或使用占位)
|
||||
onShow(async () => {
|
||||
const data = await sheep.$store('user').getInfo();
|
||||
const data = userInfo.value;
|
||||
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;
|
||||
@@ -149,13 +155,13 @@
|
||||
|
||||
// 判断用户是否被禁止接单(兼容多种字段)
|
||||
function isUserForbidden() {
|
||||
const u = user.value || {};
|
||||
const u = userInfo.value || {};
|
||||
return !!(u.forbidden || u.isForbidden || u.forbid || u.forbidReceive || u.disableReceive || u.receive === false);
|
||||
}
|
||||
|
||||
// 点击状态:根据当前状态弹不同的确认框
|
||||
function handleStatusToggle() {
|
||||
if (user.value.isOnline) {
|
||||
if (userInfo.value.isOnline) {
|
||||
confirmType.value = 'offline';
|
||||
modalTitle.value = '确认下线?';
|
||||
modalMsg.value = '下线需平台进行核准\n此时正常接单请留意核准信息';
|
||||
@@ -186,10 +192,10 @@
|
||||
showStatusPopup.value = false;
|
||||
if (type === 'online') {
|
||||
// TODO: 调用后端接口变更上线状态
|
||||
user.value.isOnline = true;
|
||||
userInfo.value.isOnline = true;
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已上线');
|
||||
} else if (type === 'offline') {
|
||||
user.value.isOnline = false;
|
||||
userInfo.value.isOnline = false;
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已下线');
|
||||
} else if (type === 'forbidden') {
|
||||
// 仅展示信息,无操作
|
||||
@@ -242,6 +248,11 @@
|
||||
url: '/pages/public/setting'
|
||||
});
|
||||
}
|
||||
|
||||
function login() {
|
||||
showAuthModal();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
619
pages/order/detail.vue
Normal file
619
pages/order/detail.vue
Normal file
@@ -0,0 +1,619 @@
|
||||
<template>
|
||||
<s-layout title="订单详情" class="set-userinfo-wrap">
|
||||
<view class="order-detail-page">
|
||||
<!-- 地图占位(可替换为原生 map 组件或第三方地图组件) -->
|
||||
<view class="map-area">
|
||||
<!-- 真实项目建议使用 <map> 并渲染 polyline/markers -->
|
||||
<image class="map-image" src="/static/img/map-placeholder.png" mode="widthFix" v-if="!mapAvailable" />
|
||||
<map v-else class="map-native" :latitude="order?.pickupLat" :longitude="order?.pickupLng" show-location enable-3D
|
||||
enable-zoom :scale="16"></map>
|
||||
<view class="map-overlay">
|
||||
<view class="eta">距离商家{{ distanceText }},预计{{ etaText }}到达</view>
|
||||
<view class="nav-btn" @click="navigateToShop">导航到商家</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 地址块 -->
|
||||
<scroll-view class="content" scroll-y="true">
|
||||
<view class="address-block">
|
||||
<view class="addr-row">
|
||||
<view class="badge pickup">取</view>
|
||||
<view class="addr-body">
|
||||
<text class="addr-title">{{ order?.shopName || '店铺名称' }}</text>
|
||||
<text class="addr-sub">{{ order?.pickupAddress }}</text>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order?.pickupLat, order?.pickupLng, order?.pickupAddress)">导航</view>
|
||||
</view>
|
||||
|
||||
<view class="addr-row">
|
||||
<view class="badge deliver">送</view>
|
||||
<view class="addr-body">
|
||||
<text class="addr-title">{{ order?.deliveryAddress }}</text>
|
||||
<text
|
||||
class="addr-sub">收货人:{{ order?.receiverName }} {{ order?.receiverPhone ? ('尾号' + (order.receiverPhone + '').slice(-4)) : ''}}</text>
|
||||
</view>
|
||||
<view class="nav-icon" @click="openMap(order?.deliveryLat, order?.deliveryLng, order?.deliveryAddress)">导航
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顾客备注 -->
|
||||
<view class="note" v-if="order?.note">
|
||||
<text>顾客:{{ order.note }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 商品清单 -->
|
||||
<view class="goods-list">
|
||||
<view class="goods-header">
|
||||
<text>商品清单</text>
|
||||
<text class="item-count">{{ totalCount }}件</text>
|
||||
<text class="total-price">¥{{ totalPrice.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="goods-item" v-for="(g, idx) in order?.items || []" :key="idx">
|
||||
<view class="g-left">
|
||||
<text class="g-name">{{ g.name }}{{ g.spec ? ('·' + g.spec) : '' }}</text>
|
||||
<text class="g-qty">×{{ g.quantity }}</text>
|
||||
</view>
|
||||
<view class="g-right">¥{{ (g.price || 0).toFixed(2) }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 联系/记录等 -->
|
||||
<view class="action-record">
|
||||
<!-- <view class="call" @click="callPhone(order?.receiverPhone)">
|
||||
<text>电话联系</text>
|
||||
</view> -->
|
||||
<view class="record" @click="toRecord">交接记录</view>
|
||||
</view>
|
||||
|
||||
<!-- 占位底部高度,避免内容被底部按钮遮挡 -->
|
||||
<view style="height:140rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作 -->
|
||||
<view class="fixed-actions">
|
||||
<view class="left-actions">
|
||||
<view class="icon-phone" @click="callPhone(order?.receiverPhone)"></view>
|
||||
</view>
|
||||
<view class="right-actions">
|
||||
<view class="btn remind">催单</view>
|
||||
<view class="btn remind" @click="openRemindPopup">电话联系</view>
|
||||
<!-- <view class="btn confirm">转单</view> -->
|
||||
<view class="btn confirm" @click="confirmArrive">确认到店</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 催单弹框(uView Plus up-popup) -->
|
||||
<up-popup v-model:show="showRemind" mode="bottom" :closeable="false" border-radius="12">
|
||||
<view class="remind-popup">
|
||||
<view class="remind-row" @click="callShopPhone">
|
||||
<text class="remind-title">联系商家</text>
|
||||
<view class="remind-btn" @click.stop="callShopPhone">拨打电话</view>
|
||||
</view>
|
||||
<view class="remind-row" @click="callCustomerPhone">
|
||||
<text class="remind-title">联系顾客</text>
|
||||
<view class="remind-btn" @click.stop="callCustomerPhone">拨打电话</view>
|
||||
</view>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="remind-cancel" @click="showRemind = false">取消</view>
|
||||
</template>
|
||||
</up-popup>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const orderId = ref(null);
|
||||
const order = ref(null);
|
||||
const loading = ref(false);
|
||||
const mapAvailable = ref(false); // 如果需要使用 map 组件,置为 true
|
||||
|
||||
// 入口:从页面参数取 orderId,然后加载数据
|
||||
onLoad((options = {}) => {
|
||||
orderId.value = options.id || options.orderId || null;
|
||||
fetchOrder();
|
||||
});
|
||||
|
||||
async function fetchOrder() {
|
||||
loading.value = true;
|
||||
try {
|
||||
// 优先尝试平台统一 request(项目内可能封装在 sheep.request 或 sheep.api)
|
||||
if (sheep && typeof sheep.request === 'function') {
|
||||
const res = await sheep.request({
|
||||
url: '/order/detail',
|
||||
method: 'GET',
|
||||
data: {
|
||||
id: orderId.value
|
||||
}
|
||||
});
|
||||
// 根据封装不同,这里兼容 res.data 或 res
|
||||
order.value = (res && res.data) ? res.data : res;
|
||||
} else if (sheep && sheep.$api && sheep.$api.trade && typeof sheep.$api.trade.detail === 'function') {
|
||||
const res = await sheep.$api.trade.detail({
|
||||
id: orderId.value
|
||||
});
|
||||
order.value = res?.data || res;
|
||||
} else {
|
||||
// 回退 mock 数据,避免界面空白,开发时替换为真实接口
|
||||
order.value = {
|
||||
id: orderId.value || 1001,
|
||||
shopName: '店铺名(示例)',
|
||||
pickupAddress: '广东省广州市天河区学院站荷光路118-121号',
|
||||
pickupLat: 23.1005,
|
||||
pickupLng: 113.3301,
|
||||
deliveryAddress: '广东省广州市天河区华景新城软件园区B栋西梯501',
|
||||
deliveryLat: 23.105,
|
||||
deliveryLng: 113.335,
|
||||
receiverName: '张先生',
|
||||
receiverPhone: '13900001234',
|
||||
note: '依据餐量提供餐具',
|
||||
items: [{
|
||||
name: '商品名称A',
|
||||
spec: '规格1',
|
||||
quantity: 2,
|
||||
price: 23.89
|
||||
},
|
||||
{
|
||||
name: '商品名称B',
|
||||
spec: '规格2',
|
||||
quantity: 1,
|
||||
price: 45.00
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchOrder error', e);
|
||||
// 友好提示
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('获取订单失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const totalCount = computed(() => {
|
||||
if (!order.value || !order.value.items) return 0;
|
||||
return order.value.items.reduce((s, it) => s + (it.quantity || 0), 0);
|
||||
});
|
||||
const totalPrice = computed(() => {
|
||||
if (!order.value || !order.value.items) return 0;
|
||||
return order.value.items.reduce((s, it) => s + ((it.price || 0) * (it.quantity || 0)), 0);
|
||||
});
|
||||
|
||||
// 显示距离与预计时间(示例,真实项目可使用服务端或高德/百度 SDK 计算)
|
||||
const distanceText = computed(() => {
|
||||
// 此处为示例固定值,后续接入定位/路程计算替换
|
||||
return order.value ? '873m' : '--';
|
||||
});
|
||||
const etaText = computed(() => {
|
||||
return order.value ? '预计十分钟到达' : '--';
|
||||
});
|
||||
|
||||
function goBack() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
|
||||
function openMap(lat, lng, name) {
|
||||
if (!lat || !lng) {
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('无法获取坐标');
|
||||
return;
|
||||
}
|
||||
uni.openLocation({
|
||||
latitude: Number(lat),
|
||||
longitude: Number(lng),
|
||||
name: name || '',
|
||||
scale: 18
|
||||
});
|
||||
}
|
||||
|
||||
function navigateToShop() {
|
||||
openMap(order.value?.pickupLat, order.value?.pickupLng, order.value?.pickupAddress);
|
||||
}
|
||||
|
||||
function callPhone(phone) {
|
||||
if (!phone) {
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('未找到联系电话');
|
||||
return;
|
||||
}
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: phone
|
||||
});
|
||||
}
|
||||
|
||||
// 催单弹框控制(使用 uView Plus 的 up-popup)
|
||||
const showRemind = ref(false);
|
||||
|
||||
function openRemindPopup() {
|
||||
showRemind.value = true;
|
||||
}
|
||||
|
||||
function closeRemindPopup() {
|
||||
showRemind.value = false;
|
||||
}
|
||||
|
||||
function callShopPhone() {
|
||||
// 商家电话优先使用 order.shopPhone,否则尝试 fallback
|
||||
const phone = order.value?.shopPhone || order.value?.shopPhoneNumber || order.value?.receiverPhone || '';
|
||||
if (!phone) {
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('未找到商家电话');
|
||||
return;
|
||||
}
|
||||
callPhone(phone);
|
||||
closeRemindPopup();
|
||||
}
|
||||
|
||||
function callCustomerPhone() {
|
||||
const phone = order.value?.receiverPhone || '';
|
||||
if (!phone) {
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('未找到顾客电话');
|
||||
return;
|
||||
}
|
||||
callPhone(phone);
|
||||
closeRemindPopup();
|
||||
}
|
||||
|
||||
function confirmArrive() {
|
||||
// 确认到店:调用接口或本地改变状态
|
||||
if (!order.value) return;
|
||||
// 示例:调用后端接口(兼容性判断)
|
||||
(async () => {
|
||||
try {
|
||||
if (sheep && typeof sheep.request === 'function') {
|
||||
await sheep.request({
|
||||
url: '/order/confirmArrive',
|
||||
method: 'POST',
|
||||
data: {
|
||||
id: order.value.id
|
||||
}
|
||||
});
|
||||
}
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('已确认到店');
|
||||
// 可在此刷新订单状态
|
||||
fetchOrder();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('确认失败,请重试');
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
//跳转交接记录
|
||||
function toRecord(id) {
|
||||
uni.navigateTo({
|
||||
url: `/pages/order/handoverRecord?orderId=${orderId.value}`
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.order-detail-page {
|
||||
// background: #fff;
|
||||
background: #f7f7f7;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.back {
|
||||
width: 44rpx;
|
||||
font-size: 40rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-right: 44rpx;
|
||||
}
|
||||
|
||||
.map-area {
|
||||
height: 360rpx;
|
||||
background: #f3f3f3;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.map-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.map-native {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-overlay {
|
||||
position: absolute;
|
||||
left: 20rpx;
|
||||
right: 20rpx;
|
||||
bottom: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.eta {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 10rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
background: #fff;
|
||||
padding: 10rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
color: #1e9fff;
|
||||
border: 1rpx solid #dbeeff;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20rpx;
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
background: #f7f7f7;
|
||||
min-height: 200rpx;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.address-block {
|
||||
background: #fff;
|
||||
padding: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.addr-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 12rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.addr-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.badge {
|
||||
width: 46rpx;
|
||||
height: 46rpx;
|
||||
border-radius: 23rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
font-size: 20rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
|
||||
.badge.pickup {
|
||||
background: #87d6ff;
|
||||
}
|
||||
|
||||
.badge.deliver {
|
||||
background: #ffd591;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.addr-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.addr-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.addr-sub {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #888;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
color: #1e9fff;
|
||||
padding: 6rpx 8rpx;
|
||||
}
|
||||
|
||||
.note {
|
||||
margin-top: 12rpx;
|
||||
background: #fff;
|
||||
padding: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.goods-list {
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
.goods-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12rpx;
|
||||
background: #fff;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.goods-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12rpx;
|
||||
background: #fff;
|
||||
margin-top: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.g-left {
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.g-left {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.g-name {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
max-width: 62%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.g-qty {
|
||||
color: #888;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
.g-right {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.action-record {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
|
||||
.call {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
padding: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
|
||||
.record {
|
||||
flex: 2;
|
||||
background: #fff;
|
||||
padding: 12rpx;
|
||||
border-radius: 8rpx;
|
||||
text-align: center;
|
||||
border: 1rpx solid #ddd;
|
||||
}
|
||||
|
||||
.fixed-actions {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 140rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
gap: 12rpx;
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.left-actions {
|
||||
width: 72rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.right-actions {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 16rpx 18rpx;
|
||||
border-radius: 12rpx;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn.remind {
|
||||
background: #f39c12;
|
||||
}
|
||||
|
||||
.btn.confirm {
|
||||
background: #1e9fff;
|
||||
}
|
||||
|
||||
.remind-popup {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.remind-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx;
|
||||
background: #f6f6f6;
|
||||
margin: 10rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.remind-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.remind-btn {
|
||||
background: #1e9fff;
|
||||
color: #fff;
|
||||
padding: 10rpx 18rpx;
|
||||
border-radius: 8rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.remind-cancel {
|
||||
text-align: center;
|
||||
padding: 18rpx 0 70rpx;
|
||||
color: #666;
|
||||
font-size: 26rpx;
|
||||
background: #fff;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
</style>
|
||||
155
pages/order/handoverRecord.vue
Normal file
155
pages/order/handoverRecord.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<s-layout title="交接记录" class="set-userinfo-wrap">
|
||||
<view class="transfer-page">
|
||||
<scroll-view class="body" scroll-y>
|
||||
<u-steps direction="column" :current="currentIndex" class="steps-wrap">
|
||||
<u-steps-item v-for="(item, idx) in records" :key="idx">
|
||||
<template #title>
|
||||
<text class="step-title">{{ item.title }}</text>
|
||||
</template>
|
||||
<template #desc>
|
||||
<text class="step-desc">{{ item.time }}</text>
|
||||
</template>
|
||||
<template #content>
|
||||
<view class="step-content">
|
||||
<text class="op-name">{{ item.operator }}</text>
|
||||
<text class="op-note" v-if="item.note"> · {{ item.note }}</text>
|
||||
</view>
|
||||
</template>
|
||||
</u-steps-item>
|
||||
</u-steps>
|
||||
<view v-if="records.length === 0" class="empty">暂无交接记录</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import sheep from '@/sheep';
|
||||
|
||||
const records = ref([]);
|
||||
const orderId = ref(null);
|
||||
|
||||
onLoad((options = {}) => {
|
||||
orderId.value = options.id || options.orderId || null;
|
||||
fetchRecords();
|
||||
});
|
||||
|
||||
async function fetchRecords() {
|
||||
try {
|
||||
if (sheep && typeof sheep.request === 'function') {
|
||||
const res = await sheep.request({
|
||||
url: '/order/transferRecords',
|
||||
method: 'GET',
|
||||
data: {
|
||||
id: orderId.value
|
||||
}
|
||||
});
|
||||
records.value = (res && res.data) ? res.data.records || res.data : res.records || res;
|
||||
} else {
|
||||
// mock 数据
|
||||
records.value = [{
|
||||
title: '已接单',
|
||||
time: '2026-01-15 10:02',
|
||||
operator: '系统',
|
||||
note: '订单自动接单'
|
||||
},
|
||||
{
|
||||
title: '到店取货',
|
||||
time: '2026-01-15 10:12',
|
||||
operator: '骑手 张三',
|
||||
note: '已取货'
|
||||
},
|
||||
{
|
||||
title: '转单给同城骑手',
|
||||
time: '2026-01-15 10:20',
|
||||
operator: '客服 小李',
|
||||
note: '因配送区域调整'
|
||||
}
|
||||
];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchRecords error', e);
|
||||
sheep.$helper && sheep.$helper.toast && sheep.$helper.toast('获取交接记录失败');
|
||||
records.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
const currentIndex = computed(() => Math.max(0, records.value.length - 1));
|
||||
|
||||
function goBack() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.transfer-page {
|
||||
background: #fff;
|
||||
/* min-height: 100vh; */
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 20rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 20rpx;
|
||||
/* background: #f7f7f7; */
|
||||
/* min-height: calc(100vh - 88rpx); */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.steps-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
color: #333;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.step-desc {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
display: block;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
margin-top: 10rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
display: flex;
|
||||
gap: 8rpx;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.op-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.op-note {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 60rpx 0;
|
||||
}
|
||||
</style>
|
||||
223
pages/registered/accountInfo.vue
Normal file
223
pages/registered/accountInfo.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<s-layout title="工资结算账户信息" class="set-userinfo-wrap">
|
||||
<view class="page">
|
||||
<up-form ref="acctForm" :model="form" :rules="rules" labelPosition="left" labelWidth="120">
|
||||
<up-form-item label="开户行城市" prop="bankCity" :required="true">
|
||||
<up-input readonly v-model="bankCityLabel" placeholder="省-市" @tap="regionShow = true" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="开户行别" prop="bankName" :required="true">
|
||||
<up-picker hasInput :columns="bankOptions" v-model="bankNameLabel" @confirm="onBankConfirm">
|
||||
<template #trigger>
|
||||
<up-input readonly v-model="bankNameLabel" placeholder="请选择开户行" />
|
||||
</template>
|
||||
</up-picker>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="开户行网点名称" prop="bankBranch" :required="true">
|
||||
<up-picker hasInput :columns="branchOptions" v-model="bankBranchLabel" @confirm="onBranchConfirm">
|
||||
<template #trigger>
|
||||
<up-input readonly v-model="bankBranchLabel" placeholder="请选择网点名称" />
|
||||
</template>
|
||||
</up-picker>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="银行卡号" prop="cardNo" :required="true">
|
||||
<up-input v-model="form.cardNo" placeholder="请输入银行卡号" type="number" maxlength="23" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="持卡人姓名" prop="cardHolder" :required="true">
|
||||
<up-input v-model="form.cardHolder" placeholder="请输入持卡人姓名" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="银行代码" prop="bankCode">
|
||||
<up-input v-model="form.bankCode" placeholder="请输入银行代码(如有)" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="手机号" prop="phone" :required="true">
|
||||
<view class="code-row">
|
||||
<view style="width:280rpx;">
|
||||
<up-input v-model="form.phone" placeholder="预留手机号码" type="number" maxlength="11" />
|
||||
</view>
|
||||
<view style="width:160rpx;margin-left:10rpx;">
|
||||
<up-button :disabled="countdown > 0" plain @click="sendCode">
|
||||
{{ countdown > 0 ? countdown + 's' : '获取验证码' }}
|
||||
</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="验证码" prop="captcha" :required="true">
|
||||
<up-input v-model="form.captcha" placeholder="输入验证码" maxlength="6" />
|
||||
</up-form-item>
|
||||
|
||||
<view class="agree-row" @click="agree = !agree">
|
||||
<view class="checkbox" :class="{checked: agree}"></view>
|
||||
<text class="agree-text">勾选同意 <text class="link">《骑手协议》</text> 提交成功后将会有专人与您联系</text>
|
||||
</view>
|
||||
|
||||
<view class="submit-row">
|
||||
<up-button type="primary" block @click="onSubmitAccount">提交申请审核</up-button>
|
||||
</view>
|
||||
</up-form>
|
||||
</view>
|
||||
<su-region-picker level="2" :show="regionShow" @confirm="onRegionConfirm" @cancel="regionShow = false" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onBeforeMount } from 'vue'
|
||||
|
||||
const form = reactive({
|
||||
bankCity: '',
|
||||
bankName: '',
|
||||
bankBranch: '',
|
||||
cardNo: '',
|
||||
cardHolder: '',
|
||||
bankCode: '',
|
||||
phone: '',
|
||||
captcha: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
bankCity: [{ required: true, message: '请选择开户城市' }],
|
||||
bankName: [{ required: true, message: '请选择开户行别' }],
|
||||
bankBranch: [{ required: true, message: '请选择网点名称' }],
|
||||
cardNo: [{ required: true, message: '请输入银行卡号' }],
|
||||
cardHolder: [{ required: true, message: '请输入持卡人姓名' }],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号' },
|
||||
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号码' },
|
||||
],
|
||||
captcha: [{ required: true, message: '请输入验证码' }],
|
||||
}
|
||||
|
||||
const acctForm = ref(null)
|
||||
const regionShow = ref(false)
|
||||
const bankOptions = [
|
||||
['中国工商银行', '中国建设银行', '中国农业银行', '中国银行', '交通银行', '招商银行']
|
||||
]
|
||||
const branchOptions = [
|
||||
['请选择网点']
|
||||
]
|
||||
const bankNameLabel = ref([])
|
||||
const bankBranchLabel = ref([])
|
||||
const bankCityLabel = ref('')
|
||||
const countdown = ref(0)
|
||||
let timer = null
|
||||
const agree = ref(false)
|
||||
|
||||
function onBankConfirm(selected) {
|
||||
const first = Array.isArray(selected) ? selected[0] : selected
|
||||
bankNameLabel.value = first?.value || first || ''
|
||||
form.bankName = bankNameLabel.value
|
||||
// 模拟获取分支列表,根据银行设置简单示例
|
||||
branchOptions[0] = bankNameLabel.value ? [`${bankNameLabel.value} 总行`, `${bankNameLabel.value} 广州分行`, `${bankNameLabel.value} 天河支行`] : ['请选择网点']
|
||||
}
|
||||
|
||||
function onBranchConfirm(selected) {
|
||||
const first = Array.isArray(selected) ? selected[0] : selected
|
||||
bankBranchLabel.value = first?.value || first || ''
|
||||
form.bankBranch = bankBranchLabel.value
|
||||
}
|
||||
|
||||
function onRegionConfirm(result) {
|
||||
console.log("result", result);
|
||||
form.bankCity = result
|
||||
bankCityLabel.value = `${result.province_name || ''} ${result.city_name || ''}`.trim()
|
||||
regionShow.value = false
|
||||
}
|
||||
|
||||
function sendCode() {
|
||||
// 简单校验手机号
|
||||
if (!/^1\d{10}$/.test(form.phone)) {
|
||||
uni.showToast({ title: '请输入正确手机号', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (countdown.value > 0) return
|
||||
// 触发发送(此处模拟)
|
||||
uni.showToast({ title: '验证码已发送', icon: 'none' })
|
||||
countdown.value = 60
|
||||
timer = setInterval(() => {
|
||||
if (countdown.value <= 1) {
|
||||
clearInterval(timer)
|
||||
countdown.value = 0
|
||||
timer = null
|
||||
} else {
|
||||
countdown.value -= 1
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
async function onSubmitAccount() {
|
||||
try {
|
||||
await acctForm.value.validate()
|
||||
if (!agree.value) {
|
||||
uni.showToast({ title: '请先同意骑手协议', icon: 'none' })
|
||||
return
|
||||
}
|
||||
// 提交逻辑(示例):打印并提示
|
||||
console.log('结算表单', JSON.parse(JSON.stringify(form)))
|
||||
uni.showToast({ title: '提交申请成功', icon: 'none' })
|
||||
// 跳转到审核中页面
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({ url: '/pages/registered/audit' })
|
||||
}, 600)
|
||||
} catch (e) {
|
||||
console.warn('结算表单校验未通过', e)
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
// 尝试从注册页恢复部分信息(如持卡人姓名)
|
||||
try {
|
||||
const saved = uni.getStorageSync('riderFormData') || null
|
||||
if (saved) {
|
||||
form.cardHolder = saved.realName || ''
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: 38rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.code-row {
|
||||
display: flex;
|
||||
// gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
.agree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
gap: 12rpx;
|
||||
color: #999;
|
||||
}
|
||||
.checkbox {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.checkbox.checked {
|
||||
background: #09aaff;
|
||||
border-color: #09aaff;
|
||||
}
|
||||
.agree-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
.link {
|
||||
color: #09aaff;
|
||||
}
|
||||
.submit-row {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
69
pages/registered/audit.vue
Normal file
69
pages/registered/audit.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<s-layout class="audit-wrap">
|
||||
<view class="audit-content">
|
||||
<view class="icon-wrap" aria-hidden="true">
|
||||
<svg viewBox="0 0 64 64" class="clock-svg" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="32" cy="32" r="30" fill="#09aaff"/>
|
||||
<path d="M32 18v14l10 6" stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</view>
|
||||
<view class="title">审核中</view>
|
||||
<view class="desc">审核结果将以短信进行通知,通过后分配订单哦~</view>
|
||||
<view class="btn-row">
|
||||
<up-button type="primary" plain @click="onDone">完成</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { } from 'vue'
|
||||
|
||||
function onDone() {
|
||||
// 返回首页(重启栈)
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.audit-wrap {
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.audit-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-top: 140rpx;
|
||||
}
|
||||
.icon-wrap {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.clock-svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.desc {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
padding: 0 40rpx;
|
||||
margin-bottom: 60rpx;
|
||||
}
|
||||
.btn-row {
|
||||
width: 520rpx;
|
||||
}
|
||||
</style>
|
||||
691
pages/registered/registerRiders.vue
Normal file
691
pages/registered/registerRiders.vue
Normal file
@@ -0,0 +1,691 @@
|
||||
<template>
|
||||
<s-layout title="注册骑手" class="set-userinfo-wrap">
|
||||
<view class="page">
|
||||
<up-form ref="riderForm" :model="form" :rules="rules" labelPosition="left" labelWidth="90">
|
||||
<view class="section-title">基础信息</view>
|
||||
|
||||
<up-form-item label="真实姓名" prop="realName" :required="true">
|
||||
<up-input v-model="form.realName" placeholder="请输入您的姓名" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="身份证号" prop="idNo" :required="true">
|
||||
<up-input v-model="form.idNo" placeholder="数字开头18位号码" maxlength="18" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="生效日期" prop="birthDate" :required="true">
|
||||
<up-datetime-picker hasInput v-model="form.birthDate" mode="date" placeholder="请选择身份证生效日期" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="失效日期" prop="expiryDate" :required="true">
|
||||
<view class="expiry-row">
|
||||
<up-radio-group v-model="form.expiryMode" direction="horizontal">
|
||||
<up-radio name="long" label="长期有效"></up-radio>
|
||||
<up-radio name="date" label="选择失效日期"></up-radio>
|
||||
</up-radio-group>
|
||||
<up-datetime-picker v-if="form.expiryMode === 'date'" hasInput
|
||||
v-model="form.expiryDate" mode="date" placeholder="选择失效日期" />
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="性别" prop="gender" :required="true">
|
||||
<up-radio-group v-model="form.gender" direction="horizontal">
|
||||
<up-radio name="male" label="男"></up-radio>
|
||||
<up-radio name="female" label="女"></up-radio>
|
||||
</up-radio-group>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="紧急联系人姓名" prop="emergencyName" :required="true">
|
||||
<up-input v-model="form.emergencyName" placeholder="请输入" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="紧急联系人手机" prop="emergencyPhone" :required="true">
|
||||
<up-input v-model="form.emergencyPhone" placeholder="请输入" type="tel" maxlength="11" />
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="上传身份证正反面" prop="idImages">
|
||||
<view class="upload-row">
|
||||
<view class="upload-box">
|
||||
<up-upload :max-count="1" :show-file-list="false" @change="onUploadFront">
|
||||
<view class="upload-placeholder" v-if="!frontImage">
|
||||
<up-icon name="+" />
|
||||
</view>
|
||||
<up-image v-else :src="frontImage" mode="aspectFill" class="thumb" />
|
||||
</up-upload>
|
||||
<text class="hint">国徽面</text>
|
||||
</view>
|
||||
<view class="upload-box">
|
||||
<up-upload :max-count="1" :show-file-list="false" @change="onUploadBack">
|
||||
<view class="upload-placeholder" v-if="!backImage">
|
||||
<up-icon name="+" />
|
||||
</view>
|
||||
<up-image v-else :src="backImage" mode="aspectFill" class="thumb" />
|
||||
</up-upload>
|
||||
<text class="hint">人像面</text>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<view class="section-title">接单选择</view>
|
||||
<up-form-item label="职业身份" prop="occupation" :required="true">
|
||||
<up-radio-group v-model="form.occupation" direction="horizontal">
|
||||
<up-radio name="student" label="在校学生"></up-radio>
|
||||
<up-radio name="worker" label="社会人员/职工"></up-radio>
|
||||
</up-radio-group>
|
||||
</up-form-item>
|
||||
|
||||
<!-- 学生视图:兼职意愿、可兼职时段、健康证 -->
|
||||
<template v-if="form.occupation === 'student'">
|
||||
<up-form-item label="兼职意愿" prop="partTimeIntent" :required="true">
|
||||
<up-picker hasInput :columns="partTimeOptions" v-model="partTimeLabel" @confirm="onPartTimeConfirm"></up-picker>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="请选择可兼职时段" prop="partTimePeriods">
|
||||
<view class="choose-row" @click="timeShow1 = true">
|
||||
<text class="muted">{{ partTimePeriodsLabel }}</text>
|
||||
<text class="status" :class="{empty: form.partTimePeriods.length===0}">{{ form.partTimePeriods.length===0 ? '待完善 >' : '已完善' }}</text>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<up-popup :show="timeShow1" mode="bottom" @close="timeShow1 = false">
|
||||
<view class="popup-content">
|
||||
<view class="popup-header">
|
||||
<text>选择可兼职时段</text>
|
||||
</view>
|
||||
<view class="popup-body">
|
||||
<!-- 网格:每行为一天,三段时段按钮可切换选中 -->
|
||||
<view class="time-grid">
|
||||
<view class="time-row" v-for="(day, dayIndex) in days" :key="dayIndex">
|
||||
<view class="day-label">{{ day }}</view>
|
||||
<view class="slots">
|
||||
<view
|
||||
class="slot-btn"
|
||||
:class="{selected: isSlotSelected(dayIndex, 0)}"
|
||||
@click="toggleSlot(dayIndex, 0)"
|
||||
key="m"
|
||||
>08:00~13:00</view>
|
||||
<view
|
||||
class="slot-btn"
|
||||
:class="{selected: isSlotSelected(dayIndex, 1)}"
|
||||
@click="toggleSlot(dayIndex, 1)"
|
||||
key="a"
|
||||
>13:00~17:30</view>
|
||||
<view
|
||||
class="slot-btn"
|
||||
:class="{selected: isSlotSelected(dayIndex, 2)}"
|
||||
@click="toggleSlot(dayIndex, 2)"
|
||||
key="e"
|
||||
>17:00~22:30</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="popup-footer">
|
||||
<up-button plain @click="onCancelTime">取消</up-button>
|
||||
<up-button type="primary" @click="onSaveTime">保存</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
</template>
|
||||
|
||||
<!-- 社会人员/职工视图:类别、意向城市、健康证 -->
|
||||
<template v-else>
|
||||
<up-form-item label="类别" prop="category" :required="true">
|
||||
<up-picker hasInput :columns="categoryOptions" @confirm="onCategoryConfirm">
|
||||
<template #trigger>
|
||||
<up-input readonly v-model="categoryLabel" placeholder="请选择类别" />
|
||||
</template>
|
||||
</up-picker>
|
||||
</up-form-item>
|
||||
|
||||
<up-form-item label="意向城市" prop="city" :required="true">
|
||||
<up-input readonly v-model="cityLabel" placeholder="请选择" @tap="regionShow = true" />
|
||||
</up-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 健康证(学生/社会人员皆可上传) -->
|
||||
<up-form-item label="健康证" prop="healthCert">
|
||||
<view class="health-row" @click="openHealthPopup">
|
||||
<view class="health-left">上传健康证</view>
|
||||
<view class="health-right">
|
||||
<up-icon name="arrow-right" color="#6c6c6c" size="21"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
</up-form-item>
|
||||
|
||||
<view class="submit-row">
|
||||
<up-button type="primary" @click="onSubmit">保存并下一步,完善工资结算信息</up-button>
|
||||
</view>
|
||||
</up-form>
|
||||
</view>
|
||||
|
||||
<!-- 健康证信息弹框 -->
|
||||
<up-popup :show="popupShow" mode="bottom" @close="onCancelHealth" :round="16" safeAreaInsetBottom>
|
||||
<view class="health-popup">
|
||||
<view class="health-popup-header">
|
||||
<text class="title">上传健康证</text>
|
||||
</view>
|
||||
<view class="health-popup-body">
|
||||
<view class="field-row">
|
||||
<text class="label">编号:</text>
|
||||
<up-input v-model="healthNumber" placeholder="请输入健康证编号" class="field-input" />
|
||||
</view>
|
||||
<view class="field-row">
|
||||
<text class="label">类别:</text>
|
||||
<up-input v-model="healthCategory" placeholder="请输入健康证类别" class="field-input" />
|
||||
</view>
|
||||
<view class="field-row date-row">
|
||||
<text class="label">有效日期:</text>
|
||||
<up-datetime-picker hasInput v-model="healthValidStart" mode="date" placeholder="年/月/日" class="date-input" />
|
||||
<text class="dash"> — </text>
|
||||
<up-datetime-picker hasInput v-model="healthValidEnd" mode="date" placeholder="年/月/日" class="date-input" />
|
||||
</view>
|
||||
|
||||
<view class="upload-list">
|
||||
<text class="upload-title">上传健康证:</text>
|
||||
<view class="upload-row-inner">
|
||||
<view v-for="(img, idx) in healthImages" :key="idx" class="upload-item">
|
||||
<up-image :src="img" class="upload-thumb" mode="aspectFill" />
|
||||
<view class="remove-btn" @click.stop="removeHealthImage(idx)">×</view>
|
||||
</view>
|
||||
<up-upload :max-count="1" :show-file-list="false" @change="onUploadHealth">
|
||||
<view class="upload-add" v-if="healthImages.length < 4">
|
||||
<up-icon name="+" />
|
||||
</view>
|
||||
</up-upload>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="health-popup-footer">
|
||||
<up-button plain @click="onCancelHealth">取消</up-button>
|
||||
<up-button type="primary" class="save-btn" @click="onSaveHealth">保存</up-button>
|
||||
</view>
|
||||
</view>
|
||||
</up-popup>
|
||||
<su-region-picker :show="regionShow" @confirm="onRegionConfirm" @cancel="regionShow = false" />
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, computed, watch, onBeforeMount } from 'vue'
|
||||
import AreaApi from '@/sheep/api/system/area';
|
||||
// import SuRegionPicker from 'sheep/ui/su-region-picker/su-region-picker.vue'
|
||||
|
||||
const form = reactive({
|
||||
realName: '',
|
||||
idNo: '',
|
||||
birthDate: '',
|
||||
expiryMode: 'long',
|
||||
expiryDate: '',
|
||||
gender: 'male',
|
||||
emergencyName: '',
|
||||
emergencyPhone: '',
|
||||
idImages: [],
|
||||
// 接单选择相关字段
|
||||
occupation: 'student',
|
||||
partTimeIntent: '',
|
||||
partTimePeriods: [],
|
||||
healthCert: '',
|
||||
// 健康证详情字段(弹窗保存到这里)
|
||||
healthCertNumber: '',
|
||||
healthCertCategory: '',
|
||||
healthCertValidStart: '',
|
||||
healthCertValidEnd: '',
|
||||
category: '',
|
||||
city: '',
|
||||
})
|
||||
|
||||
const timeShow1 = ref(false)
|
||||
const partTimeOptions = reactive([
|
||||
['长期(至少1学期)', '非长期(临时/偶尔兼职)']
|
||||
])
|
||||
const partTimeLabel = ref([])
|
||||
const categoryOptions = [
|
||||
['全职', '兼职']
|
||||
]
|
||||
const categoryLabel = ref([])
|
||||
const cityOptions = [
|
||||
{ text: '请选择城市', value: '' },
|
||||
{ text: '北京市', value: 'beijing' },
|
||||
{ text: '上海市', value: 'shanghai' },
|
||||
]
|
||||
const cityLabel = ref('')
|
||||
const regionShow = ref(false)
|
||||
// 可选时段网格数据与选择状态
|
||||
const days = ['周日','周一','周二','周三','周四','周五','周六']
|
||||
const timeSlots = ['08:00~13:00','13:00~17:30','17:00~22:30']
|
||||
// 使用二维布尔数组表示选择状态:selectedGrid[dayIndex][slotIndex] = true/false
|
||||
const selectedGrid = reactive(Array.from({ length: days.length }, () => [false, false, false]))
|
||||
// 弹窗临时副本,打开时拷贝 selectedGrid 到 tempSelected,用于取消恢复
|
||||
const tempSelected = reactive(Array.from({ length: days.length }, () => [false, false, false]))
|
||||
|
||||
const popupShow = ref(false)
|
||||
|
||||
function isSlotSelected(dayIndex, slotIndex) {
|
||||
return tempSelected[dayIndex][slotIndex]
|
||||
}
|
||||
|
||||
function toggleSlot(dayIndex, slotIndex) {
|
||||
tempSelected[dayIndex][slotIndex] = !tempSelected[dayIndex][slotIndex]
|
||||
}
|
||||
|
||||
function onCancelTime() {
|
||||
// 恢复原选中状态并关闭弹窗
|
||||
for (let i = 0; i < days.length; i++) {
|
||||
for (let j = 0; j < timeSlots.length; j++) {
|
||||
tempSelected[i][j] = selectedGrid[i][j]
|
||||
}
|
||||
}
|
||||
timeShow1.value = false
|
||||
}
|
||||
|
||||
function onSaveTime() {
|
||||
// 将 tempSelected 同步到 selectedGrid 和 form.partTimePeriods(保存为可读文本)
|
||||
const selections = []
|
||||
for (let i = 0; i < days.length; i++) {
|
||||
for (let j = 0; j < timeSlots.length; j++) {
|
||||
selectedGrid[i][j] = tempSelected[i][j]
|
||||
if (tempSelected[i][j]) {
|
||||
selections.push(`${days[i]} ${timeSlots[j]}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
form.partTimePeriods = selections
|
||||
timeShow1.value = false
|
||||
}
|
||||
|
||||
// 打开弹窗时将当前选择拷贝到 tempSelected
|
||||
watch(timeShow1, (val) => {
|
||||
if (val) {
|
||||
for (let i = 0; i < days.length; i++) {
|
||||
for (let j = 0; j < timeSlots.length; j++) {
|
||||
tempSelected[i][j] = selectedGrid[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const rules = {
|
||||
realName: [{ required: true, message: '请输入真实姓名' }],
|
||||
idNo: [
|
||||
{ required: true, message: '请输入身份证号' },
|
||||
{ pattern: /^[0-9A-Za-z]{15,18}$/, message: '请输入正确的身份证号' },
|
||||
],
|
||||
birthDate: [{ required: true, message: '请选择生效日期' }],
|
||||
expiryDate: [
|
||||
{
|
||||
validator(value) {
|
||||
if (form.expiryMode === 'date' && !value) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
message: '请选择失效日期',
|
||||
},
|
||||
],
|
||||
gender: [{ required: true, message: '请选择性别' }],
|
||||
emergencyName: [{ required: true, message: '请输入紧急联系人姓名' }],
|
||||
emergencyPhone: [
|
||||
{ required: true, message: '请输入紧急联系人手机' },
|
||||
{ pattern: /^1\d{10}$/, message: '请输入正确的手机号码' },
|
||||
],
|
||||
}
|
||||
|
||||
const riderForm = ref(null)
|
||||
const frontImage = ref('')
|
||||
const backImage = ref('')
|
||||
const healthCert = ref('')
|
||||
// 健康证弹窗临时状态
|
||||
const healthImages = reactive([])
|
||||
const healthNumber = ref('')
|
||||
const healthCategory = ref('')
|
||||
const healthValidStart = ref('')
|
||||
const healthValidEnd = ref('')
|
||||
const partTimePeriodsLabel = computed(() => {
|
||||
return form.partTimePeriods.length ? form.partTimePeriods.join('、') : ''
|
||||
})
|
||||
|
||||
function onUploadFront(event) {
|
||||
const file = Array.isArray(event) ? event[0] : (event.detail || event)
|
||||
const url = file?.url || file?.path || file?.thumb || ''
|
||||
frontImage.value = url
|
||||
form.idImages = [url, form.idImages[1] || '']
|
||||
}
|
||||
|
||||
function onUploadBack(event) {
|
||||
const file = Array.isArray(event) ? event[0] : (event.detail || event)
|
||||
const url = file?.url || file?.path || file?.thumb || ''
|
||||
backImage.value = url
|
||||
form.idImages = [form.idImages[0] || '', url]
|
||||
}
|
||||
|
||||
function onUploadHealth(event) {
|
||||
const file = Array.isArray(event) ? event[0] : (event.detail || event)
|
||||
const url = file?.url || file?.path || file?.thumb || ''
|
||||
if (url) {
|
||||
// 限制最多 4 张预览图
|
||||
if (healthImages.length < 4) {
|
||||
healthImages.push(url)
|
||||
}
|
||||
healthCert.value = url
|
||||
// 保持兼容:form.healthCert 存首张图片(若需要可改为数组)
|
||||
form.healthCert = url
|
||||
}
|
||||
}
|
||||
|
||||
function onPartTimeConfirm(selected) {
|
||||
const first = Array.isArray(selected) ? selected[0] : selected
|
||||
if (first && (first.value || first.text)) {
|
||||
form.partTimeIntent = first.value || first.text
|
||||
partTimeLabel.value = first.text || first.value
|
||||
}
|
||||
}
|
||||
|
||||
function onCategoryConfirm(selected) {
|
||||
const first = Array.isArray(selected) ? selected[0] : selected
|
||||
if (first && (first.value || first.text)) {
|
||||
form.category = first.value || first.text
|
||||
categoryLabel.value = first.text || first.value
|
||||
}
|
||||
}
|
||||
|
||||
function onCityConfirm(selected) {
|
||||
const first = Array.isArray(selected) ? selected[0] : selected
|
||||
if (first && (first.value || first.text)) {
|
||||
form.city = first.value || first.text
|
||||
cityLabel.value = first.text || first.value
|
||||
}
|
||||
}
|
||||
|
||||
function onRegionConfirm(result) {
|
||||
// result: { province_name, province_id, city_name, city_id, district_name, district_id }
|
||||
form.city = result
|
||||
cityLabel.value = `${result.province_name || ''} ${result.city_name || ''} ${result.district_name || ''}`.trim()
|
||||
regionShow.value = false
|
||||
}
|
||||
|
||||
function openHealthPopup() {
|
||||
// 从表单恢复到弹窗临时状态
|
||||
healthImages.splice(0, healthImages.length)
|
||||
if (form.healthCert) {
|
||||
healthImages.push(form.healthCert)
|
||||
}
|
||||
healthNumber.value = form.healthCertNumber || ''
|
||||
healthCategory.value = form.healthCertCategory || ''
|
||||
healthValidStart.value = form.healthCertValidStart || ''
|
||||
healthValidEnd.value = form.healthCertValidEnd || ''
|
||||
popupShow.value = true
|
||||
}
|
||||
|
||||
function removeHealthImage(index) {
|
||||
if (index >= 0 && index < healthImages.length) {
|
||||
healthImages.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function onCancelHealth() {
|
||||
// 直接关闭弹窗,放弃临时更改
|
||||
popupShow.value = false
|
||||
}
|
||||
|
||||
function onSaveHealth() {
|
||||
// 保存弹窗数据回表单
|
||||
form.healthCert = healthImages.length ? healthImages[0] : ''
|
||||
healthCert.value = form.healthCert
|
||||
form.healthCertNumber = healthNumber.value
|
||||
form.healthCertCategory = healthCategory.value
|
||||
form.healthCertValidStart = healthValidStart.value
|
||||
form.healthCertValidEnd = healthValidEnd.value
|
||||
popupShow.value = false
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
await riderForm.value.validate()
|
||||
console.log('提交表单', JSON.parse(JSON.stringify(form)))
|
||||
// 将已填写的注册信息临时存储,供结算页继续使用
|
||||
try {
|
||||
uni.setStorageSync('riderFormData', JSON.parse(JSON.stringify(form)))
|
||||
} catch (err) {
|
||||
console.warn('存储注册信息失败', err)
|
||||
}
|
||||
// 跳转到工资结算账户信息页面
|
||||
uni.navigateTo({ url: '/pages/registered/accountInfo' })
|
||||
} catch (e) {
|
||||
console.warn('表单校验未通过', e)
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
if (!!uni.getStorageSync('areaData')) {
|
||||
return;
|
||||
}
|
||||
// 提前加载省市区数据
|
||||
AreaApi.getAreaTree().then((res) => {
|
||||
if (res.code === 0) {
|
||||
uni.setStorageSync('areaData', res.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page {
|
||||
padding: 38rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.section-title {
|
||||
padding: 10px 0;
|
||||
color: #666;
|
||||
font-weight: 600;
|
||||
}
|
||||
.expiry-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.upload-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
.upload-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.upload-placeholder {
|
||||
width: 88px;
|
||||
height: 60px;
|
||||
border: 1px dashed #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.thumb {
|
||||
width: 88px;
|
||||
height: 60px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.hint {
|
||||
margin-top: 6px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
.submit-row {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.time-grid {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.day-label {
|
||||
width: 90rpx;
|
||||
color: #333;
|
||||
}
|
||||
.slots {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
.slot-btn {
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
background: #f5f5f5;
|
||||
color: #333;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.slot-btn.selected {
|
||||
background: #09aaff;
|
||||
color: #fff;
|
||||
}
|
||||
.popup-footer {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.popup-content {
|
||||
padding: 50rpx 20rpx 20rpx;
|
||||
}
|
||||
.popup-body {
|
||||
padding: 10rpx 15rpx 20rpx;
|
||||
}
|
||||
/* 健康证相关样式 */
|
||||
.health-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
.health-left {
|
||||
color: #333;
|
||||
}
|
||||
.health-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.health-thumbs {
|
||||
display: flex;
|
||||
gap: 10rpx;
|
||||
align-items: center;
|
||||
}
|
||||
.health-thumb-wrap {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
}
|
||||
.health-thumb {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.health-add {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border: 1px dashed #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
color: #999;
|
||||
}
|
||||
.health-popup {
|
||||
padding: 30rpx 30rpx 30rpx;
|
||||
background: #fff;
|
||||
}
|
||||
.health-popup-header .title {
|
||||
font-weight: 700;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.health-popup-body {
|
||||
padding: 10rpx 0 20rpx;
|
||||
}
|
||||
.field-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 38rpx;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.label {
|
||||
width: 90rpx;
|
||||
color: #666;
|
||||
}
|
||||
.field-input {
|
||||
flex: 1;
|
||||
}
|
||||
.date-row .date-input {
|
||||
width: 35%;
|
||||
}
|
||||
.dash {
|
||||
width: 10rpx;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
.upload-list {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.upload-title {
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
.upload-row-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.upload-item {
|
||||
position: relative;
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
}
|
||||
.upload-thumb {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.remove-btn {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
background: rgba(0,0,0,0.6);
|
||||
color: #fff;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
line-height: 24px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.upload-add {
|
||||
width: 88px;
|
||||
height: 88px;
|
||||
border: 1px dashed #ddd;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
color: #999;
|
||||
}
|
||||
.health-popup-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
.save-btn {
|
||||
width: 240rpx;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
214
pages/user/orderRecord.vue
Normal file
214
pages/user/orderRecord.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<s-layout title="接单记录" class="order-record-page">
|
||||
<view class="page-content">
|
||||
<view class="top-select" @tap="showPicker = true">
|
||||
<view class="select-style">
|
||||
<text style="margin-right:15rpx;">{{ selectedLabel }}</text>
|
||||
<up-icon name="arrow-down" color="#757575" size="15"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<!-- up-picker -->
|
||||
<up-picker
|
||||
:show="showPicker"
|
||||
:columns="columns"
|
||||
:defaultIndex="[defaultIndex]"
|
||||
@confirm="onConfirm"
|
||||
@cancel="showPicker = false"
|
||||
@close="showPicker = false"
|
||||
></up-picker>
|
||||
|
||||
<!-- 顶部提示 -->
|
||||
<view class="notice">数据统计均截止昨日23:59,可能存在延迟,请耐心等待</view>
|
||||
|
||||
<!-- 单量卡片 -->
|
||||
<view class="card card-volume">
|
||||
<view class="card-header">
|
||||
<view class="card-title">单量</view>
|
||||
<view class="card-link" @click="toList">
|
||||
查看 <up-icon name="arrow-right" color="#757575" size="15"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-body volume-body">
|
||||
<text class="volume-number">10<span class="unit">单</span></text>
|
||||
<text class="volume-sub">已完成</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 转单记录卡片 -->
|
||||
<!-- <view class="card card-transfer">
|
||||
<view class="card-header">
|
||||
<view class="left">
|
||||
<text class="card-title">转单记录</text>
|
||||
</view>
|
||||
<view class="card-link">
|
||||
查看 <up-icon name="arrow-right" color="#757575" size="15"></up-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="card-body transfer-body">
|
||||
<view class="col">
|
||||
<text class="col-number">0<span class="unit">单</span></text>
|
||||
<text class="col-label">待处理订单</text>
|
||||
</view>
|
||||
<view class="col">
|
||||
<text class="col-number">0<span class="unit">单</span></text>
|
||||
<text class="col-label">我转出的</text>
|
||||
</view>
|
||||
<view class="col">
|
||||
<text class="col-number">0<span class="unit">单</span></text>
|
||||
<text class="col-label">我接收的</text>
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// picker 显示控制
|
||||
const showPicker = ref(false);
|
||||
// 默认选中标签
|
||||
const selectedLabel = ref('今日');
|
||||
// 默认选中的索引(0:今日)
|
||||
const defaultIndex = 0;
|
||||
|
||||
// 列数据,u-picker 接受 columns 为数组的数组
|
||||
const columns = [
|
||||
[
|
||||
{ text: '今日', value: 'today' },
|
||||
{ text: '昨日', value: 'yesterday' },
|
||||
{ text: '近7天', value: 'last7' },
|
||||
{ text: '本月', value: 'month' },
|
||||
],
|
||||
];
|
||||
|
||||
function onConfirm(e) {
|
||||
// e.value 为选中的值数组(每列一个)
|
||||
const value = e && e.value && e.value[0];
|
||||
if (value) {
|
||||
selectedLabel.value = value.text || String(value);
|
||||
}
|
||||
showPicker.value = false;
|
||||
}
|
||||
|
||||
function toList() {
|
||||
uni.navigateTo({
|
||||
url: '/pages/user/recordList'
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-content {
|
||||
padding: 16px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.top-select {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 0 25rpx;
|
||||
}
|
||||
|
||||
.select-style {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.notice {
|
||||
background: #f5f5f5;
|
||||
color: #999;
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 14px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-link {
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.card-header .left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
color: #e74c3c;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.volume-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 8px 0 4px;
|
||||
}
|
||||
|
||||
.volume-number {
|
||||
font-size: 36px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 18px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.volume-sub {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.transfer-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.col {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.col-number {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.col-label {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
200
pages/user/recordList.vue
Normal file
200
pages/user/recordList.vue
Normal file
@@ -0,0 +1,200 @@
|
||||
<template>
|
||||
<s-layout title="订单记录" class="record-list-page">
|
||||
<view class="page-wrap">
|
||||
<scroll-view class="list" scroll-y>
|
||||
<view class="order-card" v-for="order in orders" :key="order.id">
|
||||
<!-- 收入 -->
|
||||
<view class="card-top">
|
||||
<text class="income">本单收入 {{ order.income }}元</text>
|
||||
</view>
|
||||
|
||||
<!-- 分割线 -->
|
||||
<view class="divider"></view>
|
||||
|
||||
<!-- 内容头 -->
|
||||
<view class="card-header">
|
||||
<view class="left">
|
||||
<text class="label">订单号:</text>
|
||||
<text class="order-no">{{ order.orderNo }}</text>
|
||||
</view>
|
||||
<text class="status">{{ order.statusText }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 可能的提示 -->
|
||||
<view v-if="order.notice" class="notice-orange">
|
||||
{{ order.notice }}
|
||||
</view>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<view class="card-body">
|
||||
<!-- 起点 -->
|
||||
<view class="addr-row">
|
||||
<text class="distance">{{ order.pickDistance }}</text>
|
||||
<view class="addr-content">
|
||||
<text class="place-title">{{ order.pickName }}</text>
|
||||
<text class="place-addr">{{ order.pickAddr }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 终点 -->
|
||||
<view class="addr-row to">
|
||||
<text class="distance">{{ order.deliverDistance }}</text>
|
||||
<view class="addr-content">
|
||||
<text class="place-title">{{ order.deliverName }}</text>
|
||||
<text class="place-addr">{{ order.deliverAddr }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部信息 -->
|
||||
<view class="card-footer">
|
||||
<text class="customer">{{ order.customer }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</s-layout>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
// 静态示例数据,后续可对接接口
|
||||
const orders = ref([
|
||||
{
|
||||
id: 1,
|
||||
income: '12.5',
|
||||
orderNo: '2021021115544',
|
||||
statusText: '已完成',
|
||||
notice: '',
|
||||
pickDistance: '873m',
|
||||
pickName: '乐易购-学院站',
|
||||
pickAddr: '广东省广州市天河区学院站荷光路',
|
||||
deliverDistance: '1.2km',
|
||||
deliverName: '广东省广州市天河区**********',
|
||||
deliverAddr: '张氏(先生) 屋号1254',
|
||||
customer: '',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
income: '12.5',
|
||||
orderNo: '2021021115545',
|
||||
statusText: '顾客取消订单',
|
||||
notice: '提示:已取餐订单,顾客退款不影响配送费结算',
|
||||
pickDistance: '873m',
|
||||
pickName: '乐易购-学院站',
|
||||
pickAddr: '广东省广州市天河区学院站荷光路',
|
||||
deliverDistance: '1.2km',
|
||||
deliverName: '广东省广州市天河区**********',
|
||||
deliverAddr: '张氏(先生) 屋号1254',
|
||||
customer: '',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
income: '12.5',
|
||||
orderNo: '2021021115546',
|
||||
statusText: '顾客取消订单',
|
||||
notice: '',
|
||||
pickDistance: '873m',
|
||||
pickName: '乐易购-学院站',
|
||||
pickAddr: '广东省广州市天河区学院站荷光路',
|
||||
deliverDistance: '1.2km',
|
||||
deliverName: '广东省广州市天河区**********',
|
||||
deliverAddr: '张氏(先生) 屋号1254',
|
||||
customer: '',
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-wrap {
|
||||
padding: 16px;
|
||||
background: transparent;
|
||||
}
|
||||
.list {
|
||||
min-height: 200px;
|
||||
}
|
||||
.order-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 14px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||
}
|
||||
.card-top {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.income {
|
||||
color: #e74c3c;
|
||||
font-weight: 600;
|
||||
}
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: #eee;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.card-header .left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.label {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.order-no {
|
||||
color: #222;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
.notice-orange {
|
||||
background: #f3a23a;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
margin: 6px 14px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.card-body {
|
||||
padding: 10px 14px 6px;
|
||||
}
|
||||
.addr-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.addr-row.to {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.distance {
|
||||
color: #999;
|
||||
width: 42px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.addr-content {
|
||||
flex: 1;
|
||||
}
|
||||
.place-title {
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
color: #222;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.place-addr {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
.card-footer {
|
||||
padding: 10px 14px 14px;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user