first commit

This commit is contained in:
Ls
2026-02-12 12:19:20 +08:00
commit 219fd9be5c
529 changed files with 169918 additions and 0 deletions

1
.svn/entries Normal file
View File

@@ -0,0 +1 @@
12

1
.svn/format Normal file
View File

@@ -0,0 +1 @@
12

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,13 @@
import { createSSRApp } from "vue";
import App from "./App.vue";
import uviewPlus from 'uview-plus'
export function createApp() {
const app = createSSRApp(App);
App.mpType = 'app';
app.use(uviewPlus)
return {
app,
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,225 @@
<template>
<view class="home">
<view class=""
style=" margin-top: 200rpx; display: flex; flex-direction: column; justify-content: center; align-items: center;">
<image :src="Service.GetIconImg('/static/index/logo/logo.png')" style="width: 150rpx; height: 150rpx; "
mode=""></image>
<view class="" style="font-size: 36rpx; font-weight: 800; margin-top: 20rpx;">
菜农·商家端
</view>
</view>
<view class="" style="margin: 30rpx 30rpx; margin-top: 150rpx;">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
欢迎登陆
</view>
<view class="" style="font-size: 28rpx; margin-top: 20rpx; ">
手机号登录,安全又便捷
</view>
<view v-if="type" class="" >
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.phone" type="number" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入手机号"
clearable='true' border="surround"></up-input>
</view>
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.password" type="password" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入密码"
clearable='true' border="surround"></up-input>
</view>
<view class="" style="margin-top: 30rpx;" v-if="!isLogin">
<up-input v-model="qudPow" type="password" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="确认密码"
clearable='true' border="surround"></up-input>
</view>
<!-- <view class=""
style=" display: flex; align-items: center; justify-content: space-between; margin-top: 30rpx; border: 1rpx solid #dadbde; box-sizing: border-box; padding: 20rpx 30rpx; border-radius: 200rpx; ">
<up-code-input v-model="login.code" mode="line" size='24'></up-code-input>
<view class="wrap">
<up-toast ref="uToastRef"></up-toast>
<up-code :seconds="seconds" @end="end" @start="start" ref="uCodeRef"
@change="codeChange"></up-code>
<view @click="getCode">{{tips}}</view>
</view>
</view> -->
</view>
<!-- <view v-else class="">
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.phone" type="number" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入账号"
clearable='true' border="surround"></up-input>
</view>
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.phone" type="number" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入密码"
clearable='true' border="surround"></up-input>
</view>
</view> -->
<view class="" style="margin-top: 40rpx; width: 100%; height: 80rpx; line-height: 80rpx; border-radius: 40rpx; background-color: #FFDE1D; color: #fff; font-size: 30rpx; text-align: center;" @click="loginshop()">
登录
</view>
<view class="" style="width: 90%; margin: 0 auto; margin-top: 20rpx; display: flex; justify-content: space-between; color: #999; font-size: 28rpx;">
<view class="">
忘记密码
</view>
<view class="" @click="isLogin = !isLogin">
{{isLogin?'去注册':'已有账号去登录'}}
</view>
</view>
<!-- <view class="" @click="type=!type" style="text-align: center; color: #637aff; margin: 50rpx 0; ">
{{ !type?'使用验证码登录':'使用密码登录' }}
</view> -->
<view class="" style="display: flex; justify-content: center; align-items: center;color: black; margin-top: 50rpx; " @click="isuser= !isuser">
<view class="" style="margin-right: 10rpx;">
<up-icon :name="!isuser?'checkmark-circle':'checkmark-circle-fill'" :color="!isuser?'#999':'var(--nav-mian)'" size="20"></up-icon>
</view>
我同意
<text style="color: #FF6B23;">《用户协议》</text>
<text style="color: #FF6B23;">《隐私政策》</text>
</view>
<view class="" style="text-align: center; margin-top: 20rpx;">
<!-- 新用户?系统将自动为您注册 -->
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service ,CNRiderLoginService } from "@/Service/CN/CNRiderLoginService"
import { ref } from "vue";
let qudPow = ref<string>('')
let login = ref({
phone: '',
code: '',
password:''
})
const tips = ref('');
const seconds = ref(60);
const uCodeRef = ref(null);
let type = ref(true)
let isLogin = ref<boolean>(true)//登录注册状态
let isuser = ref<boolean>(false)//用户同意协议
onLoad(() => {
});
onShow(() => {
});
//登录
const loginshop = () =>{
if(login.value.phone==''){
return Service.Msg('请输入手机号/账号!')
}
if(login.value.password==''){
return Service.Msg('请输入密码!')
}
if(!isLogin.value && qudPow.value==''){
return Service.Msg('请确认密码!')
}
if(!isLogin.value && login.value.password!=qudPow.value){
return Service.Msg('两次密码不同!')
}
console.log(isuser.value)
if(!isuser.value){
return Service.Msg('请勾选同意用户协议!')
}
CNRiderLoginService.RiderLogin(login.value).then(res=>{
if(res.code==0){
Service.Msg('登录成功!')
Service.SetUserToken(res.data.accToken)
if(res.data.type==-1){
Service.GoPage('/pages/shop/Apply')
}else{
Service.GoPageTab('/pages/index/index')
}
}else{
Service.Msg(res.msg)
}
})
}
const codeChange = (text) => {
tips.value = text;
};
const getCode = () => {
if (uCodeRef.value.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码',
});
setTimeout(() => {
uni.hideLoading();
// 这里此提示会被start()方法中的提示覆盖
Service.Msg('验证码已发送')
// 通知验证码组件内部开始倒计时
uCodeRef.value.start();
}, 2000);
} else {
Service.Msg('倒计时结束后再发送')
}
};
const end = () => {
console.log('倒计时结束');
};
const start = () => {
console.log('倒计时开始');
};
</script>
<style lang="scss">
.home {
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom, #FFDE1D, #fff 40%);
overflow: hidden;
}
.chen{
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<view style="margin: 20rpx 100rpx;">
<up-form labelPosition="left" labelWidth='90' :model="password" ref="form1">
<up-form-item label="姓名" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input clearable='true' placeholder="请输入联系人姓名"
border="none"></up-input>
</up-form-item>
<up-form-item label="手机号" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input clearable='true' placeholder="请输入手机号"
border="none"></up-input>
</up-form-item>
</up-form>
<view class="" style="margin-top: 20rpx;">
<button @click="save()" :disabled='!password.password || !password.code ' class="logout-btn">确认</button>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { ref } from "vue";
import { Service } from "../../Service/Service";
let password = ref({
password: '',
code: ''
})
onLoad(() => {
});
onShow(() => {
});
const save = () => {
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.logout-btn {
background-color: var(--nav-banbacor);
color: #fff;
font-weight: 500;
border-radius: 10rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 30rpx;
margin: 0;
}
.logout-btn:active {
background-color: #f7f7f7;
color: #000;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

View File

@@ -0,0 +1,234 @@
<template>
<view v-if="isLoading" class="skeleton-container" style="padding: 10rpx 30rpx">
<!-- 骨架屏记录项 -->
<view class="skeleton-record-item" v-for="i in 3" :key="i">
<!-- 标题骨架 -->
<view class="skeleton-title"></view>
<!-- 金额区域骨架 -->
<view class="skeleton-amount-section">
<view class="skeleton-amount-line"></view>
<view class="skeleton-amount-value"></view>
</view>
<!-- 信息行骨架 -->
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
</view>
<!-- 加载更多骨架 -->
<view class="skeleton-loadmore"></view>
</view>
<view v-else style="padding: 10rpx 30rpx;">
<view class="" v-for="(item,index) in withdrowList" :key="index"
style="margin-top: 20rpx; gap: 20rpx; background-color: #fff; border-radius: 20rpx; padding: 30rpx; ">
<view class="" style="font-weight: bold; border-bottom: 1rpx solid #f6f6f6; padding-bottom: 15rpx;">
余额提现-{{ item.payway}}
</view>
<view class=""
style="width: 100%; height: 200rpx; display: flex;flex-direction: column; justify-content: center; align-items: center; ">
<view class="" style="">
提现金额 {{ item.amount}} 元
</view>
<view class="" style="font-size: 32rpx; font-weight: bold; margin-top: 10rpx; ">
实际到账 {{item.withAmount}} 元
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; ">
<view class="" style="width: 120rpx;">
收款人
</view>
<view class="" style="">
{{ item.name }}
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; margin-top: 10rpx; ">
<view class="" style="width: 120rpx;">
提现账号
</view>
<view class="" style="">
{{ item.account }}
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; margin-top: 10rpx;">
<view class="" style="width: 120rpx;">
提现时间
</view>
<view class="" style="">
{{ Service.formatDate(item.addTime,1) }}
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app";
import { Service } from '@/Service/Service';
import { ref } from "vue";
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let isLoading = ref(true)
let withdrowList = ref<Array<any>>([])
let status = ref('nomore')
let page = ref(1)
onLoad(() => {
getData()
});
onShow(() => {
});
onReachBottom(() => {
getList()
})
const getData = () => {
status.value = 'loadmore'
page.value = 1
withdrowList.value = []
getList()
}
//获取订单
const getList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderWithList(page.value).then(res => {
isLoading.value = false
if (res.data) {
withdrowList.value = [...withdrowList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
}
})
}
</script>
<style lang="scss">
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
/* 骨架屏样式 */
.skeleton-record-item {
margin-top: 20rpx;
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
gap: 20rpx;
}
.skeleton-title {
width: 200rpx;
height: 32rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-amount-section {
width: 100%;
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
}
.skeleton-amount-line {
width: 180rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-amount-value {
width: 250rpx;
height: 40rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-info-row {
display: flex;
align-items: center;
gap: 30rpx;
margin-top: 10rpx;
}
.skeleton-info-label {
width: 120rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-info-value {
width: 300rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-loadmore {
width: 100%;
height: 80rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-top: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
</style>

View File

@@ -0,0 +1,625 @@
<template>
<view v-if="isLoading" class="rider-home-skeleton">
<!-- 用户信息区域骨架 -->
<view class="skeleton-user-info">
<view class="skeleton-user-header">
<view class="skeleton-avatar"></view>
<view class="skeleton-user-details">
<view class="skeleton-user-name"></view>
<view class="skeleton-user-id"></view>
</view>
<view class="skeleton-setting-icon"></view>
</view>
</view>
<!-- 统计数据区域骨架 -->
<view class="skeleton-stats-section">
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value income"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value completed"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value ongoing"></view>
</view>
</view>
<!-- 接单按钮骨架 -->
<view class="skeleton-action-section">
<view class="skeleton-accept-orders-btn"></view>
</view>
<!-- 附近高价单骨架 -->
<view class="skeleton-high-price-orders">
<view class="skeleton-section-title"></view>
<!-- 循环生成多个订单项骨架 -->
<view class="skeleton-order-item" v-for="index in 3" :key="index">
<view class="skeleton-order-header">
<view class="skeleton-high-price-tag"></view>
<view class="skeleton-order-price"></view>
</view>
<view class="skeleton-order-content">
<view class="skeleton-restaurant-name"></view>
<view class="skeleton-pickup-time">
<view class="skeleton-clock-icon"></view>
<view class="skeleton-pickup-time-text"></view>
</view>
</view>
<view class="skeleton-distance"></view>
<view class="skeleton-address"></view>
<view class="skeleton-grab-order-btn"></view>
</view>
</view>
<!-- 加载更多骨架 -->
<view class="skeleton-loadmore"></view>
</view>
<!-- 实际内容 -->
<view v-else class="rider-home">
<!-- 用户信息区域 -->
<view class="user-info">
<view class="user-header">
<image :src="Service.GetMateUrlByImg('/static/dele/home/test.jpeg')" mode="aspectFit" class="avatar">
</image>
<view class="user-details">
<text class="user-name">大大怪将军</text>
<text class="user-id">ID: LN007 · 已下线</text>
</view>
<up-icon @click="Service.GoPage('/pages/my/edit')" name="setting" size="32rpx" color="#333333"></up-icon>
</view>
</view>
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥86.50</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">5单</text>
</view>
<view class="stat-item">
<text class="stat-label">进行中</text>
<text class="stat-value ongoing">6单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button type="primary" shape="circle" size="default" class="accept-orders-btn"
@click="toggleAcceptOrders">{{ userStatus === '已上线' ? '接单中 · 点击暂停' : '点击开始接单' }}</up-button>
</view>
<!-- 附近高价单 -->
<view class="high-price-orders">
<view class="section-title">附近高价单</view>
<view class="order-item" @click="Service.GoPage('/pages/order/grabOrder')" v-for="(order, index) in 3" :key="index">
<view class="order-header">
<view class="high-price-tag">高价单</view>
<text class="order-price">¥20</text>
</view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<text class="restaurant-name">王记炸酱面</text>
<view class="pickup-time">
<up-icon name="clock" size="24rpx" color="#FF9500"></up-icon>
<text class="pickup-time-text">12:30 前取餐</text>
</view>
</view>
<text class="distance">距您 0.8km</text>
<text class="address">朝阳区三里屯路123号</text>
<up-button shape="circle" type="primary" size="mini" class="grab-order-btn"
:style="{backgroundColor: '#1890FF'}">立即抢单</up-button>
</view>
</view>
<up-loadmore :status="status" />
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
// 加载状态
const isLoading = ref(true);
let userStatus = ref('已下线')
let status = ref('nomore')
onLoad(() => {
setTimeout(() => {
isLoading.value = false
}, 1500)
})
// 切换接单状态
const toggleAcceptOrders = () => {
userStatus.value = userStatus.value === '已上线' ? '已下线' : '已上线';
};
</script>
<style scoped lang="scss">
page{
background-color: #F6f6f6;
}
/* 骨架屏通用样式 */
.skeleton-user-info,
.skeleton-stats-section,
.skeleton-action-section,
.skeleton-high-price-orders,
.skeleton-order-item,
.skeleton-order-header,
.skeleton-order-content,
.skeleton-pickup-time {
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 用户信息区域骨架 */
.skeleton-user-info {
background-color: #E6F7FF;
padding: 40rpx 30rpx;
}
.skeleton-user-header {
display: flex;
align-items: center;
}
.skeleton-avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #d0d0d0;
border: 1px solid #fff;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-user-details {
margin-left: 20rpx;
flex: 1;
}
.skeleton-user-name {
width: 200rpx;
height: 36rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
margin-bottom: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-user-id {
width: 250rpx;
height: 28rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-setting-icon {
width: 32rpx;
height: 32rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 统计数据区域骨架 */
.skeleton-stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.skeleton-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.skeleton-stat-label {
width: 120rpx;
height: 28rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-stat-value {
width: 150rpx;
height: 36rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 接单按钮骨架 */
.skeleton-action-section {
margin: 0 20rpx;
}
.skeleton-accept-orders-btn {
width: 100%;
height: 90rpx;
background-color: #d0d0d0;
border-radius: 45rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 附近高价单骨架 */
.skeleton-high-price-orders {
margin-top: 20rpx;
padding: 0 20rpx;
}
.skeleton-section-title {
width: 200rpx;
height: 32rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-order-item {
background-color: #ffffff;
border-radius: 16rpx;
padding: 25rpx;
margin-bottom: 20rpx;
position: relative;
}
.skeleton-order-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15rpx;
}
.skeleton-high-price-tag {
width: 100rpx;
height: 32rpx;
background-color: #d0d0d0;
border-radius: 16rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-order-price {
width: 80rpx;
height: 32rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-order-content {
margin-bottom: 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.skeleton-restaurant-name {
width: 200rpx;
height: 30rpx;
background-color: #d0d0d0;
border-radius: 6rpx;
margin-bottom: 12rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-pickup-time {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.skeleton-clock-icon {
width: 24rpx;
height: 24rpx;
background-color: #d0d0d0;
border-radius: 4rpx;
margin-right: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-pickup-time-text {
width: 200rpx;
height: 26rpx;
background-color: #d0d0d0;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-distance {
width: 150rpx;
height: 26rpx;
background-color: #d0d0d0;
border-radius: 4rpx;
margin-bottom: 12rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-address {
width: 400rpx;
height: 26rpx;
background-color: #d0d0d0;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-grab-order-btn {
width: 160rpx;
height: 60rpx;
background-color: #d0d0d0;
border-radius: 30rpx;
position: absolute;
bottom: 25rpx;
right: 25rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 加载更多骨架 */
.skeleton-loadmore {
margin: 0 250rpx;
height: 40rpx;
background-color: #d0d0d0;
margin-bottom: 20rpx;
opacity: 0.6;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏加载动画 */
@keyframes skeleton-loading {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
/* 设置延迟,让骨架屏各部分加载动画错开 */
.skeleton-user-info {
animation-delay: 0s;
}
.skeleton-stats-section {
animation-delay: 0.1s;
}
.skeleton-action-section {
animation-delay: 0.2s;
}
.skeleton-high-price-orders {
animation-delay: 0.3s;
}
.skeleton-order-item:nth-child(2) {
animation-delay: 0.4s;
}
.skeleton-order-item:nth-child(3) {
animation-delay: 0.5s;
}
.skeleton-order-item:nth-child(4) {
animation-delay: 0.6s;
}
// 骨架屏end
.rider-home {
background-color: #f5f5f5;
}
/* 用户信息区域 */
.user-info {
background-color: #E6F7FF;
padding: 30rpx;
}
.user-header {
display: flex;
align-items: center;
}
.avatar {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
border: 1px solid #fff;
}
.user-details {
margin-left: 20rpx;
flex: 1;
}
.user-name {
font-size: 36rpx;
font-weight: 600;
color: #333333;
display: block;
margin-bottom: 8rpx;
}
.user-id {
font-size: 28rpx;
color: #666666;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 附近高价单 */
.high-price-orders {
margin-top: 20rpx;
padding: 0 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
margin-bottom: 20rpx;
}
.order-item {
background-color: #ffffff;
border-radius: 16rpx;
padding: 25rpx;
margin-bottom: 20rpx;
position: relative;
}
.order-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15rpx;
}
.high-price-tag {
background-color: #FFF1F0;
color: #FF4D4F;
font-size: 24rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
}
.order-price {
font-size: 32rpx;
font-weight: 600;
color: #FF4D4F;
}
.restaurant-name {
font-size: 30rpx;
color: #333333;
display: block;
margin-bottom: 12rpx;
}
.pickup-time {
display: flex;
align-items: center;
margin-bottom: 12rpx;
}
.pickup-time-text {
font-size: 26rpx;
color: #666666;
margin-left: 8rpx;
}
.distance {
font-size: 26rpx;
color: #999999;
display: block;
margin-bottom: 12rpx;
}
.address {
font-size: 26rpx;
color: #666666;
display: block;
margin-bottom: 20rpx;
line-height: 1.4;
}
.grab-order-btn {
width: 160rpx;
height: 60rpx;
font-size: 26rpx;
position: absolute;
bottom: 25rpx;
right: 25rpx;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

View File

@@ -0,0 +1,727 @@
/**
* 图片编辑器-手势监听
* 1. wxs 暂不支持 es6 语法
* 2. 支持编译到微信小程序、QQ小程序、app-vue、H5上uni-app 2.2.5及以上版本)
*/
/** 图片偏移量 */
var offset = { x: 0, y: 0 };
/** 图片缩放比例 */
var scale = 1;
/** 图片最小缩放比例 */
var minScale = 1;
/** 图片旋转角度 */
var rotate = 0;
/** 触摸点 */
var touches = [];
/** 图片布局信息 */
var img = {};
/** 系统信息 */
var sys = {};
/** 裁剪区域布局信息 */
var area = {};
/** 触摸行为类型 */
var touchType = '';
/** 操作角的位置 */
var activeAngle = 0;
/** 裁剪区域布局信息偏移量 */
var areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
/** 容错值 */
var fault = 0.000001;
/**
* 获取a、b两数中的最小正数
* @param a
* @param b
*/
function minimum(a, b) {
if (a > 0 && b < 0) return a;
if (a < 0 && b > 0) return b;
if (a > 0 && b > 0) return Math.min(a, b);
return 0;
}
/**
* 在容错访问内获取n近似值
* @param n
*/
function num(n) {
var m = parseFloat((n).toFixed(6));
return m === fault || m === -fault ? 0 : m;
}
/**
* 比较a值在容错值范围内是否等于b值
* @param a
* @param b
*/
function equalsByFault(a, b) {
return Math.abs(a - b) <= fault;
}
/**
* 比较a值在容错值范围内是否小于b值
* @param a
* @param b
*/
function lessThanByFault(a, b) {
var c = a - b;
return c < 0 ? c < -fault : c < fault;
}
/**
* 验证并获取有效最大值
* @param v
* @param max
* @param isInclude
* @param x
* @param y
* @param rate
* @returns
*/
function validMax(v, max, isInclude, x, y, rate) {
if(typeof max === 'number') {
if(isInclude && equalsByFault(max, y)) { // 宽高不等时x轴用y轴值要做等比例转换
var n = num(max * rate);
if (n <= x) return n; // 转化后值在x轴最大值范围内
return x; // 转化后值超出x轴最大值范围则用最大值
}
return max;
}
return v;
}
/**
* 计算两点间距
* @param {Object} touches 触摸点信息
*/
function getDistanceByTouches(touches) {
// 根据勾股定理求两点间距离
var a = touches[1].pageX - touches[0].pageX;
var b = touches[1].pageY - touches[0].pageY;
var c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
// 求两点间的中点坐标
// 1. a、b可能为负值
// 2. 在求a、b时如用touches[1]减touches[0]则求中点坐标也得用touches[1]减a/2、b/2
// 3. 同理在求a、b时也可用touches[0]减touches[1]则求中点坐标也得用touches[0]减a/2、b/2
var x = touches[1].pageX - a / 2;
var y = touches[1].pageY - b / 2;
return { c, x, y };
};
/**
* 修正取值
* @param {Object} a
* @param {Object} b
* @param {Object} c
* @param {Object} reverse 是否反向
*/
function correctValue(a, b, c, reverse) {
return num(reverse ? Math.max(Math.min(a, b), c) : Math.min(Math.max(a, b), c));
}
/**
* 旋转90°或270°时检查边界限制 x、y 拖动范围,禁止滑出边界
* @param {Object} e 点坐标
* @param {Object} xReverse x是否反向
* @param {Object} yReverse y是否反向
*/
function checkRotateRange(e, xReverse, yReverse) {
var o = num((img.height - img.width) / 2); // 宽高差值一半
return {
x: correctValue(e.x, -img.height + o + area.width + area.left, area.left + o, xReverse),
y: correctValue(e.y, -img.width - o + area.height + area.top, area.top - o, yReverse)
};
}
/**
* 检查边界:限制 x、y 拖动范围,禁止滑出边界
* @param {Object} e 点坐标
*/
function checkRange(e) {
var r = rotate / 90 % 2;
if(r === 1) { // 因图片宽高可能不等,翻转 90° 或 270° 后图片宽高需反着计算,且左右和上下边界要根据差值做偏移
if (area.width === area.height) {
return checkRotateRange(e, img.height < area.height, img.width < area.width);
}
var isInclude = img.height < area.width && img.width < area.height; // 图片是否包含在裁剪区域内
if (img.width < area.height || img.height < area.width) {
if (area.width < area.height && img.width < img.height) {
return isInclude
? checkRotateRange(e, area.width < area.height, area.width < area.height)
: checkRotateRange(e, false, true);
}
if (area.height < area.width && img.height < img.width) {
return isInclude
? checkRotateRange(e, area.height < area.width, area.height < area.width)
: checkRotateRange(e, true, false);
}
}
if (img.height >= area.width && img.width >= area.height) {
return checkRotateRange(e, false, false);
}
if (isInclude) {
return area.height < area.width
? checkRotateRange(e, true, true)
: checkRotateRange(e, area.width < area.height, area.width < area.height);
}
if (img.height < area.width && !img.width < area.height) {
return checkRotateRange(e, true, false);
}
if (!img.height < area.width && img.width < area.height) {
return checkRotateRange(e, false, true);
}
return checkRotateRange(e, img.height < area.height, img.width < area.width);
}
return {
x: correctValue(e.x, -img.width + area.width + area.left, area.left, img.width < area.width),
y: correctValue(e.y, -img.height + area.height + area.top, area.top, img.height < area.height)
};
};
/**
* 变更图片布局信息
* @param {Object} e 布局信息
*/
function changeImageRect(e) {
offset.x += e.x || 0;
offset.y += e.y || 0;
var image = e.instance.selectComponent('.crop-image');
if(e.check && area.checkRange) { // 检查边界
var point = checkRange(offset);
if(offset.x !== point.x || offset.y !== point.y) {
offset = point;
}
}
// image.setStyle({
// width: img.width + 'px',
// height: img.height + 'px',
// transform: 'translate(' + offset.x + 'px, ' + offset.y + 'px) rotate(' + rotate +'deg)'
// });
var ox = (img.width - img.oldWidth) / 2;
var oy = (img.height - img.oldHeight) / 2;
image.setStyle({
width: img.oldWidth + 'px',
height: img.oldHeight + 'px',
transform: (img.gpu ? 'translateZ(0) ' : '') + 'translate(' + (offset.x + ox) + 'px, ' + (offset.y + oy) + 'px) rotate(' + rotate +'deg) scale(' + scale + ')'
});
e.instance.callMethod('dataChange', {
width: img.width,
height: img.height,
x: offset.x,
y: offset.y,
rotate: rotate
});
};
/**
* 变更裁剪区域布局信息
* @param {Object} e 布局信息
*/
function changeAreaRect(e) {
// 变更蒙版样式
var masks = e.instance.selectAllComponents('.crop-mask-block');
var maskStyles = [
{
left: 0,
width: (area.left + areaOffset.left) + 'px',
top: 0,
bottom: 0,
'z-index': area.zIndex + 2
},
{
left: (area.right + areaOffset.right) + 'px',
right: 0,
top: 0,
bottom: 0,
'z-index': area.zIndex + 2
},
{
left: (area.left + areaOffset.left) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
top: 0,
height: (area.top + areaOffset.top) + 'px',
'z-index': area.zIndex + 2
},
{
left: (area.left + areaOffset.left) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
top: (area.bottom + areaOffset.bottom) + 'px',
// height: (area.top - areaOffset.bottom + sys.offsetBottom) + 'px',
bottom: 0,
'z-index': area.zIndex + 2
}
];
var len = masks.length;
for (var i = 0; i < len; i++) {
masks[i].setStyle(maskStyles[i]);
}
// 变更边框样式
if(area.showBorder) {
var border = e.instance.selectComponent('.crop-border');
border.setStyle({
left: (area.left + areaOffset.left) + 'px',
top: (area.top + areaOffset.top) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
'z-index': area.zIndex + 3
});
}
// 变更参考线样式
if(area.showGrid) {
var grids = e.instance.selectAllComponents('.crop-grid');
var gridStyles = [
{
'border-width': '1px 0 0 0',
left: (area.left + areaOffset.left) + 'px',
right: (area.right + areaOffset.right) + 'px',
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) / 3 - 0.5) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': '1px 0 0 0',
left: (area.left + areaOffset.left) + 'px',
right: (area.right + areaOffset.right) + 'px',
top: (area.top + areaOffset.top + (area.height + areaOffset.bottom - areaOffset.top) * 2 / 3 - 0.5) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': '0 1px 0 0',
top: (area.top + areaOffset.top) + 'px',
bottom: (area.bottom + areaOffset.bottom) + 'px',
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) / 3 - 0.5) + 'px',
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': '0 1px 0 0',
top: (area.top + areaOffset.top) + 'px',
bottom: (area.bottom + areaOffset.bottom) + 'px',
left: (area.left + areaOffset.left + (area.width + areaOffset.right - areaOffset.left) * 2 / 3 - 0.5) + 'px',
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
'z-index': area.zIndex + 3
}
];
var len = grids.length;
for (var i = 0; i < len; i++) {
grids[i].setStyle(gridStyles[i]);
}
}
// 变更四个伸缩角样式
if(area.showAngle) {
var angles = e.instance.selectAllComponents('.crop-angle');
var angleStyles = [
{
'border-width': area.angleBorderWidth + 'px 0 0 ' + area.angleBorderWidth + 'px',
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0 0',
left: (area.right + areaOffset.right - area.angleSize) + 'px',
top: (area.top + areaOffset.top - area.angleBorderWidth) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': '0 0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px',
left: (area.left + areaOffset.left - area.angleBorderWidth) + 'px',
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
'z-index': area.zIndex + 3
},
{
'border-width': '0 ' + area.angleBorderWidth + 'px ' + area.angleBorderWidth + 'px 0',
left: (area.right + areaOffset.right - area.angleSize) + 'px',
top: (area.bottom + areaOffset.bottom - area.angleSize) + 'px',
'z-index': area.zIndex + 3
}
];
var len = angles.length;
for (var i = 0; i < len; i++) {
angles[i].setStyle(angleStyles[i]);
}
}
// 变更圆角样式
if(area.radius > 0) {
var circleBox = e.instance.selectComponent('.crop-circle-box');
var circle = e.instance.selectComponent('.crop-circle');
var radius = area.radius;
if(area.width === area.height && area.radius >= area.width / 2) { // 圆形
radius = (area.width / 2);
} else { // 圆角矩形
if(area.width !== area.height) { // 限制圆角半径不能超过短边的一半
radius = Math.min(area.width / 2, area.height / 2, radius);
}
}
circleBox.setStyle({
left: (area.left + areaOffset.left) + 'px',
top: (area.top + areaOffset.top) + 'px',
width: (area.width + areaOffset.right - areaOffset.left) + 'px',
height: (area.height + areaOffset.bottom - areaOffset.top) + 'px',
'z-index': area.zIndex + 2
});
circle.setStyle({
'box-shadow': '0 0 0 ' + Math.max(area.width, area.height) + 'px rgba(51, 51, 51, 0.8)',
'border-radius': radius + 'px'
});
}
};
/**
* 缩放图片
* @param {Object} e 布局信息
*/
function scaleImage(e) {
var last = scale;
scale = Math.min(Math.max(e.scale + scale, minScale), img.maxScale);
if(last !== scale) {
img.width = num(img.oldWidth * scale);
img.height = num(img.oldHeight * scale);
// 参考问题有一个长4000px、宽4000px的四方形ABCDA点的坐标固定在(-2000,-2000)
// 该四边形上有一个点E坐标为(-100,-300)将该四方形复制一份并缩小到90%后,
// 新四边形的A点坐标为多少时可使新四边形的E点与原四边形的E点重合
// 预期效果:从图中选取某点(参照物)为中心点进行缩放,缩放时无论图像怎么变化,该点位置始终固定不变
// 计算方法:以相同起点先计算缩放前后两点间的距离,再加上原图像偏移量即可
e.x = num((e.x - offset.x) * (1 - scale / last));
e.y = num((e.y - offset.y) * (1 - scale / last));
changeImageRect(e);
return true;
}
return false;
};
/**
* 获取触摸点在哪个角
* @param {number} x 触摸点x轴坐标
* @param {number} y 触摸点y轴坐标
* @return {number} 角的位置0=无1=左上2=右上3=左下4=右下;
*/
function getToucheAngle(x, y) {
// console.log('getToucheAngle', x, y, JSON.stringify(area))
var o = area.angleBorderWidth; // 需扩大触发范围则把 o 值加大即可
if(y >= area.top - o && y <= area.top + area.angleSize + o) {
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
return 1; // 左上角
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
return 2; // 右上角
}
} else if(y >= area.bottom - area.angleSize - o && y <= area.bottom + o) {
if(x >= area.left - o && x <= area.left + area.angleSize + o) {
return 3; // 左下角
} else if(x >= area.right - area.angleSize - o && x <= area.right + o) {
return 4; // 右下角
}
}
return 0; // 无触摸到角
};
/**
* 重置数据
*/
function resetData() {
offset = { x: 0, y: 0 };
scale = 1;
minScale = img.minScale;
rotate = 0;
};
/**
* 顺时针翻转图片90°
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
function rotateImage(e, o, r) {
rotate = (rotate + r) % 360;
if(img.minScale >= 1 && area.checkRange) {
// 因图片宽高可能不等,翻转后图片宽高需足够填满裁剪区域
minScale = 1;
if(img.width < area.height) {
minScale = area.height / img.oldWidth;
} else if(img.height < area.width) {
minScale = area.width / img.oldHeight;
}
if(minScale !== 1) {
scaleImage({
instance: o,
scale: minScale - scale,
x: sys.windowWidth / 2,
y: (sys.windowHeight - sys.offsetBottom) / 2
});
}
}
// 由于拖动画布后会导致图片位置偏移,翻转时的旋转中心点需是图片区域+偏移区域的中心点
// 翻转x轴中心点 = (超出裁剪区域右侧的图片宽度 - 超出裁剪区域左侧的图片宽度) / 2
// 翻转y轴中心点 = (超出裁剪区域下方的图片宽度 - 超出裁剪区域上方的图片宽度) / 2
var ox = ((offset.x + img.width - area.right) - (area.left - offset.x)) / 2;
var oy = ((offset.y + img.height - area.bottom) - (area.top - offset.y)) / 2;
changeImageRect({
instance: o,
check: true,
x: -ox - oy,
y: -oy + ox
});
};
module.exports = {
/**
* 初始化:观察数据变更
* @param {Object} newVal 新数据
* @param {Object} oldVal 旧数据
* @param {Object} o 组件实例对象
*/
initObserver: function(newVal, oldVal, o, i) {
if(newVal) {
img = newVal.img;
sys = newVal.sys;
area = newVal.area;
minScale = img.minScale;
resetData();
img.src && changeImageRect({
instance: o,
x: (sys.windowWidth - img.width) / 2,
y: (sys.windowHeight - sys.offsetBottom - img.height) / 2
});
changeAreaRect({
instance: o
});
// console.log('initRect', JSON.stringify(newVal))
}
},
/**
* 鼠标滚轮滚动
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
mousewheel: function(e, o) {
if(!img.src) return;
scaleImage({
instance: o,
check: true,
// 鼠标向上滚动时deltaY 固定 -100鼠标向下滚动时deltaY 固定 100
scale: e.detail.deltaY > 0 ? -0.05 : 0.05,
x: e.touches[0].pageX,
y: e.touches[0].pageY
});
},
/**
* 触摸开始
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
touchstart: function(e, o) {
if(!img.src) return;
touches = e.touches;
activeAngle = area.showAngle ? getToucheAngle(touches[0].pageX, touches[0].pageY) : 0;
if(touches.length === 1 && activeAngle !== 0) {
touchType = 'stretch'; // 伸缩裁剪区域
} else {
touchType = '';
}
// console.log('touchstart', JSON.stringify(e), activeAngle)
},
/**
* 触摸移动
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
touchmove: function(e, o) {
if(!img.src) return;
// console.log('touchmove', JSON.stringify(e), JSON.stringify(o))
if(touchType === 'stretch') { // 触摸四个角进行拉伸
var point = e.touches[0];
var start = touches[0];
var x = point.pageX - start.pageX;
var y = point.pageY - start.pageY;
if(x !== 0 || y !== 0) {
var maxX = num(area.width * (1 - area.minScale));
var maxY = num(area.height * (1 - area.minScale));
// console.log(x, y, maxX, maxY, offset, area)
touches[0] = point;
var r = rotate / 90 % 2;
var m = r === 1 ? num((img.height - img.width) / 2) : 0; // 宽高差值一半
var xCompare = r === 1 ? lessThanByFault(img.height, area.width) : lessThanByFault(img.width, area.width);
var yCompare = r === 1 ? lessThanByFault(img.width, area.height) : lessThanByFault(img.height, area.height)
var isInclude = xCompare && yCompare;
var isIntersect = area.checkRange && (xCompare || yCompare); // 图片是否包含在裁剪区域内
var isReverse = !isInclude || num((offset.x - area.left) / area.width) <= num((offset.y - area.top) / area.height) || (area.width > area.height && img.width < img.height && r === 1);
switch(activeAngle) {
case 1: // 左上角
x = num(x + areaOffset.left);
y = num(y + areaOffset.top);
if(x >= 0 && y >= 0) { // 有效滑动
var t = num(offset.y + m - area.top);
var l = num(offset.x - m - area.left);
// && (offset.x + img.width < area.right || offset.y + img.height < area.bottom)
var max = isIntersect && ((l >= 0) || (t >= 0))
? minimum(t, l)
: false;
if(x > y && isReverse) { // 以x轴滑动距离为缩放基准
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
if(x > maxX) x = maxX;
y = num(x * area.height / area.width);
} else { // 以y轴滑动距离为缩放基准
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
if(y > maxY) y = maxY;
x = num(y * area.width / area.height);
}
areaOffset.left = x;
areaOffset.top = y;
}
break;
case 2: // 右上角
x = num(x + areaOffset.right);
y = num(y + areaOffset.top);
if(x <= 0 && y >= 0) { // 有效滑动
var w = (r === 1 ? img.height : img.width);
var t = num(offset.y + m - area.top);
var l = num(area.right + m - offset.x - w);
var max = isIntersect && ((t >= 0) || (l >= 0))
? minimum(t, l)
: false;
// var max = isInclude && ((offset.x > 0 && offset.x + img.width <= area.right) || (offset.y > 0 && offset.y >= area.top))
// ? minimum(offset.y - area.top, area.right - offset.x - img.width)
// : false;
// console.log(offset.x, offset.y, img.width, img.height, area.top, area.right, m, max)
// console.log(offset.y + m - area.top, area.right + m - offset.x - w)
if(-x > y && isReverse) { // 以x轴滑动距离为缩放基准
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
if(-x > maxX) x = -maxX;
y = num(-x * area.height / area.width);
} else { // 以y轴滑动距离为缩放基准
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
if(y > maxY) y = maxY;
x = num(-y * area.width / area.height);
}
areaOffset.right = x;
areaOffset.top = y;
}
break;
case 3: // 左下角
x += num(x + areaOffset.left);
y += num(y + areaOffset.bottom);
if(x >= 0 && y <= 0) { // 有效滑动
var w = (r === 1 ? img.width : img.height);
var t = num(area.bottom - m - offset.y - w);
var l = num(offset.x - m - area.left);
var max = isIntersect && ((l >= 0) || (t >= 0))
? minimum(t, l)
: false;
if(x > -y && isReverse) { // 以x轴滑动距离为缩放基准
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
if(x > maxX) x = maxX;
y = num(-x * area.height / area.width);
} else { // 以y轴滑动距离为缩放基准
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
if(-y > maxY) y = -maxY;
x = num(-y * area.width / area.height);
}
areaOffset.left = x;
areaOffset.bottom = y;
}
break;
case 4: // 右下角
x = num(x + areaOffset.right);
y = num(y + areaOffset.bottom);
if(x <= 0 && y <= 0) { // 有效滑动
var w = (r === 1 ? img.height : img.width);
var h = (r === 1 ? img.width : img.height);
var t = num(area.bottom - offset.y - h - m);
var l = num(area.right + m - offset.x - w);
var max = isIntersect && ((l >= 0) || (t >= 0))
? minimum(t, l)
: false;
if(-x > -y && isReverse) { // 以x轴滑动距离为缩放基准
maxX = validMax(maxX, max, isInclude, l, t, area.width / area.height);
if(-x > maxX) x = -maxX;
y = num(x * area.height / area.width);
} else { // 以y轴滑动距离为缩放基准
maxY = validMax(maxY, max, isInclude, t, l, area.height / area.width);
if(-y > maxY) y = -maxY;
x = num(y * area.width / area.height);
}
areaOffset.right = x;
areaOffset.bottom = y;
}
break;
}
// console.log(x, y, JSON.stringify(areaOffset))
changeAreaRect({
instance: o,
});
// this.draw();
}
} else if (e.touches.length == 2) { // 双点触摸缩放
var start = getDistanceByTouches(touches);
var end = getDistanceByTouches(e.touches);
scaleImage({
instance: o,
check: !area.bounce,
scale: (end.c - start.c) / 100,
x: end.x,
y: end.y
});
touchType = 'scale';
} else if(touchType === 'scale') {// 从双点触摸变成单点触摸 / 从缩放变成拖动
touchType = 'move';
} else {
changeImageRect({
instance: o,
check: !area.bounce,
x: e.touches[0].pageX - touches[0].pageX,
y: e.touches[0].pageY - touches[0].pageY
});
touchType = 'move';
}
touches = e.touches;
},
/**
* 触摸结束
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
touchend: function(e, o) {
if(!img.src) return;
if(touchType === 'stretch') { // 拉伸裁剪区域的四个角缩放
// 裁剪区域宽度被缩放到多少
var left = areaOffset.left;
var right = areaOffset.right;
var top = areaOffset.top;
var bottom = areaOffset.bottom;
var w = area.width + right - left;
var h = area.height + bottom - top;
// 图像放大倍数
var p = scale * (area.width / w) - scale;
// 复原裁剪区域
areaOffset = { left: 0, right: 0, top: 0, bottom: 0 };
changeAreaRect({
instance: o,
});
scaleImage({
instance: o,
scale: p,
x: area.left + left + (1 === activeAngle || 3 === activeAngle ? w : 0),
y: area.top + top + (1 === activeAngle || 2 === activeAngle ? h : 0)
});
} else if (area.bounce) { // 检查边界并矫正,实现拖动到边界时有回弹效果
changeImageRect({
instance: o,
check: true
});
}
},
/**
* 顺时针翻转图片90°
* @param {Object} e 事件对象
* @param {Object} o 组件实例对象
*/
rotateImage: function(e, o) {
rotateImage(e, o, 90);
},
rotateImage90: function(e, o) {
rotateImage(e, o, 90)
},
rotateImage270: function(e, o) {
rotateImage(e, o, 270)
},
// 此处只用于对齐其他平台端的样式参数,防止异常,无作用
imageStyles: '',
maskStylesList: ['', '', '', ''],
borderStyles: '',
gridStylesList: ['', '', '', ''],
angleStylesList: ['', '', '', ''],
circleBoxStyles: '',
circleStyles: '',
}

View File

@@ -0,0 +1,238 @@
<template>
<!-- <UpApp :show="upShow" :url="url" /> -->
<view class="borybac" v-if="upShow">
<view class="up_box">
<view class="mt50">
<view class="text">
版本更新
</view>
<view class="text">
{{remark}}
</view>
</view>
<view class="jdBox">
<view class="jd">
<view class="jdbfb">
{{sum}}%
</view>
<view class="jdt">
<view class="jdn" :style="'width:'+sum+'%'">
</view>
</view>
<view class="jddx">
{{datacl(beg)}}/{{datacl(downlog)}}
</view>
</view>
</view>
<view class="" v-if="force==0" style="width: 95%; height: 60rpx; margin: 40rpx auto; display: flex; justify-content: space-between;">
<view class="" style="width: 70rpx;" >
</view>
<view class="" style="width: 240rpx; height: 60rpx; line-height: 60rpx; border-radius: 30rpx; text-align: center; color: #fff; font-size: 24rpx; background-color: var(--nav-mian);" @click="delUpApp">
开始更新
</view>
<view class="" style="font-size: 22rpx; line-height: 80rpx; color: #999;" @click="goindex">
暂不更新
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
let url = ref('')
let force = ref('0')
// 控制热更新
let upShow = ref(true)
let sum = ref(0)
let downlog = ref(0)
let beg = ref(15642544)
let remark = ref('')
let type = ref('')
//模拟请求
onLoad((data:any) => {
// getdata()
url.value=data.url
});
const goindex = function () {
uni.navigateBack({
delta: 1
});
}
const getdata = function(){
// RegisterService.GetNewVersion().then((res:any)=>{
// url.value = res.data.path
// downlog.value = res.data.size
// force.value = res.data.force
// remark.value = res.data.remark
// type.value = res.data.type
// if(res.data.force=='1'){
// delUpApp()
// }
// })
}
const datacl = function(e:any){
if(e>1024){
let sl = ((e/1024)/1024).toFixed(1)
return sl+'MB'
}else{
return (e/1024).toFixed(1)+'KB'
}
}
const delUpApp = function () {
// 1.开始请求下载
const downloadTask = uni.downloadFile({
url: url.value,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
plus.runtime.install(downloadResult.tempFilePath, {
force: false
}, function() {
uni.hideLoading()
uni.showToast({
title: "下载成功",
complete() {
if(type.value=='Bulking'){
setTimeout(function() {
plus.runtime.restart();
}, 2000)
}
}
})
console.log('install success...');
}, function(e) {
uni.hideLoading()
console.log(e,'失败')
// uni.$u.toast('下载失败!')
// console.error('install fail...');
});
}
},
fail(downloadResult) {
console.log(downloadResult,'失败')
// console.log('下载失败');
// uni.$u.toast('下载失败')
}
});
downloadTask.onProgressUpdate((res) => {
downlog.value = res.totalBytesExpectedToWrite
beg.value = res.totalBytesWritten
sum.value =res.progress
});
}
</script>
<style lang="scss" setup>
.borybac {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
overflow: hidden;
.up_box {
width: 513rpx;
height: 680rpx;
margin: 300rpx auto;
border-radius: 20rpx;
overflow: hidden;
position: relative;
background-image: url(@/static/index/system/upapphed.png);
background-repeat: no-repeat;
background-size: cover;
.mt50 {
display: block;
margin-top: 200rpx;
}
.jdBox {
overflow: hidden;
margin-top: 120rpx;
display: block;
width: 100%;
.jd {
display: block;
width: 90%;
height: 100%;
margin: 0 auto;
.jdbfb {
display: block;
width: 100%;
height: 40rpx;
line-height: 40rpx;
font-size: 30rpx;
color: var(--nav-mian);
font-weight: 600;
}
.jdt {
margin-top: 10rpx;
display: block;
width: 100%;
height: 23rpx;
border-radius: 15rpx;
background-color: #E5E5E5;
.jdn {
display: block;
height: 23rpx;
border-radius: 15rpx;
background: radial-gradient(#8370F8 0%, #455FF8 100%);
}
}
.jddx {
width: 100%;
font-size: 24rpx;
margin-top: 20rpx;
font-weight: 600;
}
}
}
}
}
.text {
display: block;
text-align: center;
margin-top: 30rpx;
width: 100%;
height: 40rpx;
font-size: 32rpx;
}
</style>

View File

@@ -0,0 +1,241 @@
{
"easycom": {
// 注意一定要放在custom里否则无效https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "门店运营",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/task",
"style": {
"navigationBarTitleText": "任务",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/income",
"style": {
"navigationBarTitleText": "我的收入",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/user",
"style": {
"navigationBarTitleText": "我的",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "v派商家",
"navigationBarBackgroundColor": "#fff",
"backgroundColor": "#000"
},
"subPackages": [{
"root": "pages/order",
"pages": [{
"path": "orderDetail",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "navigation",
"style": {
"navigationBarTitleText": "导航",
"navigationStyle": "custom"
}
},
{
"path": "abnormal",
"style": {
"navigationBarTitleText": "上报异常"
}
},
{
"path": "grabOrder",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "incomeDetail",
"style": {
"navigationBarTitleText": "收入详情",
"navigationStyle": "custom"
}
},
{
"path": "withdraw",
"style": {
"navigationBarTitleText": "提现申请"
}
},
{
"path": "finish",
"style": {
"navigationBarTitleText": "订单完成"
}
},
{
"path": "upAbnormal",
"style": {
"navigationBarTitleText": "异常上报"
}
},
{
"path": "orderMap",
"style": {
"navigationBarTitleText": "订单地图"
}
},
{
"path": "orderChat",
"style": {
"navigationBarTitleText": "联系客户"
}
}
]
},
{
"root": "pages/my",
"pages": [{
"path": "edit",
"style": {
"navigationBarTitleText": "编辑资料"
}
},
{
"path": "statusContro",
"style": {
"navigationBarTitleText": "上线管理"
}
},
{
"path": "myKF",
"style": {
"navigationBarTitleText": "联系客服"
}
},
{
"path": "AbnormalList",
"style": {
"navigationBarTitleText": "异常订单"
}
},
{
"path": "check",
"style": {
"navigationBarTitleText": "签到奖励"
}
},
{
"path": "abnormalDetail",
"style": {
"navigationBarTitleText": "异常详情"
}
},
{
"path": "security",
"style": {
"navigationBarTitleText": "账号与安全"
}
},
{
"path": "authentication",
"style": {
"navigationBarTitleText": "实名认证"
}
},
{
"path": "login",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "noticeList",
"style": {
"navigationBarTitleText": "消息通知",
"navigationStyle": "custom"
}
},
{
"path": "setConnect",
"style": {
"navigationBarTitleText": "紧急联系人"
}
},
{
"path": "evaluate",
"style": {
"navigationBarTitleText": "评价中心"
}
},
{
"path": "completeData",
"style": {
"navigationBarTitleText": "完善信息"
}
},
{
"path": "withDrowList",
"style": {
"navigationBarTitleText": "提现列表"
}
}
]
}
],
"tabBar": {
"color": "#000",
"selectedColor": "#000",
"backgroundColor": "#FFFFFF",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tab/home.png",
"selectedIconPath": "/static/tab/homed.png",
"text": "主页"
},
{
"pagePath": "pages/index/income",
"iconPath": "static/tab/income.png",
"selectedIconPath": "static/tab/incomed.png",
"text": "收入"
},
{
"pagePath": "pages/index/user",
"iconPath": "static/tab/user.png",
"selectedIconPath": "static/tab/usered.png",
"text": "我的"
}
]
}
}

View File

@@ -0,0 +1,43 @@
import { Service } from '@/Service/Service';
/*****代理端接口*****/
class NvpAgentService {
private static LoginPath: string = '/Agent/Login';
/*****登录接口*****/
static Login(name: string, pwd: string) {
var result = Service.Request(this.LoginPath, "POST", { name, pwd });
return result;
}
private static GetAgentAccInfoPath: string = '/Agent/GetAgentAccInfo';
/*****账户接口*****/
static GetAgentAccInfo() {
var result = Service.Request(this.GetAgentAccInfoPath, "GET", "");
return result;
}
private static GetAgentAccLogPath: string = '/Agent/GetAgentAccLog';
/*****账户记录*****/
static GetAgentAccLog(code: string, page: number) {
var result = Service.Request(this.GetAgentAccLogPath, "GET", { code, page });
return result;
}
private static GetAgentMerchPath: string = '/Agent/GetAgentMerch';
/*****获取代理开通商家*****/
static GetAgentMerch(type: number, page: number) {
var result = Service.Request(this.GetAgentMerchPath, "GET", { type, page });
return result;
}
private static GetAgentHomePath: string = '/Agent/GetAgentHome';
/*****代理主页*****/
static GetAgentHome() {
var result = Service.Request(this.GetAgentHomePath, "GET", { });
return result;
}
}
export {
Service,
NvpAgentService
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,30 @@
## 1.4.122024-09-21
- 修复 calendar在选择日期范围后重新选择日期需要点两次的Bug
## 1.4.112024-01-10
- 修复 回到今天时,月份显示不一致问题
## 1.4.102023-04-10
- 修复 某些情况 monthSwitch 未触发的Bug
## 1.4.92023-02-02
- 修复 某些情况切换月份错误的Bug
## 1.4.82023-01-30
- 修复 某些情况切换月份错误的Bug [详情](https://ask.dcloud.net.cn/question/161964)
## 1.4.72022-09-16
- 优化 支持使用 uni-scss 控制主题色
## 1.4.62022-09-08
- 修复 表头年月切换导致改变当前日期为选择月1号且未触发change事件的Bug
## 1.4.52022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.42022-02-25
- 修复 条件编译 nvue 不支持的 css 样式的Bug
## 1.4.32021-09-22
- 修复 startDate、 endDate 属性失效的Bug
## 1.4.22021-08-24
- 新增 支持国际化
## 1.4.12021-08-05
- 修复 弹出层被 tabbar 遮盖的Bug
## 1.4.02021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.3.162021-05-12
- 新增 组件示例地址
## 1.3.152021-02-04
- 调整为uni_modules目录规范

View File

@@ -0,0 +1,714 @@
<template>
<!-- 导航栏 -->
<view class=""
style="padding:50rpx 20rpx 18rpx; box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: #fff; display: flex; align-items: center; justify-content: space-between;">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" size="32rpx"></up-icon>
</view>
<view class="">
收入详情
</view>
<view class="" style="color: var(--nav-banbacor);">
<image :src="Service.GetIconImg('/static/index/order/message.png')" style="width: 32rpx; height: 32rpx; "
mode=""></image>
</view>
</view>
<view class="" style="width: 100%; height: 100rpx; ">
</view>
<view v-if="loading" class="skeleton-container">
<!-- 标题骨架屏 -->
<view class="skeleton-section-title"></view>
<!-- 收入卡片骨架屏 -->
<view class="skeleton-card">
<view class="skeleton-row">
<view class="skeleton-order-id"></view>
<view class="skeleton-status"></view>
</view>
<view class="skeleton-row" v-for="item in 3" >
<view class="skeleton-order-id" style="width: 30%;" ></view>
<view class="skeleton-status"></view>
</view>
<view class="skeleton-total-row">
<view class="skeleton-total-label"></view>
<view class="skeleton-total-amount"></view>
</view>
</view>
<!-- 配送信息标题骨架屏 -->
<view class="skeleton-section-title"></view>
<!-- 订单信息卡片骨架屏 -->
<view class="skeleton-card">
<!-- 商家信息骨架屏 -->
<view class="skeleton-address-item">
<view class="skeleton-address-content">
<view class="skeleton-address-text">
<view class="skeleton-name"></view>
<view class="skeleton-address-line"></view>
</view>
<view class="skeleton-call-btn"></view>
</view>
</view>
<!-- 用户信息骨架屏 -->
<view class="skeleton-address-item">
<view class="skeleton-address-content">
<view class="skeleton-address-text">
<view class="skeleton-name"></view>
<view class="skeleton-address-line"></view>
</view>
<view class="skeleton-call-btn"></view>
</view>
</view>
<!-- 时间信息骨架屏 -->
<view class="skeleton-time"></view>
</view>
<!-- 底部提示骨架屏 -->
<view class="skeleton-bottom-tip"></view>
</view>
<!-- 实际内容 -->
<view class="order-detail-container">
<view class="section-title" style="margin: 20rpx 0;" >订单信息</view>
<!-- 订单收入信息卡片 -->
<view class="income-card">
<view class="income-header">
<text class="order-id">订单 MT20251017123456</text>
<text class="status">已到账</text>
</view>
<view class="income-details">
<view class="income-item">
<text class="income-type">配送费</text>
<text class="income-amount blue">+¥5.50</text>
</view>
<view class="income-item">
<text class="income-type">冲单奖</text>
<text class="income-amount orange">+¥3.00</text>
</view>
<view class="income-item">
<text class="income-type">恶劣天气补贴</text>
<text class="income-amount green">+¥2.00</text>
</view>
</view>
<view class="total-income">
<text class="total-label">总计收入</text>
<text class="total-amount">¥10.50</text>
</view>
</view>
<!-- 订单信息卡片 -->
<view class="section-title">配送信息</view>
<view class="info-card">
<!-- 商家信息 -->
<view class="merchant-info">
<view class="merchant-detail">
<view class="merchant-text">
<text class="merchant-name">张亮麻辣烫 (五道口店)</text>
<text class="merchant-address">成府路28号</text>
</view>
</view>
<view class="call-btn" @click="callMerchant">
<up-icon name="phone" color="var(--nav-mian)" size="28rpx"></up-icon>
<text class="call-text">拨打商家</text>
</view>
</view>
<!-- 用户信息 -->
<view class="user-info">
<view class="user-detail">
<view class="user-text">
<text class="user-name">张*</text>
<text class="user-address">XX小区3栋502</text>
</view>
</view>
<view class="call-btn" @click="callUser">
<up-icon name="phone" color="var(--nav-mian)" size="28rpx"></up-icon>
<text class="call-text">拨打用户</text>
</view>
</view>
<!-- 订单时间信息 -->
<view class="order-time-info">
<text class="time-text">2025-10-17 18:30 下单 · 19:15 送达</text>
</view>
</view>
<!-- 底部提示 -->
<view class="bottom-tip">
<text class="tip-text">收入明细如有疑问,请联系客服</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { Service } from '@/Service/Service'
// 加载状态
const loading = ref(true)
// 拨打商家电话
const callMerchant = () => {
uni.makePhoneCall({
phoneNumber: '13800138000'
})
}
// 拨打用户电话
const callUser = () => {
uni.makePhoneCall({
phoneNumber: '13900139000'
})
}
onMounted(() => {
// 模拟数据加载
setTimeout(() => {
loading.value = false
}, 1500)
})
</script>
<style scoped>
/* 实际内容样式 */
.order-detail-container {
background-color: #f5f5f5;
padding: 0 20rpx ;
}
/* 订单收入信息卡片 */
.income-card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.income-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.order-id {
font-size: 32rpx;
color: #333;
}
.status {
font-size: 28rpx;
color: #52c41a;
}
.income-details {
margin-bottom: 20rpx;
}
.income-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.income-type {
font-size: 28rpx;
color: #666;
}
.income-amount {
font-size: 32rpx;
font-weight: 600;
}
.income-amount.blue {
color: #1890ff;
}
.income-amount.orange {
color: #fa8c16;
}
.income-amount.green {
color: #52c41a;
}
.total-income {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.total-label {
font-size: 30rpx;
color: #333;
font-weight: 600;
}
.total-amount {
font-size: 36rpx;
color: #ff4d4f;
font-weight: 700;
}
/* 订单信息卡片 */
.info-card {
margin-top: 20rpx;
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 34rpx;
color: #333;
font-weight: 600;
margin-bottom: 30rpx;
}
/* 商家信息 */
.merchant-info {
display: flex;
align-items: baseline;
margin-bottom: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.merchant-detail {
flex: 1;
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.merchant-text {
flex: 1;
}
.merchant-name {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 10rpx;
}
.merchant-address {
display: block;
font-size: 26rpx;
color: #666;
}
/* 用户信息 */
.user-info {
margin-bottom: 20rpx;
display: flex;
align-items: baseline;
}
.user-detail {
flex: 1;
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.user-text {
flex: 1;
}
.user-name {
display: block;
font-size: 30rpx;
color: #333;
margin-bottom: 10rpx;
}
.user-address {
display: block;
font-size: 26rpx;
color: #666;
}
/* 拨打电话按钮 */
.call-btn {
display: flex;
align-items: center;
padding: 10rpx 20rpx;
border-radius: 20rpx;
}
.call-text {
font-size: 24rpx;
color: #1890ff;
margin-left: 6rpx;
}
/* 订单时间信息 */
.time-text {
font-size: 26rpx;
color: #999;
}
/* 底部提示 */
.bottom-tip {
text-align: center;
margin-top: 30rpx;
}
.tip-text {
font-size: 24rpx;
color: #999;
}
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin-bottom: 25rpx;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 15rpx;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #faad14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #f5f5f5;
padding: 0 20rpx 60rpx;
}
/* 标题骨架屏 */
.skeleton-section-title {
width: 160rpx;
height: 34rpx;
background-color: #e6e6e6;
margin: 30rpx 0;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 卡片骨架屏 */
.skeleton-card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 收入卡片骨架屏 */
.skeleton-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.skeleton-order-id {
width: 60%;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-status {
width: 15%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-line {
width: 100%;
height: 28rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-total-row {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20rpx;
border-top: 1rpx solid #f0f0f0;
}
.skeleton-total-label {
width: 40%;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-total-amount {
width: 25%;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 地址信息骨架屏 */
.skeleton-address-item {
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.skeleton-address-item:last-child {
margin-bottom: 20rpx;
padding-bottom: 0;
border-bottom: none;
}
.skeleton-address-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-address-text {
flex: 1;
}
.skeleton-name {
width: 60%;
height: 30rpx;
background-color: #e6e6e6;
margin-bottom: 10rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-address-line {
width: 80%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-call-btn {
width: 150rpx;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 25rpx;
animation: shimmer 1.5s infinite;
}
/* 时间信息骨架屏 */
.skeleton-time {
width: 70%;
height: 26rpx;
background-color: #e6e6e6;
margin-top: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 底部提示骨架屏 */
.skeleton-bottom-tip {
margin: 0 100rpx;
height: 24rpx;
background-color: #e6e6e6;
margin-top: 30rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
opacity: 0.6;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
/* 为骨架屏元素添加渐变背景 */
.skeleton-section-title,
.skeleton-order-id,
.skeleton-status,
.skeleton-line,
.skeleton-total-label,
.skeleton-total-amount,
.skeleton-name,
.skeleton-address-line,
.skeleton-call-btn,
.skeleton-time,
.skeleton-bottom-tip {
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

View File

@@ -0,0 +1,662 @@
<template>
<!-- 导航栏 -->
<view class=""
style="padding:50rpx 20rpx 18rpx; box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: #fff; display: flex; align-items: center; justify-content: space-between;">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" size="32rpx"></up-icon>
</view>
<view class="">
订单详情
</view>
<view class="" @click="Service.GoPage('/pages/my/myKF')" style="color: var(--nav-banbacor);">
<image :src="Service.GetIconImg('/static/index/order/message.png')" style="width: 32rpx; height: 32rpx; "
mode=""></image>
</view>
</view>
<view class="" style="width: 100%; height: 88rpx; ">
</view>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 骨架屏订单状态 -->
<view class="skeleton-status"></view>
<!-- 骨架屏订单基本信息 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="skeleton-info-half"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-row">
<view class="skeleton-info-third"></view>
<view class="skeleton-info-btn"></view>
</view>
</view>
<!-- 骨架屏物品清单 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;" >
<view class="" style="background-color: #e6e6e6; width: 60%;height: 40rpx; " >
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-row" v-for="(item,index) in 3" :key="index" >
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;" >
<view class="" style="background-color: #e6e6e6; width: 90%;height: 40rpx; " >
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-list-status"></view>
</view>
<!-- 骨架屏地图区域 -->
<view class="skeleton-map"></view>
<!-- 骨架屏取餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-code"></view>
</view>
<!-- 骨架屏送餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-remark"></view>
</view>
<!-- 骨架屏底部按钮 -->
<view class="skeleton-bottom">
<view class="skeleton-bottom-btn"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单状态 -->
<view class="order-status"
:style="{ 'background-color':orderStatus==0?'#E6F7FF':(orderStatus==1?'#FFFBE6':'#FFF2F0') }">
<text :style="{ 'color':orderStatus==0?'#1890FF':(orderStatus==1?'#FAAD14':'#FF4D4F') }"
style="font-size: 34rpx; font-weight: 600;">待取餐 · 请尽快到店取餐</text>
</view>
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="info-item">
<view class="label">剩余</view>
<view class="value highlight">10分钟</view>
<view class="label">(19:30前送达)</view>
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥5.50</text>
</view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">20251021123456</text>
</view>
<view class="info-item" style=" justify-content: space-between;">
<view class="">
<text class="label">物品明细 : </text>
<text class="value">食物</text>
</view>
<view class="label">共3件商品</view>
</view>
<view class="info-item">
<text class="label">物品重量 : </text>
<text class="value">0.5kg</text>
</view>
<view class="info-item" style="justify-content: space-between; ">
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="24rpx" class="clock-icon"></u-icon>
2025-10-17 18:30下单
</view>
<view class="info-item">
<up-button @click.stop="Service.GoPage('/pages/order/abnormal')" type="warning"
color='var(--nav-diluted)' shape='circle' size="mini">提交异常</up-button>
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
5件
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
¥16
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'190rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in 5 " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
康师傅 大食桶红烧牛肉143g/桶 经典红烧酱香免洗桶装速食泡面
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×4
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥16
</view>
</view>
</view>
<view class="" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 地图区域 -->
<view class="map-section">
<view class="map-placeholder">
<text @click="Service.GoPage('/pages/order/navigation')" class="map-hint">点击查看完整导航</text>
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<text class="section-title">取餐地址</text>
<view class="address-content">
<text class="store-name">张亮麻辣烫(五道口店)</text>
<text class="address">北京市海淀区成府路28号</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button icon="phone" type="primary" shape="circle" text="拨打商家"></up-button>
</view>
<view class="pickup-code">
<text class="code-label">取餐号:</text>
<text class="code-value">A123</text>
</view>
</view>
</view>
<!-- 送餐地址 -->
<view class="address-section">
<text class="section-title">送餐地址</text>
<view class="address-content">
<text class="user-name">张*</text>
<text class="address">XX小区3栋502室</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button icon="phone" type="primary" shape="circle" text="拨打商家"></up-button>
</view>
<view class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">请放门口,勿按门铃</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action">
<up-button color="var(--nav-vice)" class="confirm-btn">我已取餐</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
onLoad(() => {
setTimeout(() => {
loading.value = false
}, 1000)
})
</script>
<style scoped>
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 140rpx;
}
/* 骨架屏导航栏 */
.skeleton-nav {
height: 88rpx;
position: fixed;
top: 0;
left: 0;
width: 100vw;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
z-index: 100;
}
.skeleton-nav-item {
width: 32rpx;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-nav-title {
width: 180rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单状态 */
.skeleton-status {
height: 100rpx;
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-status::after {
content: '';
width: 350rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单基本信息 */
.skeleton-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-row {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
}
.skeleton-info-half {
width: 45%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-right {
width: 10%;
}
.skeleton-line {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-third {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-btn {
width: 20%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 25rpx;
animation: shimmer 1.5s infinite;
}
/* 列表状态 */
.skeleton-list-status {
height: 60rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-list-status::after {
content: '';
width: 200rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏地图区域 */
.skeleton-map {
margin: 20rpx;
height: 400rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: shimmer 1.5s infinite;
position: relative;
overflow: hidden;
}
.skeleton-map::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, transparent 25%, rgba(255, 255, 255, 0.2) 25%, rgba(255, 255, 255, 0.2) 50%, transparent 50%, transparent 75%, rgba(255, 255, 255, 0.2) 75%, rgba(255, 255, 255, 0.2));
background-size: 100rpx 100rpx;
animation: shimmer 1.5s infinite linear;
}
/* 骨架屏地址区域 */
.skeleton-address {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-title {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 25rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-store-name {
width: 70%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-address-line {
width: 90%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-btn {
height: 70rpx;
background-color: #e6e6e6;
border-radius: 35rpx;
margin: 20rpx 0;
animation: shimmer 1.5s infinite;
}
.skeleton-code {
width: 50%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-remark {
width: 80%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏底部按钮 */
.skeleton-bottom {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.skeleton-bottom-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
opacity: 0.6;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.6;
}
}
/* 骨架屏滑动动画 */
@keyframes shimmer-slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
/* end */
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin-bottom: 25rpx;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 15rpx;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
</style>

View File

@@ -0,0 +1,145 @@
{
"name" : "骑手端",
"appid" : "__UNI__06C2D6A",
"description" : "",
"versionName" : "1.0.8",
"versionCode" : 108,
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"compatible" : {
"ignoreVersion" : true //true表示忽略版本检查提示框HBuilderX1.9.0及以上版本支持
},
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {
"Barcode" : {},
"Maps" : {},
"Geolocation" : {},
"Camera" : {},
"Contacts" : {}
},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion" : 25,
"targetSdkVersion" : 25,
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
},
/* ios打包配置 */
"ios" : {
"idfa" : false,
"dSYMs" : false
},
/* SDK配置 */
"sdkConfigs" : {
"ad" : {},
"maps" : {
"amap" : {
"name" : "amapZAvZjTHj",
"appkey_ios" : "3caf9e6f01b0085be1e75e0d0e281fe7",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
},
"geolocation" : {
"amap" : {
"name" : "amapZAvZjTHj",
"__platform__" : [ "android" ],
"appkey_ios" : "",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wx6ef5a6a74620a3e8",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "7DIBZ-K4HCJ-ZR2FE-FOOOP-SALFT-RLFYW"
}
}
}
}
}

View File

@@ -0,0 +1,156 @@
import { Service } from '@/Service/Service';
/*****用户接口*****/
class NvpApplyService {
// private static WithDrawPath: string = '/With/WithDraw';
// /*****佣金提现*****/
// static WithDraw(money: number, name: string, account: string) {
// var result = Service.Request(this.WithDrawPath, "POST", { money, name, account });
// return result;
// }
private static GetSiteMccCodeListPath: string = '/Apply/GetSiteMccCodeList';
/*****获取mcc列表*****/
static GetSiteMccCodeList(mercType:string, mchType: string) {
var result = Service.Request(this.GetSiteMccCodeListPath, "GET", {mercType, mchType });
return result;
}
private static GetBankTypeListPath: string = '/Apply/GetBankTypeList';
/*****获取银行列表*****/
static GetBankTypeList(name:string) {
var result = Service.Request(this.GetBankTypeListPath, "GET", {name});
return result;
}
private static GetAreaListPath: string = '/Apply/GetAreaList';
/*****获取银行地区列表*****/
static GetAreaList(areaCode:string) {
var result = Service.Request(this.GetAreaListPath, "GET", { areaCode});
return result;
}
private static GetBankCodeListPath: string = '/Apply/GetBankCodeList';
/*****获取银行代码列表*****/
static GetBankCodeList(bankType:string,cityCode:string,name:string) {
var result = Service.Request(this.GetBankCodeListPath, "GET", {bankType,cityCode,name});
return result;
}
private static SendApplyMerchPath: string = '/Apply/SendApplyMerch';
/*****进价提交*****/
static SendApplyMerch(para:any) {
var result = Service.Request(this.SendApplyMerchPath, "POST", para);
return result;
}
private static GetAgentMerchLogPath: string = '/Apply/GetAgentMerchLog';
/*****获取待审核*****/
static GetAgentMerchLog() {
var result = Service.Request(this.GetAgentMerchLogPath, "GET", {});
return result;
}
private static AuditApplyPath: string = '/Apply/AuditApply';
/*****确认资料*****/
static AuditApply(outId:string) {
var result = Service.Request(this.AuditApplyPath, "POST", {outId});
return result;
}
private static SetPayFeePath: string = '/Apply/SetPayFee';
/*****确认资料*****/
static SetPayFee(outId:string) {
var result = Service.Request(this.SetPayFeePath, "POST", {outId});
return result;
}
private static GetAssortListPath: string = '/Apply/GetAssortList';
/*****获取v派分类*****/
static GetAssortList(code:string) {
var result = Service.Request(this.GetAssortListPath, "GET", {code,parent:'0'});
return result;
}
private static GetAgentMerchPath: string = '/Agent/GetAgentMerch';
/*****获取已开通商家*****/
static GetAgentMerch(type:number,page:number) {
var result = Service.Request(this.GetAgentMerchPath, "GET", {type,page});
return result;
}
private static GetAppMerchInfoPath: string = '/Apply/GetAppMerchInfo';
/*****获取银盛已填写信息*****/
static GetAppMerchInfo(outId:string) {
var result = Service.Request(this.GetAppMerchInfoPath, "GET", {outId});
return result;
}
private static UpdateMerchPath: string = '/Apply/UpdateMerch';
/*****修改银盛信息*****/
static UpdateMerch(obj:any) {
var result = Service.Request(this.UpdateMerchPath, "POST", obj);
return result;
}
private static UploadImgPath: string = '/Apply/UploadImg';
/*****修改银盛图片*****/
static UploadImg(outId:string,picType:string,img:string) {
var result = Service.Request(this.UploadImgPath, "POST", {outId,picType,img});
return result;
}
private static GetAgentApplyPath: string = '/Apply/GetAgentApply';
/*****获取添加的商家*****/
static GetAgentApply(serch:string,page:number) {
var result = Service.Request(this.GetAgentApplyPath, "GET", {serch,page});
return result;
}
private static GetCategoryPath: string = '/Apply/GetCategory';
/*****获取商家类型*****/
static GetCategory() {
var result = Service.Request(this.GetCategoryPath, "GET", {});
return result;
}
private static AddMerchInfoPath: string = '/Apply/AddMerchInfo';
/*****添加商户*****/
static AddMerchInfo(obj:any) {
var result = Service.Request(this.AddMerchInfoPath, "POST", obj);
return result;
}
private static BandAppIdPath: string = '/Agent/BandAppId';
/*****绑定appid*****/
static BandAppId(merchId:string) {
var result = Service.Request(this.BandAppIdPath, "POST", {merchId});
return result;
}
}
export {
Service,
NvpApplyService
}

View File

@@ -0,0 +1,315 @@
<template>
<view v-if="loading" class="skeleton-container">
<!-- 成功提示区域骨架 -->
<view class="success-section skeleton-success-section">
<view class="skeleton-success-icon"></view>
<view class="skeleton-line skeleton-success-title"></view>
<view class="skeleton-line skeleton-success-desc"></view>
</view>
<!-- 订单信息卡片骨架 -->
<view class="order-card skeleton-order-card">
<view class="order-item" v-for="i in 4" :key="i">
<view class="skeleton-line skeleton-order-label"></view>
<view class="skeleton-line skeleton-order-value"></view>
</view>
<!-- 查看详情按钮骨架 -->
<view class="skeleton-line skeleton-view-detail-btn"></view>
</view>
<!-- 底部占位 -->
<view class="bottom-space"></view>
<!-- 底部按钮区域骨架 -->
<view class="bottom-buttons skeleton-bottom-buttons">
<view class="skeleton-line skeleton-continue-btn"></view>
<view class="skeleton-line skeleton-view-progress-btn"></view>
</view>
</view>
<view v-else class="abnormal-reported-page">
<!-- 成功提示区域 -->
<view class="success-section">
<view class="success-icon">
<up-icon name="checkmark-circle-fill" size="80" color="#4CD964"></up-icon>
</view>
<view class="success-title">异常已上报!</view>
<view class="success-desc">配送计时已暂停,不影响您的准时率</view>
</view>
<!-- 订单信息卡片 -->
<view class="order-card">
<view class="order-item">
<text class="order-label">订单号</text>
<text class="order-value" style="font-weight: 600;">MT20251017123456</text>
</view>
<view class="order-item">
<text class="order-label">商品信息</text>
<text class="order-value">共3件商品</text>
</view>
<view class="order-item">
<text class="order-label">配送地址</text>
<text class="order-value">XX小区3栋502室</text>
</view>
<view class="order-item">
<text class="order-label">用户备注</text>
<text class="order-value remark-text">请放门口,勿按门铃</text>
</view>
<!-- 查看详情按钮 -->
<view @click="Service.GoPage('/pages/my/abnormalDetail')" class="view-detail-btn">
<text>查看详情</text>
<up-icon name="arrow-right" size="20" color="#999"></up-icon>
</view>
</view>
<!-- 底部占位 -->
<view class="bottom-space"></view>
<!-- 底部按钮区域 -->
<view class="bottom-buttons">
<up-button @click="Service.GoPageTab('/pages/index/task')" type="primary" size="default" class="continue-btn">
继续接单
</up-button>
<up-button @click="Service.GoPage('/pages/my/abnormalDetail')" type="default" size="default" class="view-progress-btn">
查看异常进度
</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue';
import { Service } from '@/Service/Service';
let loading = ref(true)
// 页面加载时的逻辑
onLoad(() => {
setTimeout(() => {
loading.value = false
}, 1000)
})
</script>
<style lang="scss">
page {
background-color: #fff;
}
// 成功提示区域样式
.success-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 30rpx;
background-color: #F6FFFB;
margin: 20rpx;
}
.success-icon {
margin-bottom: 30rpx;
}
.success-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.success-desc {
font-size: 28rpx;
color: #999;
}
// 订单卡片样式
.order-card {
background-color: #fff;
padding: 30rpx;
border-radius: 12rpx;
margin: 0 30rpx;
box-shadow: 0 0 10rpx 0 #e2e2e2;
}
.order-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.order-item:last-child {
margin-bottom: 0;
}
.order-label {
font-size: 28rpx;
color: #666;
}
.order-value {
font-size: 28rpx;
color: #333;
text-align: right;
flex: 1;
margin-left: 30rpx;
}
.remark-text {
color: #FF6F00;
}
// 查看详情按钮样式
.view-detail-btn {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 30rpx;
padding-top: 30rpx;
border-top: 1rpx solid #f0f0f0;
color: #999;
font-size: 28rpx;
}
// 底部占位
.bottom-space {
height: 280rpx;
}
// 底部按钮区域样式
.bottom-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
gap: 20rpx;
padding: 20rpx;
background-color: #fff;
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
display: flex;
}
.continue-btn {
border-radius: 50rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
background-color: #007AFF;
margin-bottom: 20rpx;
}
.view-progress-btn {
border-radius: 50rpx;
height: 100rpx;
line-height: 98rpx;
font-size: 32rpx;
color: #007AFF;
border: 1rpx solid #007AFF;
background-color: #fff;
}
// 骨架屏基础样式和动画
.skeleton-line,
.skeleton-success-icon,
.skeleton-loading {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
// 骨架屏公共样式
.skeleton-line {
background-color: #f0f0f0;
}
// 骨架屏容器
.skeleton-container {
width: 100%;
}
// 成功提示区域骨架样式
.skeleton-success-section {
background-color: #f5f5f5;
}
.skeleton-success-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #f0f0f0;
margin-bottom: 30rpx;
}
.skeleton-success-title {
width: 40%;
height: 40rpx;
margin-bottom: 16rpx;
border-radius: 20rpx;
}
.skeleton-success-desc {
width: 70%;
height: 30rpx;
border-radius: 15rpx;
}
// 订单卡片骨架样式
.skeleton-order-card {
background-color: #f8f8f8;
}
.skeleton-order-label {
width: 20%;
height: 32rpx;
border-radius: 16rpx;
}
.skeleton-order-value {
width: 50%;
height: 32rpx;
border-radius: 16rpx;
}
.skeleton-view-detail-btn {
width: 25%;
height: 32rpx;
border-radius: 16rpx;
margin-top: 30rpx;
margin-left: auto;
}
// 底部按钮区域骨架样式
.skeleton-bottom-buttons {
background-color: #fff;
}
.skeleton-continue-btn {
width: 49%;
height: 100rpx;
border-radius: 50rpx;
margin-bottom: 20rpx;
}
.skeleton-view-progress-btn {
width: 49%;
height: 100rpx;
border-radius: 50rpx;
}
</style>

View File

@@ -0,0 +1,218 @@
<template>
<view class="home">
<view class=""
style=" margin-top: 200rpx; display: flex; flex-direction: column; justify-content: center; align-items: center;">
<image :src="Service.GetIconImg('/static/index/logo/logo.png')" style="width: 150rpx; height: 150rpx; "
mode="">
</image>
<view class="" style="font-size: 36rpx; font-weight: 800; margin-top: 20rpx;">
确菜农·骑手端
</view>
</view>
<view class="" style="margin: 30rpx 30rpx; margin-top: 150rpx;">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
欢迎登陆
</view>
<view class="" style="font-size: 28rpx; margin-top: 20rpx; ">
手机号登录,安全又便捷
</view>
<view class="">
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.phone" type="number" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入手机号"
clearable='true' border="surround"></up-input>
</view>
<view v-if="!type" class="" style="margin-top: 30rpx;">
<up-input v-model="login.password" type="password" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入密码"
clearable='true' border="surround"></up-input>
</view>
<view v-else class=""
style=" display: flex; align-items: center; justify-content: space-between; margin-top: 30rpx; border: 1rpx solid #dadbde; box-sizing: border-box; padding: 20rpx 30rpx; border-radius: 200rpx; ">
<view class="" style="">
<up-code-input v-model="login.code" :maxlength="4" mode="line" size='24'></up-code-input>
</view>
<view class="wrap">
<up-toast ref="uToastRef"></up-toast>
<up-code :seconds="seconds" @end="end" @start="start" ref="uCodeRef"
@change="codeChange"></up-code>
<view @click="getCode">{{tips}}</view>
</view>
</view>
</view>
<view class=""
style="margin-top: 40rpx; width: 100%; height: 80rpx; line-height: 80rpx; border-radius: 40rpx; background-color: #FFD700; color: #000; font-weight: bold; font-size: 30rpx; text-align: center;"
@click="loginshop()">
登录
</view>
<view class="" style="text-align: center; color: #637aff; margin: 50rpx 0; ">
<text @click="type=!type,login.password='',login.code=''">{{ !type?'使用验证码登录':'使用密码登录' }}</text>
</view>
<view class=""
style="display: flex; justify-content: center; align-items: center;color: black; margin-top: 50rpx; "
@click="isuser= !isuser">
<view class="" style="margin-right: 10rpx;">
<up-icon :name="!isuser?'checkmark-circle':'checkmark-circle-fill'"
:color="!isuser?'#999':'var(--nav-mian)'" size="20"></up-icon>
</view>
我同意
<a @click.stop style="color: #FF6B23; text-decoration: none; " :href="userUrl">《用户协议》</a>
<a @click.stop style="color: #FF6B23; text-decoration: none; " :href="userUrl">《隐私政策》</a>
</view>
<view class="" style="text-align: center; margin-top: 20rpx;">
新用户?系统将自动为您注册
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service, CNRiderLoginService } from "@/Service/CN/CNRiderLoginService"
import { ref } from "vue";
import { WebSocket } from '@/Service/Comm/TwWebSocket';
let qudPow = ref<string>('')
let login = ref({
phone: '',
code: '',
password: ''
})
const tips = ref('');
const seconds = ref(60);
const uCodeRef = ref(null);
let type = ref(false)
let isLogin = ref<boolean>(true)//登录注册状态
let isuser = ref<boolean>(false)//用户同意协议
let userUrl = ref('')
let other = ref('')
onLoad(() => {
getData()
});
onShow(() => {
});
const getData = () => {
CNRiderLoginService.GetPrivacy(1).then(res => {
userUrl.value = res.data.url
})
CNRiderLoginService.GetPrivacy(4).then(res => {
other.value = res.data.url
})
}
//登录
const loginshop = () => {
if (login.value.phone == '') {
return Service.Msg('请输入手机号/账号!')
}
if (login.value.password == '' && !type.value) {
return Service.Msg('请输入密码!')
}
if (login.value.code == '' && type.value) {
return Service.Msg('请输入验证码!')
}
if (!isuser.value) {
return Service.Msg('请勾选同意用户协议!')
}
CNRiderLoginService.RiderLogin(login.value).then(res => {
if (res.code == 0) {
Service.Msg('登录成功!')
Service.SetUserToken(res.data.accToken)
uni.$emit('ImCom')
setTimeout(() => {
isuser.value = false
Service.GoPageTab('/pages/index/index')
}, 1000)
} else {
Service.Msg(res.msg)
}
})
}
const codeChange = (text) => {
tips.value = text;
};
const getCode = () => {
if (!login.value.phone) {
Service.Msg('请输入手机号')
return
}
if (uCodeRef.value.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码',
});
setTimeout(() => {
uni.hideLoading();
CNRiderLoginService.SendUserSms(login.value.phone, 'RiderReg').then(res => {
})
uCodeRef.value.start();
}, 2000);
} else {
Service.Msg('倒计时结束后再发送')
}
};
const end = () => {
console.log('倒计时结束');
};
const start = () => {
console.log('倒计时开始');
};
</script>
<style lang="scss">
.home {
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom, #FFD700, #fff 40%);
overflow: hidden;
}
.chen {}
</style>

View File

@@ -0,0 +1,369 @@
<template>
<view class="income-container">
<!-- 收入概览区域 -->
<view class="income-overview">
<view class="income-header">
<text class="income-title">今日已赚</text>
<text class="income-amount">¥86.50</text>
</view>
<!-- 时间选择标签 -->
<view class="time-tabs">
<text v-for="(item ,index) in timeList" :key="index" @click="changeTab(index)"
:class="{ 'active':currentTime==index }" class="tab-item">{{item}}</text>
</view>
<text class="month-total">本月累计¥1,240.00</text>
</view>
<!-- 收入构成区域 -->
<view class="income-composition">
<!-- 饼图占位区域 -->
<view class="pie-chart-placeholder">
<view id="cahrt" class="pie-chart">
<!-- 黑边白底的饼图占位 -->
</view>
<view class="chart-legend">
<view class="legend-item">
<view class="legend-dot blue"></view>
<text class="legend-text">配送费¥60.50</text>
</view>
<view class="legend-item">
<view class="legend-dot orange"></view>
<text class="legend-text">冲单奖¥20.00</text>
</view>
<view class="legend-item">
<view class="legend-dot green"></view>
<text class="legend-text">恶劣天气补贴¥6.00</text>
</view>
</view>
</view>
</view>
<!-- 钱包区域 -->
<view class="wallet-section">
<view class="wallet-header">
<text class="wallet-title">钱包余额</text>
<text class="wallet-amount">¥86.50</text>
</view>
<u-button class="withdraw-button" type="primary">立即提现</u-button>
<text class="withdraw-tip">提现到账时间: T+1工作日</text>
</view>
<!-- 收入明细区域 -->
<view class="detail-header">
<text class="detail-title">收入明细</text>
</view>
<view class="detail-list" v-for="(item, index) in 3" :key="index">
<view class="detail-content">
<view class="icon-placeholder">
<image src="/static/index/income/order.png"
style="width: 55rpx; height: 55rpx;" mode=""></image>
</view>
<view class="detail-info">
<text class="order-id">订单 MT20251020123456</text>
<text class="order-time">10-20 18:35</text>
</view>
<text class="order-amount">+¥5.50</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
import { Service } from '@/Service/Service';
let timeList = ref([
'今日',
'本周',
'本月'
])
let currentTime = ref(0)
let option = ref({
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: true,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 1048 },
{ value: 735 },
{ value: 580 },
{ value: 484},
{ value: 300 }
]
}
]
})
onLoad(() => {
})
onMounted(()=>{
let chartDom =ref( document.getElementById('cahrt'))
let myChart = ref(echarts.init(chartDom.value))
myChart.value.setOption(option.value);
})
const changeTab = (index : number) => {
currentTime.value = index
}
</script>
<style scoped>
.income-container {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
box-sizing: border-box;
}
/* 收入概览区域 */
.income-overview {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.income-header {
margin-bottom: 20rpx;
}
.income-title {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.income-amount {
font-size: 48rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 时间标签 */
.time-tabs {
display: flex;
margin-bottom: 20rpx;
}
.tab-item {
font-size: 28rpx;
color: #666;
margin-right: 40rpx;
padding-bottom: 10rpx;
}
.tab-item.active {
color: #1890ff;
border-bottom: 3rpx solid #1890ff;
}
.month-total {
font-size: 26rpx;
color: #666;
}
/* 收入构成区域 */
.income-composition {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 饼图占位 */
.pie-chart-placeholder {
display: flex;
align-items: center;
}
.pie-chart {
width: 200rpx;
height: 200rpx;
margin-right: 30rpx;
position: relative;
overflow: hidden;
}
.chart-legend {
flex: 1;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.legend-dot.blue {
background-color: #1890ff;
}
.legend-dot.orange {
background-color: #fa8c16;
}
.legend-dot.green {
background-color: #52c41a;
}
.legend-text {
font-size: 26rpx;
color: #666;
}
/* 钱包区域 */
.wallet-section {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.wallet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.wallet-title {
font-size: 28rpx;
color: #333;
}
.wallet-amount {
font-size: 36rpx;
font-weight: bold;
color: #ff4d4f;
}
.withdraw-button {
width: 100%;
height: 80rpx;
line-height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
background-color: #1890ff;
color: #fff;
margin-bottom: 10rpx;
}
.withdraw-tip {
display: block;
font-size: 22rpx;
color: #999;
text-align: center;
}
/* 收入明细区域 */
.income-detail {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
}
.detail-header {
margin-bottom: 20rpx;
}
.detail-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.detail-content {
display: flex;
align-items: center;
}
.detail-list {
margin: 15rpx 0 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-radius: 20rpx;
}
/* 图标占位符 - 黑边白底 */
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.detail-info {
flex: 1;
}
.order-id {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.order-time {
font-size: 24rpx;
color: #999;
}
.order-amount {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 没有更多记录 */
.no-more {
text-align: center;
padding: 40rpx 0;
}
.no-more-text {
font-size: 24rpx;
color: #999;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

View File

@@ -0,0 +1,655 @@
<template>
<!-- 骨架屏状态 -->
<view v-if="loading" class="skeleton-loading">
<!-- 收入概览骨架屏 -->
<view class="skeleton-card income-overview">
<view class="skeleton-income-title"></view>
<view class="skeleton-income-amount"></view>
<view class="skeleton-time-tabs">
<view class="skeleton-tab-item"></view>
<view class="skeleton-tab-item"></view>
<view class="skeleton-tab-item"></view>
</view>
<view class="skeleton-month-total"></view>
</view>
<!-- 收入构成骨架屏 -->
<view class="skeleton-card income-composition">
<view class="skeleton-pie-container">
<view class="skeleton-pie-chart"></view>
<view class="skeleton-legend-list">
<view class="skeleton-legend-item" v-for="item in 3">
<view class="skeleton-legend-dot"></view>
<view class="skeleton-legend-text"></view>
</view>
</view>
</view>
</view>
<!-- 钱包区域骨架屏 -->
<view class="skeleton-card wallet-section">
<view class="skeleton-wallet-header">
<view class="skeleton-wallet-title"></view>
<view class="skeleton-wallet-amount"></view>
</view>
<view class="skeleton-withdraw-button"></view>
<view class="skeleton-withdraw-tip"></view>
</view>
<!-- 收入明细骨架屏 -->
<view class="skeleton-card">
<view class="skeleton-detail-title"></view>
</view>
<!-- 明细项骨架屏 -->
<view v-for="index in 3" :key="index" class="skeleton-card detail-list">
<view class="skeleton-detail-content">
<view class="skeleton-icon-placeholder"></view>
<view class="skeleton-detail-info">
<view class="skeleton-order-id"></view>
<view class="skeleton-order-time"></view>
</view>
<view class="skeleton-order-amount"></view>
</view>
</view>
<!-- 加载更多 -->
<view class="skeleton-card" style="display: flex; justify-content: center; ">
<view class="skeleton-detail-title"></view>
</view>
</view>
<!-- 收入概览区域 -->
<view v-else class="income-container">
<view class="income-overview">
<view class="income-header">
<text class="income-title">今日已赚</text>
<text class="income-amount">¥86.50</text>
</view>
<!-- 时间选择标签 -->
<view class="time-tabs">
<text v-for="(item ,index) in timeList" :key="index" @click="changeTab(index)"
:class="{ 'active':currentTime==index }" class="tab-item">{{item}}</text>
</view>
<text class="month-total">本月累计¥1,240.00</text>
</view>
<!-- 收入构成区域 -->
<view class="income-composition">
<!-- 饼图占位区域 -->
<view class="pie-chart-placeholder">
<view id="cahrt" class="pie-chart">
<!-- 黑边白底的饼图占位 -->
</view>
<view class="chart-legend">
<view class="legend-item">
<view class="legend-dot blue"></view>
<text class="legend-text">配送费¥60.50</text>
</view>
<view class="legend-item">
<view class="legend-dot orange"></view>
<text class="legend-text">冲单奖¥20.00</text>
</view>
<view class="legend-item">
<view class="legend-dot green"></view>
<text class="legend-text">恶劣天气补贴¥6.00</text>
</view>
</view>
</view>
</view>
<!-- 钱包区域 -->
<view class="wallet-section">
<view class="wallet-header">
<text class="wallet-title">钱包余额</text>
<text class="wallet-amount">¥86.50</text>
</view>
<u-button class="withdraw-button" @click="Service.GoPage('/pages/order/withdraw')" type="primary">立即提现</u-button>
<text class="withdraw-tip">提现到账时间: T+1工作日</text>
</view>
<!-- 收入明细区域 -->
<view class="detail-header">
<text class="detail-title">收入明细</text>
</view>
<view class="detail-list" @click="Service.GoPage('/pages/order/incomeDetail')" v-for="(item, index) in 3"
:key="index">
<view class="detail-content">
<view class="icon-placeholder">
<image :src="Service.GetIconImg('/static/index/income/order.png')"
style="width: 55rpx; height: 55rpx;" mode=""></image>
</view>
<view class="detail-info">
<text class="order-id">订单 MT20251020123456</text>
<text class="order-time">10-20 18:35</text>
</view>
<text class="order-amount">+¥5.50</text>
</view>
</view>
<up-loadmore :status="status" />
<view class="" style="width: 100vw; height: 100rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
import { Service } from '@/Service/Service';
let loading = ref(true)
let timeList = ref([
'今日',
'本周',
'本月'
])
let currentTime = ref(0)
let status = ref('nomore')
let test = ref(10)
let option = ref({
tooltip: {
'show': false
},
legend: {
'show': false
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: true,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: test.value, name: "配送奖", itemStyle: { color: '#1890FF' } },
{ value: 735, name: "冲单奖", itemStyle: { color: '#52C41A' } },
{ value: 580, name: "恶劣天气奖", itemStyle: { color: '#FF4D4F' } }
]
}
]
})
onLoad(() => {
setTimeout(() => {
loading.value = false
setTimeout(() => {
draw()
}, 1000)
}, 1000)
})
onMounted(() => {
})
const draw = () => {
let chartDom = ref(document.getElementById('cahrt'))
let myChart = ref(echarts.init(chartDom.value))
myChart.value.setOption(option.value)
}
const changeTab = (index : number) => {
currentTime.value = index
}
</script>
<style scoped>
.income-container {
padding: 20rpx;
background-color: #f5f5f5;
}
/* 收入概览区域 */
.income-overview {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.income-header {
margin-bottom: 20rpx;
}
.income-title {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.income-amount {
font-size: 48rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 时间标签 */
.time-tabs {
display: flex;
margin-bottom: 20rpx;
}
.tab-item {
font-size: 28rpx;
color: #666;
margin-right: 40rpx;
padding-bottom: 10rpx;
}
.tab-item.active {
color: #1890ff;
border-bottom: 3rpx solid #1890ff;
}
.month-total {
font-size: 26rpx;
color: #666;
}
/* 收入构成区域 */
.income-composition {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 饼图占位 */
.pie-chart-placeholder {
display: flex;
align-items: center;
}
.pie-chart {
width: 200rpx;
height: 200rpx;
margin-right: 30rpx;
position: relative;
overflow: hidden;
}
.chart-legend {
flex: 1;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.legend-dot.blue {
background-color: var(--nav-mian);
}
.legend-dot.orange {
background-color: var(--nav-vice);
}
.legend-dot.green {
background-color: var(--nav-diluted);
}
.legend-text {
font-size: 26rpx;
color: #666;
}
/* 钱包区域 */
.wallet-section {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.wallet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.wallet-title {
font-size: 28rpx;
color: #333;
}
.wallet-amount {
font-size: 36rpx;
font-weight: bold;
color: #ff4d4f;
}
.withdraw-button {
width: 100%;
height: 80rpx;
line-height: 80rpx;
font-size: 32rpx;
border-radius: 40rpx;
background-color: #1890ff;
color: #fff;
margin-bottom: 10rpx;
}
.withdraw-tip {
display: block;
font-size: 22rpx;
color: #999;
text-align: center;
}
/* 收入明细区域 */
.income-detail {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
}
.detail-header {
margin-bottom: 20rpx;
}
.detail-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.detail-content {
display: flex;
align-items: center;
}
.detail-list {
margin: 15rpx 0 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-radius: 20rpx;
}
/* 图标占位符 - 黑边白底 */
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.detail-info {
flex: 1;
}
.order-id {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.order-time {
font-size: 24rpx;
color: #999;
}
.order-amount {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 没有更多记录 */
.no-more {
text-align: center;
padding: 40rpx 0;
}
.no-more-text {
font-size: 24rpx;
color: #999;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-card {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
position: relative;
overflow: hidden;
}
/* 收入概览骨架屏 */
.skeleton-income-title {
width: 100rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-bottom: 15rpx;
}
.skeleton-income-amount {
width: 200rpx;
height: 50rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-bottom: 20rpx;
}
.skeleton-time-tabs {
display: flex;
margin-bottom: 20rpx;
}
.skeleton-tab-item {
width: 80rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-right: 40rpx;
}
.skeleton-month-total {
width: 200rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
/* 收入构成骨架屏 */
.skeleton-pie-container {
display: flex;
align-items: center;
}
.skeleton-pie-chart {
width: 200rpx;
height: 200rpx;
background-color: #f0f0f0;
border-radius: 50%;
margin-right: 30rpx;
}
.skeleton-legend-list {
flex: 1;
}
.skeleton-legend-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.skeleton-legend-dot {
width: 16rpx;
height: 16rpx;
background-color: #f0f0f0;
border-radius: 50%;
margin-right: 10rpx;
}
.skeleton-legend-text {
width: 200rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
/* 钱包区域骨架屏 */
.skeleton-wallet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.skeleton-wallet-title {
width: 120rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-wallet-amount {
width: 150rpx;
height: 40rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-withdraw-button {
width: 100%;
height: 80rpx;
background-color: #f0f0f0;
border-radius: 40rpx;
margin-bottom: 10rpx;
}
.skeleton-withdraw-tip {
width: 300rpx;
height: 24rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin: 0 auto;
}
/* 收入明细骨架屏 */
.skeleton-detail-title {
width: 150rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-detail-content {
display: flex;
align-items: center;
}
.skeleton-icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #f0f0f0;
border-radius: 8rpx;
margin-right: 20rpx;
}
.skeleton-detail-info {
flex: 1;
}
.skeleton-order-id {
width: 300rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-bottom: 10rpx;
}
.skeleton-order-time {
width: 200rpx;
height: 26rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-order-amount {
width: 100rpx;
height: 36rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
/* 骨架屏动画 */
.skeleton-loading .skeleton-income-title::after,
.skeleton-loading .skeleton-income-amount::after,
.skeleton-loading .skeleton-tab-item::after,
.skeleton-loading .skeleton-month-total::after,
.skeleton-loading .skeleton-pie-chart::after,
.skeleton-loading .skeleton-legend-dot::after,
.skeleton-loading .skeleton-legend-text::after,
.skeleton-loading .skeleton-wallet-title::after,
.skeleton-loading .skeleton-wallet-amount::after,
.skeleton-loading .skeleton-withdraw-button::after,
.skeleton-loading .skeleton-withdraw-tip::after,
.skeleton-loading .skeleton-detail-title::after,
.skeleton-loading .skeleton-icon-placeholder::after,
.skeleton-loading .skeleton-order-id::after,
.skeleton-loading .skeleton-order-time::after,
.skeleton-loading .skeleton-order-amount::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,445 @@
<template>
<view v-if="loading" class="">
<view class="tab-bar-skeleton">
<view class="skeleton-slot skeleton-tab-item"></view>
<view class="skeleton-slot skeleton-tab-item"></view>
</view>
<view class="skeleton-container">
<view class="skeleton-order-item" v-for="index in 3" :key="'skeleton-' + index">
<!-- 订单头部骨架 -->
<view class="skeleton-header">
<view class="skeleton-slot skeleton-order-number"></view>
<view class="skeleton-slot skeleton-status-badge"></view>
</view>
<!-- 异常类型骨架 -->
<view class="skeleton-slot skeleton-type"></view>
<!-- 异常描述骨架 -->
<view class="skeleton-slot skeleton-description"></view>
<!-- 底部信息骨架 -->
<view class="skeleton-footer">
<view class="skeleton-slot skeleton-time"></view>
<view class="skeleton-actions">
<view class="skeleton-slot skeleton-btn"></view>
<view class="skeleton-slot skeleton-btn"></view>
</view>
</view>
</view>
<view class="" style="width: 100vw; display: flex; justify-content: center; ">
<view class="skeleton-slot " style="width: 160rpx; height: 30rpx; "></view>
</view>
</view>
</view>
<view v-else class="abnormal-orders-page">
<!-- 选项卡 -->
<view class="tab-bar">
<view class="tab-item" :class="{ active: activeTab === 0 }" @click="switchTab(0)">
<text class="tab-text" :class="{ active: activeTab === 0 }">待处理</text>
</view>
<view class="tab-item" :class="{ active: activeTab === 1 }" @click="switchTab(1)">
<text class="tab-text" :class="{ active: activeTab === 1 }">已处理</text>
</view>
</view>
<!-- 订单列表 -->
<view class="order-list">
<view class="order-item" v-for="(orderItem,orederIndex) in dataList" :key="orederIndex">
<!-- 订单头部 -->
<view class="order-header">
<text class="order-number">订单号: {{ orderItem.orderId}}</text>
<view class="order-status pending">
<text class="status-text">{{ orderItem.status===0?'待处理':'已处理' }}</text>
</view>
</view>
<!-- 订单内容 -->
<view class="">
<text class="abnormal-type">{{orderItem.type }}</text>
<text class="abnormal-description">{{ orderItem.remark }}</text>
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<text class="order-time"
style="font-size: 26rpx; color: #666;">{{ Service.formatDate(orderItem.addTime,1) }}</text>
<!-- 操作按钮 -->
<view class="order-actions">
<text @click="Service.GoPage('/pages/my/abnormalDetail?orderId='+orderItem.orderId)"
class="action-btn detail-btn">查看详情</text>
</view>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, computed } from 'vue';
import { Service } from "@/Service/Service"
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let loading = ref(true)
// 响应式数据
const activeTab = ref(0);
let status = ref('nomore')
let page = ref(1)
let dataList = ref<Array<any>>([])
onLoad(() => {
getData()
})
const getData = () => {
dataList.value = []
page.value = 1
status.value = 'loadmore'
getList()
}
const getList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderOrderServiceList(activeTab.value, page.value).then(res => {
loading.value = false
if (res.data) {
dataList.value = [...dataList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
}
})
}
// 切换选项卡
const switchTab = (tab : any) => {
activeTab.value = tab;
getData()
};
// 重新上报
const reportAgain = (orderNo : string) => {
uni.showModal({
title: '重新上报',
content: '确定要重新上报此订单异常吗?',
success: (res) => {
if (res.confirm) {
// 模拟重新上报
uni.showToast({
title: '已提交重新上报申请',
icon: 'success'
});
}
}
});
};
</script>
<style scoped lang="scss">
/* 页面基础样式 */
.abnormal-orders-page {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 顶部导航栏 */
.nav-bar {
height: 100rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
padding: 0 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left,
.nav-right {
width: 60rpx;
display: flex;
justify-content: center;
align-items: center;
}
.nav-center {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.nav-title {
font-size: 34rpx;
color: #333;
font-weight: 500;
}
/* 选项卡 */
.tab-bar {
height: 88rpx;
display: flex;
background-color: #fff;
margin-bottom: 20rpx;
}
.tab-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 50rpx;
height: 6rpx;
background-color: #1890ff;
border-radius: 3rpx;
}
.tab-text {
font-size: 28rpx;
color: #666;
}
.tab-text.active {
color: #1890ff;
font-weight: 500;
}
/* 订单列表 */
.order-list {
padding: 0 20rpx;
}
.order-item {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 订单头部 */
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.order-number {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.order-status {
padding: 4rpx 16rpx;
border-radius: 20rpx;
}
.order-status.pending {
background-color: #fff2e8;
}
.order-status.processed {
background-color: #f6ffed;
}
.status-text {
font-size: 24rpx;
}
.order-status.pending .status-text {
color: #fa5151;
}
.order-status.processed .status-text {
color: #52c41a;
}
.abnormal-type {
font-size: 28rpx;
color: #333;
font-weight: 500;
display: block;
margin-bottom: 12rpx;
}
.abnormal-description {
font-size: 26rpx;
color: #666;
display: block;
margin-bottom: 12rpx;
line-height: 1.5;
}
.order-time {
font-size: 24rpx;
color: #999;
}
/* 操作按钮 */
.order-actions {
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.action-btn {
font-size: 26rpx;
padding: 8rpx 20rpx;
}
.detail-btn {
color: #1890ff;
}
.report-btn {
color: #52c41a;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 骨架屏样式 - 纯CSS实现 */
/* 选项卡骨架屏 */
.tab-bar-skeleton {
height: 88rpx;
display: flex;
background-color: #fff;
margin-bottom: 20rpx;
padding: 0 40rpx;
}
.skeleton-tab-item {
flex: 1;
height: 36rpx;
align-self: center;
margin: 0 20rpx;
border-radius: 18rpx;
}
/* 订单列表骨架屏 */
.skeleton-container {
padding: 0 20rpx;
}
/* 订单项骨架 */
.skeleton-order-item {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
/* 订单头部布局 */
.skeleton-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
/* 底部布局 */
.skeleton-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 操作按钮区域 */
.skeleton-actions {
display: flex;
gap: 20rpx;
}
/* 通用骨架占位元素 */
.skeleton-slot {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite ease-in-out;
border-radius: 8rpx;
}
/* 特定元素尺寸 */
.skeleton-order-number {
width: 60%;
height: 36rpx;
}
.skeleton-status-badge {
width: 120rpx;
height: 40rpx;
border-radius: 20rpx;
}
.skeleton-type {
width: 30%;
height: 36rpx;
margin-bottom: 16rpx;
}
.skeleton-description {
width: 100%;
height: 32rpx;
margin-bottom: 16rpx;
}
.skeleton-time {
width: 25%;
height: 30rpx;
}
.skeleton-btn {
width: 100rpx;
height: 30rpx;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>

View File

@@ -0,0 +1,86 @@
{
"id": "uni-calendar",
"displayName": "uni-calendar 日历",
"version": "1.4.12",
"description": "日历组件",
"keywords": [
"uni-ui",
"uniui",
"日历",
"",
"打卡",
"日历选择"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

View File

@@ -0,0 +1,97 @@
# qf-image-cropper
## 图片裁剪插件
uniapp微信小程序图片裁剪插件支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。
### 平台支持:
1. 支持微信小程序移动端、PC端、开发者工具
2. 支持H5平台2.1.0版本起)
3. 支持APP平台2.1.5版本起Android、IOS
4. 其他平台暂未测试兼容性未知
### 支持功能:
1. 自定义裁剪尺寸
2. 定点等比例缩放移动端以双指触摸中心点为缩放中心点PC端以鼠标所在点为缩放中心点
3. 自由拖动:支持限制滑出边界,也支持回弹效果(滑动时可滑出边界,释放时回弹到边界)
4. 图片翻转:在裁剪尺寸非 1:1 的情况下,翻转时宽高无法铺满裁剪区域时,图片会自动放大到合适尺寸
5. 裁剪生成新图片
6. 本地选择图片
7. 可定制样式:可自由选择是否渲染裁剪边框、可伸缩裁剪顶角、参考线
8. 裁剪圆角图片:圆形、圆角矩形
### 属性说明
| 属性名 | 类型 | 默认值 | 说明 |
|:---|:---|:---|:---|
| src | String | | 图片资源地址 |
| width | Number | 300 | 裁剪宽度 |
| height | Number | 300 | 裁剪高度 |
| showBorder | Boolean | true | 是否绘制裁剪区域边框 |
| showGrid | Boolean | true | 是否绘制裁剪区域网格参考线 |
| showAngle | Boolean | true | 是否展示四个支持伸缩的角 |
| areaScale | Number | 0.3 | 裁剪区域最小缩放倍数 |
| minScale | Number | 1 | 图片最小缩放倍数 |
| maxScale | Number | 5 | 图片最大缩放倍数 |
| checkRange | Boolean | true | 检查图片位置是否超出裁剪边界,如果超出则会矫正位置 |
| backgroundColor | String | | 生成图片背景色:如果裁剪区域没有完全包含在图片中时,不设置该属性则生成图片存在一定的透明块 |
| bounce | Boolean | true | 是否有回弹效果:当 checkRange 为 true 时有效,拖动时可以拖出边界,释放时会弹回边界 |
| rotatable | Boolean | true | 是否支持翻转 |
| reverseRotatable | Boolean | false | 是否支持逆向翻转 |
| choosable | Boolean | true | 是否支持从本地选择素材 |
| gpu | Boolean | false | 是否开启硬件加速,图片缩放过程中如果出现元素的“留影”或“重影”效果,可通过该方式解决或减轻这一问题 |
| angleSize | Number | 20 | 四个角尺寸单位px |
| angleBorderWidth | Number | 2 | 四个角边框宽度单位px |
| zIndex | Number/String | | 调整组件层级 |
| radius | Number | | 裁剪图片圆角半径单位px |
| fileType | String | png | 生成文件的类型,只支持 'jpg' 或 'png'。默认为 'png' |
| delay | Number | 1000 | 图片从绘制到生成所需时间单位ms<br>微信小程序平台使用 `Canvas 2D` 绘制时有效<br>如绘制大图或出现裁剪图片空白等情况应适当调大该值,因 `Canvas 2d` 采用同步绘制,需自己把控绘制完成时间 |
| navigation | Boolean | true | 页面是否是原生标题栏:<br>H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 `"navigationStyle": "custom"` 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差。<br>注因H5平台的窗口高度是包含标题栏的而屏幕触摸点的坐标是不包含的 |
| @crop | EventHandle | | 剪裁完成后触发event = { tempFilePath }。在H5平台下tempFilePath 为 base64 |
### 基本用法
```
<template>
<div>
<qf-image-cropper :width="500" :height="500" :radius="30" @crop="handleCrop"></qf-image-cropper>
</div>
</template>
<script>
import QfImageCropper from '@/components/qf-image-cropper/qf-image-cropper.vue';
export default {
components: {
QfImageCropper
},
methods: {
handleCrop(e) {
uni.previewImage({
urls: [e.tempFilePath],
current: 0
});
}
}
}
</script>
```
通过ref组件实例可在进入页面后直接打开相册选择图片
```
mounted() {
this.$refs.qfImageCropper.chooseImage({ sourceType: ['album'] });
}
```
### 使用说明
1.建议在`pages.json`中将引用插件的页面添加一下配置禁止下拉刷新和禁止页面滑动,防止出现性能或页面抖动等问题。
```
{
"enablePullDownRefresh": false,
"disableScroll": true
}
```
2.建议使用本插件不要设置过大宽高的目标图片尺寸建议1365x1365以内否则可能会导致如下问题
```
1.界面卡顿,内存占用过高
2.生成图片失真(模糊)
3.确定裁剪后一直显示 `裁剪中...`,该问题是由 `uni.canvasToTempFilePath` 无法回调导致,不同平台不同设备限制可能有所不同。
```
3.如裁剪后的图片存在偏移的问题,请检查是否受自己项目中父组件或全局样式影响。
4.src属性设置网络图片时图片资源必须是能触发 `getImageInfo` API 的 success 回调才可用于插件裁剪。因此小程序平台获取网络图片信息需先配置download域名白名单才能生效。
5.如果组件无法正常渲染且使用了 `v-if` 时,可尝试将 `v-if` 替换为 `v-show`
6.如果App端导入组件后无法正常渲染请尝试重新运行

View File

@@ -0,0 +1,126 @@
{
"easycom": {
// 注意一定要放在custom里否则无效https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "门店运营",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/task",
"style": {
"navigationBarTitleText": "任务",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/income",
"style": {
"navigationBarTitleText": "我的收入",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/user",
"style": {
"navigationBarTitleText": "我的",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "v派商家",
"navigationBarBackgroundColor": "#fff",
"backgroundColor": "#000"
},
"subPackages": [
{
"root": "pages/order",
"pages": [
{
"path": "orderDetail",
"style": {
"navigationBarTitleText": "项目中心",
"navigationStyle": "custom"
}
},
{
"path" : "navigation",
"style" :
{
"navigationBarTitleText" : "导航",
"navigationStyle": "custom"
}
},
{
"path" : "abnormal",
"style" :
{
"navigationBarTitleText" : "上报异常",
"navigationStyle": "custom"
}
},
{
"path" : "grabOrder",
"style" :
{
"navigationBarTitleText" : "订单详情"
}
}
]
}
],
"tabBar": {
"color": "#000",
"selectedColor": "#000",
"backgroundColor": "#FFFFFF",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tab/home.png",
"selectedIconPath": "/static/tab/homed.png",
"text": "主页"
},
{
"pagePath": "pages/index/task",
"iconPath": "static/tab/task.png",
"selectedIconPath": "static/tab/tasked.png",
"text": "任务"
},
{
"pagePath": "pages/index/income",
"iconPath": "static/tab/income.png",
"selectedIconPath": "static/tab/incomed.png",
"text": "收入"
},
{
"pagePath": "pages/index/user",
"iconPath": "static/tab/user.png",
"selectedIconPath": "static/tab/usered.png",
"text": "我的"
}
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,380 @@
<template>
<!-- 实际内容 -->
<view class="rider-home">
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥86.50</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">5单</text>
</view>
<view class="stat-item">
<text class="stat-label">进行中</text>
<text class="stat-value ongoing">6单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button type="primary" shape="circle" size="default" class="accept-orders-btn"
@click="toggleAcceptOrders">{{ userStatus === '已上线' ? '接单中 · 点击暂停' : '点击开始接单' }}</up-button>
</view>
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view class="" style="padding: 0 30rpx;">
<view @click="gopage()" v-for="(orderItem,orderIndex) in 3 " :key="orderIndex" class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="high-price-tag" :style="{'border':activeTab==0?'1rpx solid #52C41A':'1rpx solid #FAAD14','color':activeTab==0?'#52C41A':'#FAAD14' }" style="background-color: #fff;" >
<text class="high-price-text">{{activeTab==0?'待取单':'配送中'}}</text>
</view>
<view class="" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">老北京炸酱面</text>
<text v-if="activeTab==0" class="distance">500m</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text">北京市朝阳区三里屯SOHO</text>
<text v-if="activeTab!==0" class="address-text">共3件商品</text>
</view>
<!-- 商品次数-->
<view class="address-info">
<text class="address-text">共3件商品</text>
<view class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
</view>
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==0" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">A121</text>
</view>
<view v-if="activeTab==1" class="">
<text class="address-text">据您1.2km</text>
</view>
<view class="pickup-time">
<up-icon name="clock" color="#FF9500" size="24rpx" />
<text class="time-text" :style="{'color':activeTab==0?'#FAAD14':'#FF0000'}" >{{activeTab==0?'12:30 前取餐':'12:30 前送达'}}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<up-button type="primary" :color="activeTab==0?'#1890FF':'#52C41A'" size="large" class="grab-btn">{{ activeTab==0?'我已取餐':'确认送达'}}</up-button>
</view>
</view>
<up-loadmore :status="status" />
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
// 加载状态
const isLoading = ref(true);
let userStatus = ref('已下线')
let status = ref('nomore')
const tabs = ['待取货', '配送中']
const activeTab = ref(0)
onLoad(() => {
setTimeout(() => {
isLoading.value = false
}, 1500)
})
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
}
// 切换接单状态
const toggleAcceptOrders = () => {
userStatus.value = userStatus.value === '已上线' ? '已下线' : '已上线';
};
// 页面跳转
const gopage = () => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder')
} else {
Service.GoPage('/pages/order/orderDetail')
}
}
</script>
<style scoped lang="scss">
page {
background-color: #F6f6f6;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 顶部标签栏 */
.tab-bar {
margin-top: 20rpx;
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: center;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
</style>

View File

@@ -0,0 +1,703 @@
<template>
<!-- 导航栏 -->
<view class=""
style=" z-index: 100; padding:50rpx 20rpx 18rpx; box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: #fff; display: flex; align-items: center; justify-content: space-between;">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" size="32rpx"></up-icon>
</view>
<view class="">
订单详情
</view>
<view class="" @click="Service.GoPage('/pages/my/myKF')" style="color: var(--nav-banbacor);">
<image :src="Service.GetIconImg('/static/index/order/message.png')" style="width: 32rpx; height: 32rpx; "
mode=""></image>
</view>
</view>
<view class="" style="width: 100%; height: 88rpx; ">
</view>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 骨架屏订单状态 -->
<view class="skeleton-status"></view>
<!-- 骨架屏订单基本信息 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="skeleton-info-half"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-row">
<view class="skeleton-info-third"></view>
<view class="skeleton-info-btn"></view>
</view>
</view>
<!-- 骨架屏物品清单 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;">
<view class="" style="background-color: #e6e6e6; width: 60%;height: 40rpx; ">
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-row" v-for="(item,index) in 3" :key="index">
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;">
<view class="" style="background-color: #e6e6e6; width: 90%;height: 40rpx; ">
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-list-status"></view>
</view>
<!-- 骨架屏地图区域 -->
<view class="skeleton-map"></view>
<!-- 骨架屏取餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-code"></view>
</view>
<!-- 骨架屏送餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-remark"></view>
</view>
<!-- 骨架屏底部按钮 -->
<view class="skeleton-bottom">
<view class="skeleton-bottom-btn"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单状态 -->
<view class="order-status"
:style="{ 'background-color':orderStatus==0?'#E6F7FF':(orderStatus==1?'#FFFBE6':'#FFF2F0') }">
<text :style="{ 'color':orderStatus==0?'#1890FF':(orderStatus==1?'#FAAD14':'#FF4D4F') }"
style="font-size: 34rpx; font-weight: 600;">待取餐 · 请尽快到店取餐</text>
</view>
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="info-item">
<view class="label" style="font-weight: 700; font-size: 30rpx;">
{{ orderInfo.distribution=='预约订单'?'预计'+orderInfo.makeTime.split(' ')[1]+'送达':orderInfo.makeTime }}
</view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">{{ orderId }}</text>
</view>
<view class="info-item" style="justify-content: space-between;">
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="16" class="clock-icon"></u-icon>
{{ Service.formatDate(orderInfo.addTime,1) }} 下单
</view>
<view class="">
<button @click="Service.GoPage('/pages/order/abnormal?orderId='+orderInfo.orderId)" class=""
style="padding: 0rpx 30rpx; height: 60rpx; font-size: 24rpx; border-radius: 40rpx; background-color: red; color: #fff; ">提交异常</button>
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
数量
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
金额
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'110rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in JSON.parse(orderInfo.detail) " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
{{goodsItem.goodsName}}
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×{{ goodsItem.count }}
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥{{ goodsItem.count*goodsItem.price }}
</view>
</view>
</view>
<view class=""
style="display: flex; align-items: center;justify-content: space-between; margin-top: 10rpx; ">
<view class="">
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥{{ Number(orderInfo.postage).toFixed(2) }}</text>
</view>
</view>
<view class="" v-if="JSON.parse(orderInfo.detail).length>2" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<text class="section-title">取餐地址</text>
<view class="address-content">
<view class="store-name">{{ storeInfo.name }}</view>
<text class="address">{{ storeInfo.city }}{{storeInfo.region }}{{ storeInfo.address }}</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button @click="call(storeInfo.phone)" icon="phone" type="primary" shape="circle"
text="拨打商家"></up-button>
</view>
<view class="pickup-code">
<text class="code-label">取餐号:</text>
<text class="code-value">A123</text>
</view>
</view>
</view>
<!-- 送餐地址 -->
<view class="address-section">
<text class="section-title">送餐地址</text>
<view class="address-content">
<text class="user-name">{{ JSON.parse(orderInfo.address).realName }}</text>
<text class="address">{{ JSON.parse(orderInfo.address).address }}</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button @click="call(JSON.parse(orderInfo.address).phone)" icon="phone" type="primary"
shape="circle" text="拨打商家"></up-button>
</view>
<view v-if="orderInfo.remark" class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">{{ orderInfo.remark }}</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action" style="display: flex; justify-content: space-between;'">
<up-button @click="upGoods()" color="var(--nav-vice)" class="confirm-btn">{{type==1?'我已取货':'订单完成'}}</up-button>
<view class="" style="width: 40rpx;">
</view>
<up-button @click="Service.GoPage('/pages/order/orderChat?orderId='+orderId)" color="#6868ee" class="confirm-btn">联系商家</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
let deliveryTime = ref('')
let orderInfo = ref<any>({})
let storeInfo = ref<any>({})
let storeLocation = ref<any>({})
let type = ref(1)
let orderId = ref('')
onLoad((data : any) => {
orderId.value = data.orderId
type.value = data.type
getData()
})
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
loading.value = false
if (res.data) {
deliveryTime.value = res.data.deliveryTime
orderInfo.value = res.data.orderInfo
storeInfo.value = res.data.storeInfo
storeLocation.value = res.data.storeLocation
}
})
}
const call = (phone : string) => {
uni.makePhoneCall({
phoneNumber: phone,
fail(e) {
console.log( e);
},
success(e) {
console.log('1111', e);
}
})
}
const upGoods = () => {
CNRiderOrderService.UpdateRiderOrderTake(orderId.value, type.value).then(res => {
if (res.data) {
Service.Msg('取餐成功!')
setTimeout(() => {
Service.GoPageBack()
}, 1000)
} else {
Service.Msg(res.msg)
}
})
}
</script>
<style scoped>
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 140rpx;
}
/* 骨架屏导航栏 */
.skeleton-nav {
height: 88rpx;
position: fixed;
top: 0;
left: 0;
width: 100vw;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
z-index: 100;
}
.skeleton-nav-item {
width: 32rpx;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-nav-title {
width: 180rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单状态 */
.skeleton-status {
height: 100rpx;
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-status::after {
content: '';
width: 350rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单基本信息 */
.skeleton-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-row {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
}
.skeleton-info-half {
width: 45%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-right {
width: 10%;
}
.skeleton-line {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-third {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-btn {
width: 20%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 25rpx;
animation: shimmer 1.5s infinite;
}
/* 列表状态 */
.skeleton-list-status {
height: 60rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-list-status::after {
content: '';
width: 200rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏地图区域 */
.skeleton-map {
margin: 20rpx;
height: 400rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: shimmer 1.5s infinite;
position: relative;
overflow: hidden;
}
.skeleton-map::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, transparent 25%, rgba(255, 255, 255, 0.2) 25%, rgba(255, 255, 255, 0.2) 50%, transparent 50%, transparent 75%, rgba(255, 255, 255, 0.2) 75%, rgba(255, 255, 255, 0.2));
background-size: 100rpx 100rpx;
animation: shimmer 1.5s infinite linear;
}
/* 骨架屏地址区域 */
.skeleton-address {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-title {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 25rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-store-name {
width: 70%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-address-line {
width: 90%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-btn {
height: 70rpx;
background-color: #e6e6e6;
border-radius: 35rpx;
margin: 20rpx 0;
animation: shimmer 1.5s infinite;
}
.skeleton-code {
width: 50%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-remark {
width: 80%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏底部按钮 */
.skeleton-bottom {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.skeleton-bottom-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
opacity: 0.6;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.6;
}
}
/* 骨架屏滑动动画 */
@keyframes shimmer-slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
/* end */
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin-bottom: 25rpx;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin: 10rpx 0;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
</style>

View File

@@ -0,0 +1,506 @@
<template>
<view class="skeleton-container" v-if="isLoading">
<!-- 顶部提示栏骨架 -->
<view class="skeleton-top-tip"></view>
<!-- 身份信息区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-title"></view>
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
</view>
<!-- 身份证照片区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-title"></view>
<view class="skeleton-upload-item"></view>
<view class="skeleton-upload-item"></view>
</view>
<!-- 协议勾选骨架 -->
<view class="skeleton-agreement"></view>
<!-- 提交按钮骨架 -->
<view class="skeleton-submit"></view>
</view>
<view v-else class="real-name-auth-container">
<!-- 顶部提示栏 -->
<view class="top-tip">
<text class="tip-text">修改实名认证将重新审核大约1~2工作日</text>
</view>
<!-- 表单内容 -->
<view class="form-content">
<!-- 身份信息区域 -->
<view class="section">
<view class="section-title">身份信息</view>
<!-- 姓名输入 -->
<view class="form-item">
<view class="label">姓名</view>
<u-input v-model="formData.nick" placeholder="请输入身份证姓名" placeholder-color="#999" border="none"
class="input" input-align="right" />
</view>
<!-- 身份证号输入 -->
<view class="form-item">
<view class="label">身份证号</view>
<u-input v-model="formData.idenNumber" placeholder="请输入18位身份证号" placeholder-color="#999" border="none"
class="input" input-align="right" maxlength="18" />
</view>
</view>
<!-- 身份证照片区域 -->
<view class="section">
<view class="section-title">身份证照片</view>
<!-- 上传身份证正面 -->
<view class="upload-item">
<view @click="uploadFImg(1)" class="upload-area bordered-area">
<view v-if="!formData.identityA" class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证正面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-else class="uploaded-placeholder">
<image :src="Service.GetMateUrlByImg(formData.identityA)" style="width: 100%; height: 100%;" mode=""></image>
</view>
</view>
</view>
<!-- 上传身份证反面 -->
<view class="upload-item">
<view @click="uploadFImg(2)" class="upload-area bordered-area">
<view v-if="!formData.identityB" class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证反面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-else class="uploaded-placeholder">
<image :src="Service.GetMateUrlByImg(formData.identityB)" style="width: 100%; height: 100%;" mode=""></image>
</view>
</view>
</view>
</view>
<!-- 协议勾选 -->
<view class="agreement-section">
<u-checkbox v-model="formData.agreed" shape="circle" class="checkbox" />
<text class="agreement-text">我已阅读并同意</text>
<text class="agreement-link" @click="handleAgreementClick">《骑手实名认证协议》</text>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<u-button @click="save()" type="primary" class="submit-btn" size="large">
提交认证
</u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, computed } from 'vue';
import { CNRiderDataService , Service } from "@/Service/CN/CNRiderDataService"
let isLoading = ref(true)
// 表单数据
const formData = ref<any>({});
onLoad(() => {
getData()
})
const getData = () => {
CNRiderDataService.GetRiderInfo().then(res => {
isLoading.value = false
if(res.data){
formData.value=res.data.riderInfo
}
})
}
const uploadFImg = (index:number) => {
uni.chooseImage({
count: 1, // 最多选择3张图片
sizeType: ['original', 'compressed'], // 支持原图和压缩图
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
success: function (res) {
let path = res.tempFiles[0].path
Service.uploadH5(path, 'Avatar', data => {
if(index==1){
formData.value.identityA=data
}else{
formData.value.identityB=data
}
})
},
fail: function (err) {
console.error('选择失败:', err.errMsg);
}
})
}
// 处理协议点击
const handleAgreementClick = () => {
// 打开协议详情
console.log('打开协议详情');
};
const save=()=>{
// if(!formData.value.agreed){
// Service.Msg('请同意骑手实名认证协议')
// return
// }
if(!formData.value.nick){
Service.Msg('请输入姓名')
return
}
if(!formData.value.idenNumber){
Service.Msg('请输入身份证号')
return
}
CNRiderDataService.UpdateRiderIdentity(formData.value.nick,formData.value.idenNumber,formData.value.identityA,formData.value.identityB).then(res=>{
if(res.data){
Service.Msg('上传成功!')
}else{
Service.Msg(res.msg)
}
})
}
</script>
<style scoped lang="scss">
page {
background-color: #f5f5f5;
}
/* 骨架屏样式 */
.skeleton-container {
padding: 0 30rpx;
}
.skeleton-top-tip {
height: 70rpx;
width: 100%;
background-color: #e6f7ff;
margin: 20rpx 0;
border-radius: 10rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-section {
margin-bottom: 40rpx;
}
.skeleton-title {
height: 32rpx;
width: 160rpx;
background-color: #e6e6e6;
border-radius: 16rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-form-item {
display: flex;
align-items: center;
background-color: #fff;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.skeleton-form-item:last-child {
border-bottom: none;
}
.skeleton-label {
height: 28rpx;
width: 120rpx;
background-color: #e6e6e6;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-input {
height: 28rpx;
width: calc(100% - 140rpx);
background-color: #e6e6e6;
margin-left: 20rpx;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-upload-item {
height: 200rpx;
width: 100%;
background-color: #fff;
border: 2rpx solid #000;
border-radius: 20rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-face-area {
height: 240rpx;
background-color: #fff;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.skeleton-face-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #e6e6e6;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-face-text {
height: 28rpx;
width: 200rpx;
background-color: #e6e6e6;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-agreement {
height: 30rpx;
width: 60%;
background-color: #e6e6e6;
border-radius: 15rpx;
margin-bottom: 40rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-submit {
height: 92rpx;
width: 100%;
background-color: #e6e6e6;
border-radius: 46rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.top-tip {
background-color: #E6F7FF;
padding: 20rpx 30rpx;
margin: 20rpx;
border-radius: 20rpx;
}
.tip-text {
color: #1890ff;
font-size: 28rpx;
}
.form-content {
padding: 0 30rpx;
}
.section {
margin-bottom: 40rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 10rpx;
}
.form-item {
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.label {
width: 120rpx;
font-size: 28rpx;
color: #333;
}
.input {
flex: 1;
font-size: 28rpx;
}
.upload-item {
margin-bottom: 20rpx;
}
.upload-area {
height: 350rpx;
border-radius: 8rpx;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
}
.bordered-area {
background-color: #fff;
border-radius: 20rpx;
width: 100%;
}
.upload-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
z-index: 1;
}
.upload-icon {
font-size: 60rpx;
color: #1890ff;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #1890ff;
}
.uploaded-placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
color: #333;
font-size: 28rpx;
}
.face-verify-area {
background-color: #fff;
height: 240rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 8rpx;
}
.face-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #e6f7ff;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
}
.face-text {
font-size: 28rpx;
color: #1890ff;
font-weight: 500;
}
.agreement-section {
display: flex;
align-items: center;
margin-bottom: 40rpx;
padding: 0 10rpx;
}
.checkbox {
margin-right: 10rpx;
}
.agreement-text {
font-size: 26rpx;
color: #666;
}
.agreement-link {
font-size: 26rpx;
color: #1890ff;
margin-left: 4rpx;
}
.input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.submit-section {
margin-top: 60rpx;
padding: 0 0rpx;
}
.submit-btn {
height: 92rpx;
font-size: 32rpx;
border-radius: 46rpx;
background-color: #1890ff;
}
.submit-btn[disabled] {
background-color: #a0cfff;
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,13 @@
// import { createPinia, defineStore } from "pinia";
// import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// export class StoreAssist{
// private pinia:any=createPinia();
// constructor() {
// this.pinia.use(piniaPluginPersistedstate);
// }
// }

View File

@@ -0,0 +1,421 @@
<template>
<view v-if="loading" class="">
<view class="tab-bar-skeleton">
<view class="skeleton-slot skeleton-tab-item"></view>
<view class="skeleton-slot skeleton-tab-item"></view>
</view>
<view class="skeleton-container">
<view class="skeleton-order-item" v-for="index in 3" :key="'skeleton-' + index">
<!-- 订单头部骨架 -->
<view class="skeleton-header">
<view class="skeleton-slot skeleton-order-number"></view>
<view class="skeleton-slot skeleton-status-badge"></view>
</view>
<!-- 异常类型骨架 -->
<view class="skeleton-slot skeleton-type"></view>
<!-- 异常描述骨架 -->
<view class="skeleton-slot skeleton-description"></view>
<!-- 底部信息骨架 -->
<view class="skeleton-footer">
<view class="skeleton-slot skeleton-time"></view>
<view class="skeleton-actions">
<view class="skeleton-slot skeleton-btn"></view>
<view class="skeleton-slot skeleton-btn"></view>
</view>
</view>
</view>
<view class="" style="width: 100vw; display: flex; justify-content: center; " >
<view class="skeleton-slot " style="width: 160rpx; height: 30rpx; " ></view>
</view>
</view>
</view>
<view v-else class="abnormal-orders-page">
<!-- 选项卡 -->
<view class="tab-bar">
<view class="tab-item" :class="{ active: activeTab === 'pending' }" @click="switchTab('pending')">
<text class="tab-text" :class="{ active: activeTab === 'pending' }">待处理</text>
</view>
<view class="tab-item" :class="{ active: activeTab === 'processed' }" @click="switchTab('processed')">
<text class="tab-text" :class="{ active: activeTab === 'processed' }">已处理</text>
</view>
</view>
<!-- 订单列表 -->
<view class="order-list">
<view class="order-item" v-for="(orderItem,orederIndex) in 3" :key="orederIndex">
<!-- 订单头部 -->
<view class="order-header">
<text class="order-number">订单号: MT20251017123456</text>
<view class="order-status pending">
<text class="status-text">待处理</text>
</view>
</view>
<!-- 订单内容 -->
<view class="">
<text class="abnormal-type">配送超时</text>
<text class="abnormal-description">订单配送时间超过预估时间25分钟</text>
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<text class="order-time">今天14:30</text>
<!-- 操作按钮 -->
<view class="order-actions">
<text @click="Service.GoPage('/pages/my/abnormalDetail')" class="action-btn detail-btn">查看详情</text>
<text @click="Service.GoPage('/pages/order/abnormal')" class="action-btn report-btn">
重新上报
</text>
</view>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, computed } from 'vue';
import { Service } from "@/Service/Service"
let loading = ref(true)
// 响应式数据
const activeTab = ref<'pending' | 'processed'>('pending');
let status=ref('nomore')
onLoad(() => {
setTimeout(()=>{
loading.value=false
},1000)
})
// 切换选项卡
const switchTab = (tab : 'pending' | 'processed') => {
activeTab.value = tab;
};
// 重新上报
const reportAgain = (orderNo : string) => {
uni.showModal({
title: '重新上报',
content: '确定要重新上报此订单异常吗?',
success: (res) => {
if (res.confirm) {
// 模拟重新上报
uni.showToast({
title: '已提交重新上报申请',
icon: 'success'
});
}
}
});
};
</script>
<style scoped lang="scss">
/* 页面基础样式 */
.abnormal-orders-page {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 顶部导航栏 */
.nav-bar {
height: 100rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
padding: 0 30rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
position: sticky;
top: 0;
z-index: 100;
}
.nav-left,
.nav-right {
width: 60rpx;
display: flex;
justify-content: center;
align-items: center;
}
.nav-center {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.nav-title {
font-size: 34rpx;
color: #333;
font-weight: 500;
}
/* 选项卡 */
.tab-bar {
height: 88rpx;
display: flex;
background-color: #fff;
margin-bottom: 20rpx;
}
.tab-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.tab-item.active::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 50rpx;
height: 6rpx;
background-color: #1890ff;
border-radius: 3rpx;
}
.tab-text {
font-size: 28rpx;
color: #666;
}
.tab-text.active {
color: #1890ff;
font-weight: 500;
}
/* 订单列表 */
.order-list {
padding: 0 20rpx;
}
.order-item {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 订单头部 */
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.order-number {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.order-status {
padding: 4rpx 16rpx;
border-radius: 20rpx;
}
.order-status.pending {
background-color: #fff2e8;
}
.order-status.processed {
background-color: #f6ffed;
}
.status-text {
font-size: 24rpx;
}
.order-status.pending .status-text {
color: #fa5151;
}
.order-status.processed .status-text {
color: #52c41a;
}
.abnormal-type {
font-size: 28rpx;
color: #333;
font-weight: 500;
display: block;
margin-bottom: 12rpx;
}
.abnormal-description {
font-size: 26rpx;
color: #666;
display: block;
margin-bottom: 12rpx;
line-height: 1.5;
}
.order-time {
font-size: 24rpx;
color: #999;
}
/* 操作按钮 */
.order-actions {
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.action-btn {
font-size: 26rpx;
padding: 8rpx 20rpx;
}
.detail-btn {
color: #1890ff;
}
.report-btn {
color: #52c41a;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
/* 骨架屏样式 - 纯CSS实现 */
/* 选项卡骨架屏 */
.tab-bar-skeleton {
height: 88rpx;
display: flex;
background-color: #fff;
margin-bottom: 20rpx;
padding: 0 40rpx;
}
.skeleton-tab-item {
flex: 1;
height: 36rpx;
align-self: center;
margin: 0 20rpx;
border-radius: 18rpx;
}
/* 订单列表骨架屏 */
.skeleton-container {
padding: 0 20rpx;
}
/* 订单项骨架 */
.skeleton-order-item {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
/* 订单头部布局 */
.skeleton-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
/* 底部布局 */
.skeleton-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
/* 操作按钮区域 */
.skeleton-actions {
display: flex;
gap: 20rpx;
}
/* 通用骨架占位元素 */
.skeleton-slot {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite ease-in-out;
border-radius: 8rpx;
}
/* 特定元素尺寸 */
.skeleton-order-number {
width: 60%;
height: 36rpx;
}
.skeleton-status-badge {
width: 120rpx;
height: 40rpx;
border-radius: 20rpx;
}
.skeleton-type {
width: 30%;
height: 36rpx;
margin-bottom: 16rpx;
}
.skeleton-description {
width: 100%;
height: 32rpx;
margin-bottom: 16rpx;
}
.skeleton-time {
width: 25%;
height: 30rpx;
}
.skeleton-btn {
width: 100rpx;
height: 30rpx;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
</style>

View File

@@ -0,0 +1,261 @@
{
"easycom": {
// 注意一定要放在custom里否则无效https://ask.dcloud.net.cn/question/131175
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "门店运营",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/task",
"style": {
"navigationBarTitleText": "任务",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/income",
"style": {
"navigationBarTitleText": "我的收入",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
},
{
"path": "pages/index/user",
"style": {
"navigationBarTitleText": "我的",
"navigationBarBackgroundColor": "#36394D",
"navigationStyle": "custom",
"backgroundColor": "#F8F8F8"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "v派商家",
"navigationBarBackgroundColor": "#fff",
"backgroundColor": "#000"
},
"subPackages": [
{
"root": "pages/order",
"pages": [
{
"path": "orderDetail",
"style": {
"navigationBarTitleText": "项目中心",
"navigationStyle": "custom"
}
},
{
"path" : "navigation",
"style" :
{
"navigationBarTitleText" : "导航",
"navigationStyle": "custom"
}
},
{
"path" : "abnormal",
"style" :
{
"navigationBarTitleText" : "上报异常",
"navigationStyle": "custom"
}
},
{
"path" : "grabOrder",
"style" :
{
"navigationBarTitleText" : "订单详情"
}
},
{
"path" : "incomeDetail",
"style" :
{
"navigationBarTitleText" : "收入详情",
"navigationStyle": "custom"
}
},
{
"path" : "withdraw",
"style" :
{
"navigationBarTitleText" : "提现申请"
}
},
{
"path" : "finish",
"style" :
{
"navigationBarTitleText" : "订单完成"
}
},
{
"path" : "upAbnormal",
"style" :
{
"navigationBarTitleText" : "异常上报"
}
},
{
"path": "orderMap",
"style": {
"navigationBarTitleText": "订单地图"
}
}
]
},
{
"root": "pages/my",
"pages": [{
"path" : "edit",
"style" :
{
"navigationBarTitleText" : "编辑资料"
}
},
{
"path" : "statusContro",
"style" :
{
"navigationBarTitleText" : "上线管理"
}
},
{
"path" : "myKF",
"style" :
{
"navigationBarTitleText" : "联系客服",
"navigationStyle": "custom"
}
},
{
"path" : "AbnormalList",
"style" :
{
"navigationBarTitleText" : "异常订单"
}
},
{
"path" : "check",
"style" :
{
"navigationBarTitleText" : "签到奖励"
}
},
{
"path" : "abnormalDetail",
"style" :
{
"navigationBarTitleText" : "异常详情"
}
},
{
"path" : "security",
"style" :
{
"navigationBarTitleText" : "账号与安全"
}
},
{
"path" : "authentication",
"style" :
{
"navigationBarTitleText" : "实名认证"
}
},
{
"path" : "login",
"style" :
{
"navigationBarTitleText" : "登录",
"navigationStyle": "custom"
}
},
{
"path" : "noticeList",
"style" :
{
"navigationBarTitleText" : "消息通知",
"navigationStyle": "custom"
}
},
{
"path" : "setConnect",
"style" :
{
"navigationBarTitleText" : "紧急联系人"
}
},
{
"path" : "evaluate",
"style" :
{
"navigationBarTitleText" : "评价中心"
}
},
{
"path" : "completeData",
"style" :
{
"navigationBarTitleText" : "完善信息"
}
},
{
"path" : "withDrowList",
"style" :
{
"navigationBarTitleText" : "提现列表"
}
}
]
}
],
"tabBar": {
"color": "#000",
"selectedColor": "#000",
"backgroundColor": "#FFFFFF",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tab/home.png",
"selectedIconPath": "/static/tab/homed.png",
"text": "主页"
},
{
"pagePath": "pages/index/income",
"iconPath": "static/tab/income.png",
"selectedIconPath": "static/tab/incomed.png",
"text": "收入"
},
{
"pagePath": "pages/index/user",
"iconPath": "static/tab/user.png",
"selectedIconPath": "static/tab/usered.png",
"text": "我的"
}
]
}
}

View File

@@ -0,0 +1,19 @@
export class UploadAssist {
static Upload(url: string, path: string, fromData: any) {
return new Promise(function(resolve, reject) {
uni.uploadFile({
url: url, //仅为示例,非真实的接口地址
filePath: path,
name: 'file',
formData: fromData,
success: (uploadFileRes) => {
resolve(uploadFileRes);
},
fail: (err) => {
reject(err);
},
});
})
}
}

View File

@@ -0,0 +1,22 @@
import { Service } from '@/Service/Service';
/*****腾讯云存储*****/
class NvpTencentCosService {
private static GetAuthorizationPath: string = '/TencentCos/GetAuthorization';
/*****获取云存储配置*****/
static GetAuthorization() {
var result = Service.Request(this.GetAuthorizationPath, "GET", "");
return result;
}
private static GetUpLoadInfoPath: string = '/TencentCos/GetUpLoadInfo';
/*****获取上传地址*****/
static GetUpLoadInfo(code: string, fileName: string, desire: string) {
var result = Service.Request(this.GetUpLoadInfoPath, "GET", { code, fileName, desire });
return result;
}
}
export {
Service,
NvpTencentCosService
}

View File

@@ -0,0 +1,63 @@
import {Service} from "@/Service/Service"
export class EventHandler {
//处理事件
static Events(data: any) {
var result = JSON.parse(data.data);
if (result.code == 'msg') {
this.ChatEnent(result);
} else if (result.code == 'order') {
uni.$emit('newOrder', data);
}
}
static ChatEnent(data: any) {
console.log(data,'xxx')
var eventName = `chat_${data.chanId}`;
uni.$emit(eventName, data);
}
static ChatUserEnent(data: any) {
var eventUserName = `chatUser_${data.sendId}`;
uni.$emit('UpdatePrivateMsg', data);
uni.$emit(eventUserName, data);
this.plusPush();
}
static plusPush() {
//#ifdef APP-PLUS
if(!Service.getIsHede()){
return
}
let content = '您有一条新的消息~';
let options = {
cover: false,
when: new Date(),
title: '通知消息'
};
let body = {
id: 'id',
key: 'key'
};
let payload = JSON.stringify(body);
plus.push.createMessage(content, payload, options);
//#endif
}
static SystemEnent(data: any) {
let obj = JSON.parse(data.data);
if (obj.code == 'Like' || obj.code == 'Aite' || obj.code == 'Comment' || obj.code == 'Notice') {
uni.$emit('MegEvent', obj);
} else if (obj.code == 'Off') {
uni.$emit('ImComOff', 'user');
} else if (obj.code == 'UpdateUserInfo') {
uni.$emit('UpdateUserInfo');
} else if (obj.code == 'Friend') {
uni.$emit('Friend');
}
}
static ConnectBus() {
uni.$emit('UpdateChat');
}
}

View File

@@ -0,0 +1,75 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide, onError } from '@dcloudio/uni-app'
import { ref, onUnmounted } from 'vue'
// 全局定时器引用
let globalTimer = null
// 每分钟要执行的方法
const minuteTask = () => {
console.log('每分钟执行一次的任务', new Date().toLocaleTimeString())
}
// 启动全局定时器
const startGlobalTimer = () => {
if (globalTimer) {
clearInterval(globalTimer)
}
// 立即执行一次
minuteTask()
// 设置每分钟执行一次60000毫秒 = 1分钟
globalTimer = setInterval(() => {
minuteTask()
}, 60000)
}
// 停止全局定时器
const stopGlobalTimer = () => {
if (globalTimer) {
clearInterval(globalTimer)
globalTimer = null
}
}
onLaunch(() => {
console.log('App Launch')
// 应用启动时开始定时器
startGlobalTimer()
})
onShow(() => {
console.log('App Show')
// 应用回到前台时重新开始定时器
startGlobalTimer()
})
onHide(() => {
console.log('App Hide')
// 应用进入后台时停止定时器,节省资源
stopGlobalTimer()
})
// Vue组件卸载时清理定时器
onUnmounted(() => {
stopGlobalTimer()
})
// 全局错误处理
onError((err) => {
console.error('App Error:', err)
})
</script>
<style lang="scss">
@import "uview-plus/index.scss";
@import "colorui/main.css";
@import "colorui/icon.css";
page {
--nav-mian: #1890FF; //全局颜色
--nav-vice: #52C41A; //副颜色
--nav-diluted: #FF4D4F; //次颜色
}
</style>

View File

@@ -0,0 +1,511 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 余额区域骨架屏 -->
<view class="skeleton-balance-section">
<div class="skeleton-balance-label"></div>
<div class="skeleton-balance-amount"></div>
<div class="skeleton-balance-current"></div>
</view>
<!-- 账户选择区域骨架屏 -->
<view class="skeleton-account-section">
<div class="skeleton-section-title"></div>
<div class="skeleton-account-item">
<div class="skeleton-account-icon"></div>
<div class="skeleton-account-name"></div>
<div class="skeleton-account-radio"></div>
</div>
<div class="skeleton-account-item">
<div class="skeleton-account-icon"></div>
<div class="skeleton-account-name"></div>
<div class="skeleton-account-radio"></div>
</div>
</view>
<!-- 提示信息骨架屏 -->
<view class="skeleton-tip-section">
<div class="skeleton-tip-icon"></div>
<div class="skeleton-tip-content">
<div class="skeleton-tip-line"></div>
<div class="skeleton-tip-line-small"></div>
</div>
</view>
<!-- 确认按钮骨架屏 -->
<view class="skeleton-confirm-section">
<div class="skeleton-confirm-button"></div>
</view>
</view>
<!-- 真实内容 -->
<view v-else class="withdraw-container">
<!-- 可提现金额 -->
<view class="balance-section">
<text class="balance-label">可提现金额</text>
<text class="balance-amount">¥86.50</text>
<text class="current-balance">当前钱包余额</text>
</view>
<!-- 提现账户选择 -->
<view class="account-section">
<text class="section-title">提现账户</text>
<view class=""
style="display: flex;align-items: center; border-bottom: 4rpx solid #e2e2e2;padding-bottom: 10px; margin-bottom: 10px;">
<up-icon name="zhifubao-circle-fill" size="30" color="#1890ff"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
支付宝
</view>
<view @click="changePay(0)" class="">
<view v-if="current==0" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
<view class="" style="display: flex;align-items: center;">
<up-icon name="weixin-circle-fill" size="30" color="#28C445"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
微信
</view>
<view @click="changePay(1)" class="">
<view v-if="current==1" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
</view>
<!-- 提示信息 -->
<view class="tip-section">
<up-icon name="clock" size="18" color="#1890ff"></up-icon>
<view class="" style="margin-left: 10rpx;">
<view class="tip-text">预计 T+1 工作日到账</view>
<view class="tip-text" style="color: #666666; font-size: 24rpx; margin-top: 10rpx; ">无手续费</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="confirm-section">
<button class="confirm-button" @click="confirmWithdraw">确认提现 ¥86.50</button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
let current = ref(null)
let loading=ref(true)
onLoad(() => {
setTimeout(()=>{
loading.value=false
},1000)
})
const changePay = (item : any) => {
current.value = item
}
// 确认提现
const confirmWithdraw = () => {
// 这里可以添加提现逻辑
uni.showToast({
title: '提现申请已提交',
icon: 'success'
});
};
</script>
<style scoped>
.withdraw-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 选择 */
.radio-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid var(--nav-mian);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.radio-inner {
width: 20rpx;
height: 20rpx;
background-color: var(--nav-mian);
border-radius: 50%;
}
.radio-no-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #dadada;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #fff;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #000;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.help-icon {
width: 40rpx;
height: 40rpx;
}
/* 余额显示区域 */
.balance-section {
background-color: #fff;
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
.balance-label {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 60rpx;
font-weight: bold;
color: #ff4757;
display: block;
margin-bottom: 10rpx;
}
.current-balance {
font-size: 28rpx;
color: #999;
}
/* 账户选择区域 */
.account-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 20rpx;
}
.account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.account-item:last-child {
border-bottom: none;
}
.account-info {
display: flex;
align-items: center;
}
.account-icon {
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
background-color: #1677ff;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
border: 2rpx solid #000;
}
.account-icon.wechat {
background-color: #07c160;
}
.icon-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.account-name {
font-size: 32rpx;
color: #333;
}
/* 提示信息区域 */
.tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.tip-text {
font-size: 28rpx;
margin-left: 10rpx;
}
/* 确认按钮区域 */
.confirm-section {
background-color: #fff;
position: fixed;
width: 100vw;
left: 0;
bottom: 0;
padding: 30rpx;
}
.confirm-button {
width: 100%;
height: 90rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
display: flex;
align-items: center;
justify-content: center;
border: none;
}
.confirm-button:active {
background-color: #0958d9;
}
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
/* 骨架屏通用样式 */
.skeleton-balance-section,
.skeleton-account-section,
.skeleton-tip-section {
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
/* 余额区域骨架屏 */
.skeleton-balance-section {
background-color: #fff;
}
.skeleton-balance-label {
width: 30%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-amount {
width: 40%;
height: 60rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-current {
width: 25%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 账户选择区域骨架屏 */
.skeleton-account-section {
background-color: #fff;
}
.skeleton-section-title {
width: 25%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 30rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 4rpx solid #e2e2e2;
}
.skeleton-account-item:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 0;
}
.skeleton-account-icon {
width: 60rpx;
height: 60rpx;
background-color: #e6e6e6;
border-radius: 12rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-name {
flex: 1;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-radio {
width: 36rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 50%;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 提示信息骨架屏 */
.skeleton-tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.skeleton-tip-icon {
width: 40rpx;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 50%;
margin-right: 15rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-content {
flex: 1;
}
.skeleton-tip-line {
width: 70%;
height: 28rpx;
background-color: #e6e6e6;
margin-bottom: 15rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-line-small {
width: 50%;
height: 24rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 确认按钮骨架屏 */
.skeleton-confirm-section {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
padding: 30rpx;
}
.skeleton-confirm-button {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
</style>

View File

@@ -0,0 +1,335 @@
<template>
<view class="review-management-page">
<!-- 全新方案:纯 CSS 手动构建的骨架屏 -->
<view v-if="loading" class="skeleton-wrapper">
<view class="skeleton-card" style="height: 150rpx;"></view>
<view class="skeleton-tabs">
<view class="skeleton-item skeleton-text" style="width: 100%; height: 44rpx;"></view>
</view>
<view v-for="i in 3" :key="i" class="skeleton-card">
<view class="skeleton-item skeleton-text" style="width: 100%; height: 300rpx;"></view>
</view>
</view>
<!-- 页面实际内容 -->
<view v-else class="page-content" >
<!-- 1. 数据看板 -->
<view class="summary-card">
<view class="data-item">
<view class="value"><text class="score">{{ satisfaction }}%</text><up-icon name="star-fill"
color="#ff9900" size="20"></up-icon></view>
<text class="label">满意度</text>
</view>
<view class="data-item">
<text class="value">{{total}}</text>
<text class="label">总评价数</text>
</view>
</view>
<!-- 2. Tabs 切换 -->
<view class="sticky-tabs-wrapper">
<up-tabs :list="reviewTabs" :current="currentTab" @change="onTabChange" lineColor="#fa6400"
:activeStyle="{ color: '#fa6400', fontWeight: 'bold' }" :inactiveStyle="{ color: '#666' }">
</up-tabs>
</view>
<view class="review-list">
<view class="review-card" v-for="review in reviewList" :key="review">
<view class="reviewer-info">
<text class="name" style="font-weight: bold;" >{{ review.nick }}</text>
<up-rate v-model="review.source" readonly activeColor="#ff9900" size="16"></up-rate>
<text class="time">{{ Service.formatDate(review.addTime,1) }}</text>
</view>
<text class="review-text">{{ review.sign }}</text>
</view>
<up-loadmore status="nomore" nomoreText="没有更多评价了"></up-loadmore>
<view class="" style="width: 100%; height: 20rpx;" >
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { onLoad, onReachBottom, onShow } from '@dcloudio/uni-app';
import { Service } from "@/Service/Service";
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
const loading = ref<boolean>(true);
const currentTab = ref<number>(0);
let satisfaction = ref(0)
let status = ref('nomore')
let page = ref(1)
let review = ref('')
let storeInfo = ref<any>()
let total = ref(0)
const reviewTabs = ref([
{ name: '全部' }, { name: '好评' }, { name: '差评' }
]);
const reviewList = ref<Array<any>>([]);
let currentId = ref('')
onLoad(() => {
getData()
});
onShow(() => { });
onReachBottom(()=>{
getList()
})
// 获取数据
const getData = () => {
status.value = 'loadmore'
page.value = 1
reviewList.value = []
getList()
}
const getList = () => {
if (status.value == 'loading' || status.value == 'nomore') {
return
}
status.value = 'loading'
CNRiderDataService.GetRiderEvaluate(currentTab.value, page.value).then(res => {
loading.value = false
if (res.data) {
satisfaction.value = res.data.satisfaction
total.value=res.data.total
reviewList.value = [...reviewList.value, ...res.data.evaluateList]
page.value++
status.value = res.data.evaluateList == 10 ? 'loadmore' : 'nomore'
}
})
}
const onTabChange = (e : { index : number }) => {
currentTab.value = e.index;
getData()
};
const replyTo = () => {
if (review.value == '') {
return
}
};
</script>
<style lang="scss" scoped>
page{
background-color: #f6f6f6;
}
@keyframes skeleton-blink {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
.skeleton-item {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-blink 1.5s infinite linear;
}
.skeleton-rect {
border-radius: 12rpx;
}
.skeleton-text {
border-radius: 4rpx;
}
.skeleton-wrapper {
padding: 24rpx;
background-color: #f7f7f7;
.skeleton-tabs {
padding: 20rpx 0;
background-color: #fff;
}
.skeleton-card {
background-color: #fff;
padding: 30rpx;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
}
.review-management-page {
}
.summary-card {
display: flex;
justify-content: space-around;
text-align: center;
background-color: #fff8f5;
border-radius: 16rpx;
padding: 30rpx;
margin: 24rpx;
.data-item {
.value {
font-size: 40rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
}
.label {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
.text-red {
color: #fa5151;
}
}
}
.sticky-tabs-wrapper {
position: sticky;
top: 0;
z-index: 10;
background-color: #fff;
:deep(.up-tabs__wrapper__nav__item) {
flex: 1;
}
:deep(.up-tabs__wrapper__nav) {
position: relative;
.unread-badge {
position: absolute;
top: 16rpx;
right: 20rpx;
background-color: #fa5151;
color: #fff;
font-size: 20rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
border-radius: 16rpx;
text-align: center;
padding: 0 8rpx;
}
}
}
.review-list {
padding: 24rpx;
.review-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
&.bad-review {
border: 1rpx solid #fef0f0;
}
.reviewer-info {
display: flex;
align-items: center;
gap: 16rpx;
.name {
font-size: 30rpx;
font-weight: 500;
}
.time {
font-size: 24rpx;
color: #999;
margin-left: auto;
}
}
.review-text {
font-size: 28rpx;
color: #333;
margin: 20rpx 0;
display: block;
line-height: 1.6;
}
.review-images {
display: flex;
gap: 16rpx;
.image-placeholder {
width: 160rpx;
height: 160rpx;
overflow: hidden;
border-radius: 8rpx;
}
}
.merchant-reply {
display: flex;
align-items: center;
gap: 12rpx;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx;
margin-top: 20rpx;
font-size: 26rpx;
.reply-label {
color: #fa6400;
font-weight: 500;
}
.reply-text {
color: #666;
}
}
.reply-action {
text-align: right;
margin-top: 20rpx;
text {
display: inline-block;
border: 1rpx solid #fa6400;
color: #fa6400;
font-size: 26rpx;
padding: 8rpx 24rpx;
border-radius: 30rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,81 @@
{
"id": "qf-image-cropper",
"displayName": "图片裁剪插件",
"version": "2.2.5",
"description": "图片裁剪插件,支持自定义尺寸、定点等比例缩放、拖动、图片翻转、剪切圆形/圆角图片、定制样式,功能多性能高体验好注释全。",
"keywords": [
"qf-image-cropper",
"图片裁剪",
"图片编辑",
"头像裁剪",
"小程序"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "y",
"阿里": "n",
"百度": "n",
"字节跳动": "n",
"QQ": "u",
"钉钉": "n",
"快手": "n",
"飞书": "n",
"京东": "n"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@@ -0,0 +1,544 @@
<template>
<view v-if="loading" class="skeleton-container">
<!-- 收入概览区域骨架 -->
<view class="income-container" style="padding-top: 80rpx">
<view class="income-overview">
<view class="" style="display: flex; align-items: center; justify-content: space-between">
<view class="">
<view class="skeleton-income-title"></view>
<view class="" style="display: flex; align-items: center">
<view class="skeleton-income-amount"></view>
</view>
</view>
<view class="skeleton-withdraw-button"></view>
</view>
</view>
<!-- 收入明细区域骨架 -->
<view class="detail-header">
<view class="skeleton-detail-title"></view>
</view>
<!-- 时间选择标签骨架 -->
<view class="time-tabs">
<view class="skeleton-tab-item" v-for="(item, index) in 4" :key="index"></view>
</view>
<!-- 明细列表骨架 -->
<view class="detail-list" v-for="(item, index) in 3" :key="index">
<view class="detail-content">
<view class="skeleton-icon-placeholder"></view>
<view class="detail-info">
<view class="skeleton-order-id"></view>
<view class="skeleton-order-time"></view>
</view>
<view class="">
<view class="skeleton-order-amount"></view>
<view class="skeleton-order-balance"></view>
</view>
</view>
</view>
<!-- 加载更多骨架 -->
<view class="skeleton-load-more"></view>
</view>
</view>
<!-- 收入概览区域 -->
<view v-else class="income-container" style="padding-top: 80rpx">
<view v-if="!istrought" class="">
<view class="income-overview">
<view class="" style="display: flex; align-items: center; justify-content: space-between">
<view class="">
<text class="income-title">账户余额</text>
<view class="" style="display: flex; align-items: center">
<text class="income-amount">¥{{ riderAcc.account }}</text>
</view>
</view>
<u-button class="withdraw-button" @click="Service.GoPage('/pages/order/withdraw')"
type="primary">立即提现</u-button>
</view>
</view>
<!-- 收入明细区域 -->
<view class="detail-header">
<text class="detail-title">收支明细</text>
</view>
<!-- 时间选择标签 -->
<view class="time-tabs">
<text v-for="(item, index) in timeList" :key="index" @click="changeTab(index)"
:class="{ active: currentTime == index }" class="tab-item">{{
item
}}</text>
</view>
<view class="detail-list" v-for="(item, index) in accList" :key="index">
<view class="detail-content">
<view class="icon-placeholder">
<image :src="Service.GetIconImg('/static/index/income/order.png')"
style="width: 55rpx; height: 55rpx" mode=""></image>
</view>
<view class="detail-info">
<text class="order-id">{{ item.name }}</text>
<text class="order-time">{{ Service.formatDate(item.addTime, 1) }}</text>
</view>
<view class="" style="">
<view class="order-amount" style="text-align: right">
{{ item.code == '收入' ? '+' : '-' }}{{ item.amount }}
</view>
<view class="order-time"> 账户余额 {{ item.balance }} </view>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
</view>
<view v-if="istrought" style=" margin-top: 20rpx; text-align: center; font-weight: bold; font-size: 34rpx;"
class="">
信息审核中·请等待审核
</view>
<calender ref="calendar" :range="true" :insert="false" @confirm="dataConfirm" />
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app'
import { ref, onMounted } from 'vue'
import * as echarts from 'echarts'
import { Service } from '@/Service/Service'
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
import calender from '@/uni_modules/uni-calendar/components/uni-calendar/uni-calendar'
let loading = ref(true)
let calendar = ref(null)
let timeList = ref(['今日', '本周', '本月', '自定义'])
let currentTime = ref(0)
let status = ref('nomore')
let page = ref(1)
let riderAcc = ref<any>({})
let accList = ref<Array<any>>([])
let timeString = ref('')
let istrought = ref(false)
onLoad(() => { })
onShow(() => {
getData()
getIncome()
})
onMounted(() => { })
const getData = () => {
CNRiderDataService.GetRiderAccInfo().then((res) => {
loading.value = false
if (res.code == 0) {
riderAcc.value = res.data.riderAcc
istrought.value = false
}
else if (res.code == 1008) {
istrought.value = true
}
else {
Service.Msg(res.msg)
}
})
}
// 收入列表
const getIncome = () => {
status.value = 'loadmore'
page.value = 1
accList.value = []
getIncomeList()
}
const getIncomeList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderAccLog(
currentTime.value == 0 ? '' : currentTime.value == 3 ? timeString.value : String(currentTime.value),
page.value
).then((res) => {
accList.value = [...accList.value, ...res.data.accLog]
status.value = res.data.accLog == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
const changeTab = (index : number) => {
currentTime.value = index
if (index == 3) {
calendar.value.open()
return
}
getIncome()
}
const dataConfirm = (e) => {
timeString.value = e.range.data[0] + '_' + e.range.data.slice(-1)
if (e.range.data.length == 0) {
timeString.value = e.fulldate
getIncome()
return
}
getIncome()
}
</script>
<style scoped>
.income-container {
padding: 20rpx;
}
/* 收入概览区域 */
.income-overview {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.income-title {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.income-amount {
font-size: 48rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 时间标签 */
.time-tabs {
display: flex;
margin-bottom: 10rpx;
}
.tab-item {
font-size: 28rpx;
color: #666;
margin-right: 40rpx;
padding-bottom: 10rpx;
}
.tab-item.active {
color: #1890ff;
border-bottom: 3rpx solid #1890ff;
}
.month-total {
font-size: 30rpx;
color: #666;
}
/* 收入构成区域 */
.income-composition {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 饼图占位 */
.pie-chart-placeholder {
display: flex;
align-items: center;
}
.pie-chart {
width: 200rpx;
height: 200rpx;
margin-right: 30rpx;
position: relative;
overflow: hidden;
}
.chart-legend {
flex: 1;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.legend-dot.blue {
background-color: var(--nav-mian);
}
.legend-dot.orange {
background-color: var(--nav-vice);
}
.legend-dot.green {
background-color: var(--nav-diluted);
}
.legend-text {
font-size: 26rpx;
color: #666;
}
/* 钱包区域 */
.wallet-section {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.wallet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.wallet-title {
font-size: 28rpx;
color: #333;
}
.wallet-amount {
font-size: 36rpx;
font-weight: bold;
color: #ff4d4f;
}
.withdraw-button {
margin: 0;
width: fit-content;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
border-radius: 40rpx;
background-color: #1890ff;
color: #fff;
}
.withdraw-tip {
display: block;
font-size: 22rpx;
color: #999;
text-align: center;
}
/* 收入明细区域 */
.income-detail {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
}
.detail-header {
margin-bottom: 20rpx;
}
.detail-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.detail-content {
display: flex;
align-items: center;
}
.detail-list {
margin: 15rpx 0 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-radius: 20rpx;
}
/* 图标占位符 - 黑边白底 */
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #e6f7ff;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.detail-info {
flex: 1;
}
.order-id {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.order-time {
font-size: 24rpx;
color: #999;
}
.order-amount {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 没有更多记录 */
.no-more {
text-align: center;
padding: 40rpx 0;
}
.no-more-text {
font-size: 24rpx;
color: #999;
}
/* 骨架屏样式 */
.skeleton-container {
width: 100%;
min-height: 100vh;
background-color: #f5f5f5;
}
/* 收入概览骨架 */
.skeleton-income-title {
width: 120rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
margin-bottom: 10rpx;
}
.skeleton-income-amount {
width: 240rpx;
height: 48rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-withdraw-button {
width: 160rpx;
height: 60rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 30rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 明细标题骨架 */
.skeleton-detail-title {
width: 150rpx;
height: 34rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 时间标签骨架 */
.skeleton-tab-item {
width: 80rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
margin-right: 40rpx;
}
/* 明细列表骨架 */
.skeleton-icon-placeholder {
width: 70rpx;
height: 70rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
margin-right: 20rpx;
}
.skeleton-order-id {
width: 200rpx;
height: 26rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
margin-bottom: 5rpx;
}
.skeleton-order-time {
width: 150rpx;
height: 24rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-order-amount {
width: 120rpx;
height: 32rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
text-align: right;
}
.skeleton-order-balance {
width: 180rpx;
height: 24rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
text-align: right;
margin-top: 5rpx;
}
/* 加载更多骨架 */
.skeleton-load-more {
height: 80rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 40rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
margin: 20rpx;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
</style>

View File

@@ -0,0 +1,355 @@
import { HttpRequest, StoreAssist, UploadAssist, ResultData } from '@/common/Common';
import { BaseConfig } from './BaseConfig';
export class Service extends BaseConfig {
// 获取是否后台
static getIsHede () {
let isHede = this.GetStorageCache('isHede')
if (isHede == null || isHede == '') {
return false;
} else {
return isHede;
}
}
//获取API地址
static ApiUrl(path : string) {
return `${this.servesUrl}${path}`;
}
//获取图片地址
static GetpayImg(path : string) {
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.payuploadUrl}${path}`;
}
}
//获取图标地址
static GetIconImg(path : string) {
return path
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.imgUrl}${path}`;
}
}
//获取图片地址
static GetMateUrlByImg(path : string) {
return path
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.imgUrl}${path}`;
}
}
//获取音视频地址
static GetMateUrlByMedia(path : string) {
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.mediaUrl}${path}`;
}
}
//获取登录账号token
static GetUserToken() {
return Service.GetStorageCache('token');
}
// 获取登录状态
static GetUserIsLogin() {
var token = this.GetUserToken();
if (token == null || token == '') {
return false;
} else {
return true;
}
}
//设置登录账户Token
static SetUserToken(token : string) {
this.SetStorageCache('token', token);
}
//清理登录账户Token
static OffUserToken() {
Service.DelStorageCache('token');
uni.$emit('ImComOff', 'user');
this.ClearUserStateData();
}
//获取登录账号状态信息
static GetUserStateData() {
return Service.GetStorageCache('StateDomain');
}
//设置当前登录账号状态信息
static SetUserStateData() {
return Service.GetStorageCache('StateDomain');
}
//清理当前登录账号状态信息
static ClearUserStateData() {
Service.DelStorageCache('StateDomain');
}
//获取当前客户端ID
static GetUserClientId() {
return this.GetStorageCache('ClientId');
}
//保存当前客户端ID
static SetUserClientId(clientId: string) {
this.SetStorageCache('ClientId', clientId);
}
//获取缓存
static GetStorageCache(key : string) {
return StoreAssist.Get(key);
}
//删除缓存
static DelStorageCache(key : string) {
StoreAssist.Delete(key);
}
//设置缓存
static SetStorageCache(key : string, data : any) {
StoreAssist.Set(key, data);
}
/*****以下是基础方法调用与拦截器*****/
static Request(url : string, method : 'GET' | 'POST' | 'PUT' | undefined, data : object | any) {
const token = Service.GetUserToken();
const _url = Service.ApiUrl(url);
var result = HttpRequest.RequestWithToken(_url, method, token, data).then((retResult : any) => {
if (retResult.statusCode == '200') {
var obj = retResult.data;
if (obj.code == 401) {
//过期
this.OffUserToken();
this.Msg('登录过期,请重新登录')
this.GoPage('/pages/my/login')
return Promise.reject();
} else if (obj.code == 40101) {
//失效
this.OffUserToken();
this.GoPageDelse('/pages/mine/login/login');
return Promise.reject();
} else if (obj.code == 1004) {
//资源不存在
this.GoPageDelse('/pages/AppSet/404/404');
return Promise.reject();
// return new ResultData(-1, '', '');
} else if (obj.code == 40188) {
//无权限
this.GoPageDelse('/pages/AppSet/40188/40188');
return Promise.reject();
// return new ResultData(-1, '', '');
} else if (obj.code == 1008) {
//业务提示
return new ResultData(obj.code, obj.msg, obj.data);
} else {
return new ResultData(obj.code, obj.msg, obj.data);
}
} else {
return new ResultData(-1, '', '');
}
});
return result;
}
/*****以下是腾讯云oss上传*****/
static UpLoadMedia(code : string, fileName : string, desire : string, path : string) {
var result = this.Request(this.uploadUrl, 'GET', { code, fileName, desire }).then((retResult) => {
if (retResult.code == 0) {
var upOk = UploadAssist.Upload(retResult.data.url, path, retResult.data.cosData).then((upRet : any) => {
if (upRet.statusCode == 200) {
const retData : any = { code: retResult.data.code, file: retResult.data.file, cache: retResult.data.cache };
return new ResultData(0, '上传成功!', retData);
} else {
this.Msg('上传失败!');
return new ResultData(-1, '', '');
}
});
return upOk;
} else {
this.Msg('上传失败!');
return new ResultData(-1, retResult.msg,retResult.data);
}
});
return result;
}
/***********消息操作**************/
static Msg(message : any, icon ?: any) : void {
if (icon != null) {
uni.showToast({
title: message,
icon: icon
});
} else {
uni.showToast({
title: message,
icon: 'none'
});
}
}
static Alert(msg : string, cb ?: any) {
uni.showModal({
title: '提示',
content: msg,
showCancel: false,
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
cb && cb();
}
}
})
}
static LoadIng(text : any) : void {
uni.showLoading({
title: text,
icon: 'none'
});
}
static LoadClose() : void {
uni.hideLoading();
}
/**********跳转操作*********/
static GoPageTab(path : string) : void {
uni.switchTab({
url: path
});
}
/**********跳转操作*********/
static GoPage(path : string) : void {
uni.navigateTo({
url: path, //跳转的页面
success: function (res) {
// 通过eventChannel向被打开页面传送数据
}
});
}
/**********跳转并删除当前页面操作*********/
static GoPageDelse(path : string) : void {
uni.redirectTo({
url: path //跳转的页面
});
}
/**********返回上一页*********/
static GoPageBack() : void {
uni.navigateBack({ delta: 1 });
}
/*****获取图片base64*****/
static UpLoadMediaBase64(path : string) {
return new Promise(function (resolve, reject) {
uni.uploadFile({
url: 'http://cloud.pccsh.com/DefUp/UploadFileImgBase64', //仅为示例,非真实的接口地址
filePath: path,
name: 'file',
success: (uploadFileRes) => {
resolve(uploadFileRes);
},
fail: (err) => {
reject(err);
}
});
});
}
/*****获取图片位置信息*****/
//获取时间戳
static GetTimeSpan(milliSecond : number) {
return Date.now() + milliSecond;
}
// 时间戳处理
static formatDate(time : any, type : number) : string {
const date = new Date(time);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始所以加1并用0填充
const day = String(date.getDate()).padStart(2, '0'); // 用0填充
const hours = String(date.getHours()).padStart(2, '0'); // 用0填充
const minutes = String(date.getMinutes()).padStart(2, '0'); // 用0填充
const seconds = String(date.getSeconds()).padStart(2, '0'); // 用0填充
if (type == 0) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
else if (type == 1) {
return `${year}-${month}-${day} ${hours}:${minutes}`;
} else if (type == 2) {
return `${year}-${month}-${day}`;
} else if (type == 3) {
return `${hours}:${minutes}`;
} else if (type == 4) {
return `${year}${month}${day}`;
}
else {
return `${hours}:${minutes}`;
}
}
/*****节流*****/
static throttle(fn: () => void, time: number) {
let canRun: boolean = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn(); //可以不执行
canRun = true;
}, time);
};
}
/*****防抖*****/
static debounce<T extends (...args: any[]) => void>(fn: T, time: number): (...args: Parameters<T>) => void {
let timerId: NodeJS.Timeout | null = null;
return (...args: Parameters<T>) => {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args); // 执行传入的函数
timerId = null; // 清除定时器ID
}, time);
};
}
// 普通图片上传
static uploadH5(path, dic, callback) {
console.log(this.payuploadUrl,'xxx')
uni.uploadFile({
url: this.payuploadUrl+'/Upload/Upload',
method: "POST",
header: {
'Authorization': 'Bearer ' + Service.GetUserToken(),
},
formData: {
"path": dic,
},
filePath: path,
name: 'file',
success: (data) => {
let info = data.data
callback(info)
}
})
}
}

View File

@@ -0,0 +1,242 @@
<template>
<!-- <UpApp :show="upShow" :url="url" /> -->
<view class="borybac" v-if="upShow">
<view class="up_box">
<view class="mt50">
<view class="" style="margin: 0 60rpx;" >
1.优化布局细节,优化购物体验,优化产品体验
</view>
<view class="" style="margin: 0 60rpx;" >
2.修复已知问题修复一些BUG
</view>
</view>
<view class="jdBox">
<view class="jd">
<view class="jdbfb">
{{sum}}%
</view>
<view class="jdt">
<view class="jdn" :style="'width:'+sum+'%'">
</view>
</view>
<view v-if="buttonContro" class="jddx">
{{datacl(beg)}}/{{datacl(downlog)}}
</view>
</view>
</view>
<view class="" v-if="force==0"
style="width: 100%; padding: 0 20rpx; height: 60rpx; margin: 40rpx auto; display: flex; justify-content: space-between;">
<view class="" style="width: 70rpx;">
</view>
<view class=""
style="width: 240rpx; height: 60rpx; line-height: 60rpx; border-radius: 30rpx; text-align: center; color: #fff; font-size: 24rpx; background-color: #FFD700;"
@click="delUpApp">
开始更新
</view>
<view class="" style="font-size: 22rpx; line-height: 80rpx; color: #999;" @click="goindex">
暂不更新
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
let buttonContro = ref(false)
let url = ref('')
let force = ref('0')
// 控制热更新
let upShow = ref(true)
let sum = ref(0)
let downlog = ref(0)
let beg = ref(0)
let remark = ref('')
let type = ref('')
//模拟请求
onLoad((data : any) => {
// getdata()
url.value = data.url
});
const goindex = function () {
uni.navigateBack({
delta: 1
});
}
const getdata = function () {
// RegisterService.GetNewVersion().then((res:any)=>{
// url.value = res.data.path
// downlog.value = res.data.size
// force.value = res.data.force
// remark.value = res.data.remark
// type.value = res.data.type
// if(res.data.force=='1'){
// delUpApp()
// }
// })
}
const datacl = function (e : any) {
if (e > 1024) {
let sl = ((e / 1024) / 1024).toFixed(1)
return sl + 'MB'
} else {
return (e / 1024).toFixed(1) + 'KB'
}
}
const delUpApp = function () {
if (buttonContro.value) {
return
}
buttonContro.value = true
// 1.开始请求下载
const downloadTask = uni.downloadFile({
url: url.value,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
plus.runtime.install(downloadResult.tempFilePath, {
force: false
}, function () {
uni.hideLoading()
uni.showToast({
title: "下载成功",
complete() {
if (type.value == 'Bulking') {
setTimeout(function () {
plus.runtime.restart();
}, 2000)
}
}
})
console.log('install success...');
}, function (e) {
uni.hideLoading()
console.log(e, '失败')
// uni.$u.toast('下载失败!')
// console.error('install fail...');
});
}
},
fail(downloadResult) {
console.log(downloadResult, '失败')
// console.log('下载失败');
// uni.$u.toast('下载失败')
}
});
downloadTask.onProgressUpdate((res) => {
downlog.value = res.totalBytesExpectedToWrite
beg.value = res.totalBytesWritten
sum.value = res.progress
});
}
</script>
<style lang="scss" setup>
.borybac {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: #f6f6f6;
display: flex;
overflow: hidden;
.up_box {
width: 650rpx;
height: 1000rpx;
margin: 200rpx auto;
border-radius: 20rpx;
overflow: hidden;
position: relative;
background-image: url(@/static/index/system/updata.png);
background-repeat: no-repeat;
background-size: cover;
.mt50 {
display: block;
margin-top: 570rpx;
}
.jdBox {
overflow: hidden;
margin-top: 70rpx;
display: block;
width: 100%;
padding: 0 20rpx;
.jd {
display: block;
width: 90%;
height: 100%;
margin: 0 auto;
.jdbfb {
display: block;
width: 100%;
height: 40rpx;
line-height: 40rpx;
font-size: 30rpx;
color: #FFD700;
font-weight: 600;
}
.jdt {
margin-top: 10rpx;
display: block;
width: 100%;
height: 23rpx;
border-radius: 15rpx;
background-color: #E5E5E5;
.jdn {
display: block;
height: 23rpx;
border-radius: 15rpx;
background: radial-gradient(#8370F8 0%, #455FF8 100%);
}
}
.jddx {
width: 100%;
font-size: 24rpx;
margin-top: 20rpx;
font-weight: 600;
}
}
}
}
}
.text {
display: block;
text-align: center;
margin-top: 30rpx;
width: 100%;
height: 40rpx;
font-size: 32rpx;
}
</style>

View File

@@ -0,0 +1,22 @@
## 1.1.72024-10-29
修复底部露出部分组件bug
## 1.1.62024-08-20
更新本地数据源为最新数据源
## 1.1.52024-06-12
使用说明文档优化
## 1.1.42024-06-12
增加问题反馈描述
## 1.1.32024-02-29
更新使用文档
## 1.1.22024-01-16
解决Vue3项目导入导出报错问题
## 1.1.12023-12-06
defaultValue可以传入defaultValue:['河北省','唐山市','丰南区']数组类型以及defaultValue: '420103'地区编码字符串类型
## 1.1.02023-12-05
即默认值传入地区编码,也支持传入中文省市区数组
## 1.0.92023-12-04
优化
## 1.0.82023-10-24
修复东菀市和中山市下各镇的行政编码错误问题
## 1.0.42023-09-15
改为uni_modules规范

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

View File

@@ -0,0 +1,125 @@
<template>
<view class="">
<web-view ref="webviewRef" v-if="isshow" :src="url" @message="handleMessage" @logData = "logData"></web-view>
<!-- ✅ 新增:一个绝对定位的遮罩层,用于在刷新时覆盖 web-view -->
<view v-else class="reloading-mask">
<up-loading-icon text="正在获取订单状态..." v-if="orderOver" textSize="16"></up-loading-icon>
<up-loading-icon text="订单已完成" v-else textSize="16"></up-loading-icon>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { Service } from "@/Service/Service";
import { ref, onMounted } from 'vue';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let orderId = ref<string>('')
let url = ref<string>('')
let isshow = ref<false>(false)
let orderInfo = ref<any>({
isFood: 0
})
let riderOrder = ref<any>({
status:0
})
let orderOver = ref<true>(false)
onLoad((data) => {
if (data.orderId) {
orderId.value = data.orderId
getData()
} else {
Service.Msg('为获取到订单ID')
}
});
// 初始化url
const getUrl = () => {
isshow.value = false
url.value = 'https://hmjz.327gzs.top?orderId=' + orderId.value + '&isFood=' + riderOrder.value.status
isshow.value = true
}
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
if (res.code==0) {
orderInfo.value = res.data.orderInfo
riderOrder.value = res.data.riderOrder
getUrl()
}else{
Service.Msg(res.mgs)
}
})
}
// 点击完成送餐取餐调用
const handleMessage = (data) => {
let preat = data.detail.data[0]
if(preat.action =='message'){
if (riderOrder.value.status == 0) {
// 去商家、取餐
pickFood(1)
} else {
// 去用户,送餐
pickFood(2)
}
}else if(preat.action =='logData'){
CNRiderOrderService.UpdateRiderLocation(preat.data[0],preat.data[1]).then(res=>{
if(res.code==0){
}
})
}
return
};
// 骑手定位
const logData = (data) =>{
console.log(data,'骑手定位')
}
// 取餐
const pickFood = ( type:number) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId.value, type).then(res => {
if (res.data) {
Service.Msg(type==1?'取餐成功':'订单完成')
if(type==2){
setTimeout(()=>{Service.GoPageTab('/pages/index/index')},500)
}
getData()
} else {
Service.Msg(res.msg)
}
})
};
</script>
<style lang="scss" scoped>
.reloading-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #f7f7f7; // 使用页面底色
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
</style>

View File

@@ -0,0 +1,68 @@
import { Service } from '@/Service/Service';
/*****订单接口*****/
class CNRiderOrderService {
private static GetRiderOrderListPath : string = '/Rider/GetRiderOrderList';
/*****首页新订单*****/
static GetRiderOrderList(page : number) {
var result = Service.Request(this.GetRiderOrderListPath, "GET", {page});
return result;
}
private static RiderTakeOrderPath : string = '/Rider/RiderTakeOrder';
/*****骑手接单*****/
static RiderTakeOrder(orderId : string) {
var result = Service.Request(this.RiderTakeOrderPath, "POST", {orderId});
return result;
}
private static GetRiderTakeOrderListPath : string = '/Rider/GetRiderTakeOrderList';
/*****首页 待取餐/配送中*****/
static GetRiderTakeOrderList(status: number,page : number) {
var result = Service.Request(this.GetRiderTakeOrderListPath, "GET", {status,page});
return result;
}
private static GetUnitOrderInfoPath : string = '/Order/GetUnitOrderInfo';
/*****获取订单详情*****/
static GetUnitOrderInfo(orderId: string) {
var result = Service.Request(this.GetUnitOrderInfoPath, "GET", {orderId});
return result;
}
private static UpdateRiderOrderTakePath : string = '/Rider/UpdateRiderOrderTake';
/*****取餐*****/
static UpdateRiderOrderTake(orderId: string,status:number) {
var result = Service.Request(this.UpdateRiderOrderTakePath, "POST", {orderId,status});
return result;
}
private static GetRiderAccLogPath : string = '/Rider/GetRiderAccLog';
/*****收入列表*****/
static GetRiderAccLog(time: string,page:number) {
var result = Service.Request(this.GetRiderAccLogPath, "GET", {time,page});
return result;
}
private static AddRiderWithPath : string = '/Rider/AddRiderWith';
/*****骑手提现*****/
static AddRiderWith(amount: number,payway:string,name:string,account:string) {
var result = Service.Request(this.AddRiderWithPath, "POST", {amount,payway,name,account});
return result;
}
private static GetRiderWithListPath : string = '/Rider/GetRiderWithList';
/*****骑手提现列表*****/
static GetRiderWithList(page:number) {
var result = Service.Request(this.GetRiderWithListPath, "GET", {page});
return result;
}
}
export {
Service,
CNRiderOrderService
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

View File

@@ -0,0 +1,332 @@
<template>
<view class="review-management-page">
<!-- 全新方案:纯 CSS 手动构建的骨架屏 -->
<view v-if="loading" class="skeleton-wrapper">
<view class="skeleton-card" style="height: 150rpx;"></view>
<view class="skeleton-tabs">
<view class="skeleton-item skeleton-text" style="width: 100%; height: 44rpx;"></view>
</view>
<view v-for="i in 3" :key="i" class="skeleton-card">
<view class="skeleton-item skeleton-text" style="width: 100%; height: 300rpx;"></view>
</view>
</view>
<!-- 页面实际内容 -->
<view v-else class="page-content" :class="{'height-pre':showReview}">
<!-- 1. 数据看板 -->
<view class="summary-card">
<view class="data-item">
<view class="value"><text class="score">{{ satisfaction }}</text><up-icon name="star-fill"
color="#ff9900" size="20"></up-icon></view>
<text class="label">满意度</text>
</view>
<view class="data-item">
<text class="value">{{total}}</text>
<text class="label">总评价数</text>
</view>
</view>
<!-- 2. Tabs 切换 -->
<view class="sticky-tabs-wrapper">
<up-tabs :list="reviewTabs" :current="currentTab" @change="onTabChange" lineColor="#fa6400"
:activeStyle="{ color: '#fa6400', fontWeight: 'bold' }" :inactiveStyle="{ color: '#666' }">
</up-tabs>
</view>
<view class="review-list">
<view class="review-card" v-for="review in reviewList" :key="review">
<view class="reviewer-info">
<text class="name" style="font-weight: bold;" >{{ review.nick }}</text>
<up-rate v-model="review.source" readonly activeColor="#ff9900" size="16"></up-rate>
<text class="time">{{ Service.formatDate(review.addTime,1) }}</text>
</view>
<text class="review-text">{{ review.sign }}</text>
</view>
<up-loadmore status="nomore" nomoreText="没有更多评价了"></up-loadmore>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { Service } from "@/Service/Service";
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
const loading = ref<boolean>(true);
const currentTab = ref<number>(0);
let satisfaction = ref(0)
let status = ref('nomore')
let page = ref(1)
let showReview = ref(false)
let review = ref('')
let storeInfo = ref<any>()
let total = ref(0)
const reviewTabs = ref([
{ name: '全部' }, { name: '好评' }, { name: '差评' }
]);
const reviewList = ref<Array<any>>([]);
let currentId = ref('')
onLoad(() => {
getData()
});
onShow(() => { });
// 获取数据
const getData = () => {
status.value = 'loadmore'
page.value = 1
reviewList.value = []
getList()
}
const getList = () => {
if (status.value == 'loading' || status.value == 'nomore') {
return
}
status.value = 'loading'
CNRiderDataService.GetRiderEvaluate(currentTab.value, page.value).then(res => {
loading.value = false
if (res.data) {
satisfaction.value = res.data.satisfaction
total.value=res.data.total.value
reviewList.value = [...reviewList.value, ...res.data.evaluateList]
page.value++
status.value = res.data.evaluateList == 10 ? 'loadmore' : 'nomore'
}
})
}
const onTabChange = (e : { index : number }) => {
currentTab.value = e.index;
getData()
};
const replyTo = () => {
if (review.value == '') {
return
}
};
</script>
<style lang="scss" scoped>
.height-pre {
height: 100vh;
overflow: hidden;
}
@keyframes skeleton-blink {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
.skeleton-item {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-blink 1.5s infinite linear;
}
.skeleton-rect {
border-radius: 12rpx;
}
.skeleton-text {
border-radius: 4rpx;
}
.skeleton-wrapper {
padding: 24rpx;
background-color: #f7f7f7;
.skeleton-tabs {
padding: 20rpx 0;
background-color: #fff;
}
.skeleton-card {
background-color: #fff;
padding: 30rpx;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
}
.review-management-page {
background-color: #f7f7f7;
min-height: 100vh;
}
.summary-card {
display: flex;
justify-content: space-around;
text-align: center;
background-color: #fff8f5;
border-radius: 16rpx;
padding: 30rpx;
margin: 24rpx;
.data-item {
.value {
font-size: 40rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
gap: 4rpx;
}
.label {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
.text-red {
color: #fa5151;
}
}
}
.sticky-tabs-wrapper {
position: sticky;
top: 0;
z-index: 10;
background-color: #fff;
:deep(.up-tabs__wrapper__nav__item) {
flex: 1;
}
:deep(.up-tabs__wrapper__nav) {
position: relative;
.unread-badge {
position: absolute;
top: 16rpx;
right: 20rpx;
background-color: #fa5151;
color: #fff;
font-size: 20rpx;
min-width: 32rpx;
height: 32rpx;
line-height: 32rpx;
border-radius: 16rpx;
text-align: center;
padding: 0 8rpx;
}
}
}
.review-list {
padding: 24rpx;
.review-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
&.bad-review {
border: 1rpx solid #fef0f0;
}
.reviewer-info {
display: flex;
align-items: center;
gap: 16rpx;
.name {
font-size: 30rpx;
font-weight: 500;
}
.time {
font-size: 24rpx;
color: #999;
margin-left: auto;
}
}
.review-text {
font-size: 28rpx;
color: #333;
margin: 20rpx 0;
display: block;
line-height: 1.6;
}
.review-images {
display: flex;
gap: 16rpx;
.image-placeholder {
width: 160rpx;
height: 160rpx;
overflow: hidden;
border-radius: 8rpx;
}
}
.merchant-reply {
display: flex;
align-items: center;
gap: 12rpx;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx;
margin-top: 20rpx;
font-size: 26rpx;
.reply-label {
color: #fa6400;
font-weight: 500;
}
.reply-text {
color: #666;
}
}
.reply-action {
text-align: right;
margin-top: 20rpx;
text {
display: inline-block;
border: 1rpx solid #fa6400;
color: #fa6400;
font-size: 26rpx;
padding: 8rpx 24rpx;
border-radius: 30rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,423 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="order-detail skeleton-loading">
<!-- 订单基本信息骨架屏 -->
<view class="order-basic-info skeleton-section">
<view class="" style="display: flex; justify-content: space-between;align-items: center; " >
<view class="skeleton-block skeleton-short"></view>
<view class="skeleton-block skeleton-short"></view>
</view>
<view class="skeleton-block skeleton-long"></view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;" >
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-medium"></view>
</view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<!-- 物品清单骨架屏 -->
<view class="order-basic-info skeleton-section">
<view v-for="item in 4" class="skeleton-block" style="width: 100%; height: 40rpx; " ></view>
<view class="" style="display: flex; justify-content: center;" >
<view class="skeleton-block skeleton-short"></view>
</view>
</view>
<!-- 地图区域骨架屏 -->
<view class="map-section skeleton-section">
<view class="map-placeholder skeleton-map"></view>
</view>
<!-- 地址区域骨架屏 -->
<view class="address-section skeleton-section">
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<view class="bottom-padding"></view>
<!-- 底部按钮骨架屏 -->
<view class="bottom-action">
<view class="skeleton-button"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: baseline; justify-content: space-between;">
<view class="info-item">
<view class="label" style="font-weight: 700; font-size: 30rpx;">30分钟内送达</view>
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥5.50</text>
</view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">20251021123456</text>
</view>
<view class="info-item" style=" justify-content: space-between;">
<view class="">
<text class="label">物品明细 : </text>
<text class="value">食物</text>
</view>
<view class="label">共3件商品</view>
</view>
<view class="info-item">
<text class="label">物品重量 : </text>
<text class="value">0.5kg</text>
</view>
<view class="info-item">
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="24rpx" class="clock-icon"></u-icon>
2025-10-17 18:30下单
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
5件
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
¥16
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'190rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in 5 " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
康师傅 大食桶红烧牛肉143g/桶 经典红烧酱香免洗桶装速食泡面
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×4
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥16
</view>
</view>
</view>
<view class="" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 地图区域 -->
<view class="map-section">
<view class="map-placeholder">
<text @click="Service.GoPage('/pages/order/navigation')" class="map-hint">点击查看完整导航</text>
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<view class="" style=" border-bottom: 4rpx solid #e2e2e2; ">
<text class="section-title">取餐地址 : </text>
<view class="address-content">
<text class="store-name">张亮麻辣烫(五道口店)</text>
<text class="address">北京市海淀区成府路28号</text>
</view>
</view>
<view style="margin-top: 20rpx; font-size: 30rpx;font-weight: 800;color: #333;">送餐地址 : </view>
<view class="address-content">
<text class="user-name">张*</text>
<text class="address">XX小区3栋502室</text>
<view class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">请放门口,勿按门铃</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action">
<up-button color="var(--nav-mian)" class="confirm-btn">立即接单</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
onLoad(() => {
setTimeout(() => {
loading.value = false
}, 1000)
})
</script>
<style scoped>
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin-bottom: 25rpx;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 15rpx;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-section {
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
background-color: #fff;
}
.skeleton-block {
background-color: #f0f0f0;
margin-bottom: 20rpx;
border-radius: 4rpx;
position: relative;
overflow: hidden;
}
.skeleton-short {
height: 40rpx;
width: 30%;
}
.skeleton-medium {
height: 30rpx;
width: 30%;
}
.skeleton-long {
height: 30rpx;
width: 90%;
}
.skeleton-map {
height: 400rpx;
background-color: #f0f0f0;
border-radius: 20rpx;
margin: 0;
}
.skeleton-button {
height: 90rpx;
background-color: #f0f0f0;
border-radius: 45rpx;
}
.bottom-padding {
height: 140rpx;
}
/* 骨架屏动画 */
.skeleton-block::after,
.skeleton-map::after,
.skeleton-button::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,494 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="task-list-container skeleton-loading">
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<view class="" style="width: 100vw; height: 120rpx"> </view>
<!-- 骨架屏订单列表 -->
<view class="order-list">
<!-- 骨架屏任务卡片 1 -->
<view v-for="item in 3" class="task-section skeleton-card">
<view class="" style="display: flex; align-items: center; justify-content: space-between;" >
<view class="skeleton-tag"></view>
<view class="skeleton-time-group">
<view class="icon-placeholder"></view>
<view class="skeleton-time-text"></view>
</view>
</view>
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<view class="skeleton-address-info">
<view class="icon-placeholder"></view>
<view class="skeleton-address-text"></view>
</view>
<view class="skeleton-price-time-row">
<view class="skeleton-price"></view>
<view class="skeleton-time-group">
<view class="icon-placeholder"></view>
<view class="skeleton-time-text"></view>
</view>
</view>
<view class="skeleton-button"></view>
</view>
</view>
<view class="" style="width: 100vw; display: flex; justify-content: center; margin-top: 20rpx; " >
<view class="" style="width: 200rpx; height: 40rpx; background-color: #fff; border-radius: 4rpx; " >
</view>
</view>
</view>
<view v-else class="task-list-container">
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view class="" style="width: 100vw; height: 120rpx; ">
</view>
<!-- 订单列表 -->
<view class="order-list">
<view @click="gopage()" v-for="(orderItem,orderIndex) in 3 " :key="orderIndex" class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view v-if="activeTab==0" class="high-price-tag" style="border-radius: 8rpx;" >
<image :src="Service.GetIconImg('/static/index/task/fire.png')" style="width: 24rpx; height: 24rpx;" mode=""></image>
<text class="high-price-text" style="margin-left: 4rpx;" >高价单</text>
</view>
<view v-else class="high-price-tag" :style="{'border':activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14','color':activeTab==1?'#52C41A':'#FAAD14' }" style="background-color: #fff;" >
<text class="high-price-text">{{activeTab==1?'待取单':'配送中'}}</text>
</view>
<view class="" v-if="activeTab!==0" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">老北京炸酱面</text>
<text class="distance">500m</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text">北京市朝阳区三里屯SOHO</text>
<text v-if="activeTab!==0" class="address-text">共3件商品</text>
</view>
<!-- 商品次数-->
<view v-if="activeTab==1" class="address-info">
<text class="address-text">共3件商品</text>
</view>
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==0" class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">A121</text>
</view>
<view v-if="activeTab==2" class="">
<text class="address-text">据您1.2km</text>
</view>
<view class="pickup-time">
<up-icon name="clock" color="#FF9500" size="24rpx" />
<text class="time-text" :style="{'color':activeTab==0?'#FAAD14':'#FF0000'}" >{{activeTab==0?'12:30 前取餐':'12:30 前送达'}}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<up-button type="primary" @click="buttonClick()" :color="activeTab==0?'#1890FF':(activeTab==1?'#52C41A':'#52C41A')" size="large" class="grab-btn">{{ activeTab==0?'立即抢单':(activeTab==1?'我已取餐':'确认送达') }}</up-button>
</view>
<!-- 没有更多任务提示 -->
<up-loadmore :status="status" />
<view class="" style="width: 100vw; height: 60rpx; " ></view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { Service } from '@/Service/Service'
let loading = ref(true)
// 标签数据
const tabs = ['新任务', '待取货', '配送中']
const activeTab = ref(0)
let status = ref('nomore')
onLoad(() => {
setTimeout(() => {
loading.value = false
}, 1000)
})
// 页面跳转
const gopage = () => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder')
} else {
Service.GoPage('/pages/order/orderDetail')
}
}
const buttonClick=()=>{
if(activeTab.value==2){
Service.GoPage('/pages/order/finish')
}
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
}
</script>
<style scoped>
page {
background-color: #F5F5F5;
}
/* 顶部标签栏 */
.tab-bar {
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
position: fixed;
top: 0;
left: 0;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
/* 订单列表 */
.order-list {
padding: 0rpx 30rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: center;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* 没有更多任务 */
.no-more-tasks {
margin-top: 40rpx;
text-align: center;
padding: 20rpx 0;
}
.no-more-text {
font-size: 28rpx;
color: #999999;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-card {
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
position: relative;
overflow: hidden;
}
.skeleton-tab-text {
width: 80rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-tag {
width: 100rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 15rpx;
margin-bottom: 20rpx;
}
.skeleton-merchant-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-distance {
width: 60rpx;
height: 28rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-address-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.icon-placeholder {
width: 24rpx;
height: 24rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-right: 10rpx;
}
.skeleton-address-text {
width: 400rpx;
height: 28rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-price-time-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25rpx;
}
.skeleton-price {
width: 80rpx;
height: 36rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-time-group {
display: flex;
align-items: center;
}
.skeleton-time-text {
width: 150rpx;
height: 26rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-button {
width: 100%;
height: 80rpx;
background-color: #f0f0f0;
border-radius: 40rpx;
}
/* 骨架屏动画 */
.skeleton-card .skeleton-tag::after,
.skeleton-card .skeleton-merchant-name::after,
.skeleton-card .skeleton-distance::after,
.skeleton-card .skeleton-address-text::after,
.skeleton-card .icon-placeholder::after,
.skeleton-card .skeleton-price::after,
.skeleton-card .skeleton-time-text::after,
.skeleton-card .skeleton-button::after,
.skeleton-loading .skeleton-tab-text::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,476 @@
<template>
<view class="skeleton-container" v-if="isLoading">
<!-- 顶部提示栏骨架 -->
<view class="skeleton-top-tip"></view>
<!-- 身份信息区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-title"></view>
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
</view>
<!-- 身份证照片区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-title"></view>
<view class="skeleton-upload-item"></view>
<view class="skeleton-upload-item"></view>
</view>
<!-- 人脸识别区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-title"></view>
<view class="skeleton-face-area">
<view class="skeleton-face-icon"></view>
<view class="skeleton-face-text"></view>
</view>
</view>
<!-- 协议勾选骨架 -->
<view class="skeleton-agreement"></view>
<!-- 提交按钮骨架 -->
<view class="skeleton-submit"></view>
</view>
<view v-else class="real-name-auth-container">
<!-- 顶部提示栏 -->
<view class="top-tip">
<text class="tip-text">请完成实名认证,保障您的接单权益</text>
</view>
<!-- 表单内容 -->
<view class="form-content">
<!-- 身份信息区域 -->
<view class="section">
<view class="section-title">身份信息</view>
<!-- 姓名输入 -->
<view class="form-item">
<view class="label">姓名</view>
<u-input v-model="formData.name" placeholder="请输入身份证姓名" placeholder-color="#999" border="none"
class="input" input-align="right" />
</view>
<!-- 身份证号输入 -->
<view class="form-item">
<view class="label">身份证号</view>
<u-input v-model="formData.idCard" placeholder="请输入18位身份证号" placeholder-color="#999" border="none"
class="input" input-align="right" maxlength="18" />
</view>
</view>
<!-- 身份证照片区域 -->
<view class="section">
<view class="section-title">身份证照片</view>
<!-- 上传身份证正面 -->
<view class="upload-item">
<view class="upload-area bordered-area">
<view class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证正面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-if="formData.frontImage" class="uploaded-placeholder">
<text>身份证正面</text>
</view>
</view>
</view>
<!-- 上传身份证反面 -->
<view class="upload-item">
<view class="upload-area bordered-area">
<view class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证反面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-if="formData.backImage" class="uploaded-placeholder">
<text>身份证反面</text>
</view>
</view>
</view>
</view>
<!-- 人脸识别区域 -->
<view class="section">
<view class="section-title">人脸验证</view>
<view class="face-verify-area" @click="handleFaceVerify">
<view class="face-icon">
<up-icon name="account" size="60" color="#1890ff"></up-icon>
</view>
<view class="face-text">点击进行人脸识别</view>
</view>
</view>
<!-- 协议勾选 -->
<view class="agreement-section">
<u-checkbox v-model="formData.agreed" shape="circle" class="checkbox" />
<text class="agreement-text">我已阅读并同意</text>
<text class="agreement-link" @click="handleAgreementClick">《骑手实名认证协议》</text>
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<u-button type="primary" class="submit-btn" size="large">
提交认证
</u-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, computed } from 'vue';
let isLoading = ref(true)
// 表单数据
const formData = ref({
name: '',
idCard: '',
frontImage: '',
backImage: '',
agreed: false
});
onLoad(() => {
setTimeout(() => {
isLoading.value = false
}, 1000)
})
// 处理人脸识别
const handleFaceVerify = () => {
// 在实际应用中这里会调用人脸识别相关API
console.log('开始人脸识别');
};
// 处理协议点击
const handleAgreementClick = () => {
// 打开协议详情
console.log('打开协议详情');
};
</script>
<style scoped lang="scss">
page {
background-color: #f5f5f5;
}
/* 骨架屏样式 */
.skeleton-container {
padding: 0 30rpx;
}
.skeleton-top-tip {
height: 70rpx;
width: 100%;
background-color: #e6f7ff;
margin: 20rpx 0;
border-radius: 10rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-section {
margin-bottom: 40rpx;
}
.skeleton-title {
height: 32rpx;
width: 160rpx;
background-color: #e6e6e6;
border-radius: 16rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-form-item {
display: flex;
align-items: center;
background-color: #fff;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.skeleton-form-item:last-child {
border-bottom: none;
}
.skeleton-label {
height: 28rpx;
width: 120rpx;
background-color: #e6e6e6;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-input {
height: 28rpx;
width: calc(100% - 140rpx);
background-color: #e6e6e6;
margin-left: 20rpx;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-upload-item {
height: 200rpx;
width: 100%;
background-color: #fff;
border: 2rpx solid #000;
border-radius: 20rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-face-area {
height: 240rpx;
background-color: #fff;
border-radius: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.skeleton-face-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #e6e6e6;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-face-text {
height: 28rpx;
width: 200rpx;
background-color: #e6e6e6;
border-radius: 14rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-agreement {
height: 30rpx;
width: 60%;
background-color: #e6e6e6;
border-radius: 15rpx;
margin-bottom: 40rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-submit {
height: 92rpx;
width: 100%;
background-color: #e6e6e6;
border-radius: 46rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
opacity: 0.6;
}
50% {
opacity: 1;
}
100% {
opacity: 0.6;
}
}
.top-tip {
background-color: #E6F7FF;
padding: 20rpx 30rpx;
margin: 20rpx;
border-radius: 20rpx;
}
.tip-text {
color: #1890ff;
font-size: 28rpx;
}
.form-content {
padding: 0 30rpx;
}
.section {
margin-bottom: 40rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 10rpx;
}
.form-item {
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.label {
width: 120rpx;
font-size: 28rpx;
color: #333;
}
.input {
flex: 1;
font-size: 28rpx;
}
.upload-item {
margin-bottom: 20rpx;
}
.upload-area {
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
}
.bordered-area {
background-color: #fff;
border-radius: 20rpx;
width: 100%;
}
.upload-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
z-index: 1;
}
.upload-icon {
font-size: 60rpx;
color: #1890ff;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #1890ff;
}
.uploaded-placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
border: 2rpx solid #000;
color: #333;
font-size: 28rpx;
}
.face-verify-area {
background-color: #fff;
height: 240rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 8rpx;
}
.face-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #e6f7ff;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
}
.face-text {
font-size: 28rpx;
color: #1890ff;
font-weight: 500;
}
.agreement-section {
display: flex;
align-items: center;
margin-bottom: 40rpx;
padding: 0 10rpx;
}
.checkbox {
margin-right: 10rpx;
}
.agreement-text {
font-size: 26rpx;
color: #666;
}
.agreement-link {
font-size: 26rpx;
color: #1890ff;
margin-left: 4rpx;
}
.input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.submit-section {
margin-top: 60rpx;
padding: 0 20rpx;
}
.submit-btn {
height: 92rpx;
font-size: 32rpx;
border-radius: 46rpx;
background-color: #1890ff;
}
.submit-btn[disabled] {
background-color: #a0cfff;
color: #ffffff;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,808 @@
<template>
<view v-if="isLoading" class="skeleton-container" style="padding-top: 80rpx;" >
<!-- 统计数据区域骨架 -->
<view class="skeleton-stats-section">
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
</view>
<!-- 接单按钮骨架 -->
<view class="skeleton-action-section">
<view class="skeleton-accept-btn"></view>
</view>
<!-- 标签栏骨架 -->
<view class="skeleton-tab-bar">
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<!-- 任务列表骨架 -->
<view class="skeleton-task-list">
<!-- 任务卡片骨架 -->
<view class="skeleton-task-section" v-for="i in 3" :key="i">
<!-- 高价单标签骨架 -->
<view class="skeleton-tag-row">
<view class="skeleton-high-price-tag"></view>
<view class="skeleton-phone-btn"></view>
</view>
<!-- 商家信息骨架 -->
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<!-- 地址信息骨架 -->
<view class="skeleton-address-info">
<view class="skeleton-address-text"></view>
</view>
<!-- 价格和取餐时间骨架 -->
<view class="skeleton-price-time-row">
<view class="skeleton-pickup-code"></view>
<view class="skeleton-pickup-time"></view>
</view>
<!-- 立即抢单按钮骨架 -->
<view class="skeleton-grab-btn"></view>
</view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="rider-home" style="padding-top: 60rpx;" >
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥{{userData.dayAmount.toFixed(2)}}</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">{{userData.dayOrderCount}}单</text>
</view>
<view class="stat-item">
<text class="stat-label">配送中</text>
<text class="stat-value ongoing">{{ userData.takeOrderCount }}单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button :disabled='riderInfo.status===0' type="primary" shape="circle" size="default"
class="accept-orders-btn"
@click="toggleAcceptOrders">{{ riderInfo.status==0?'审核中':(riderInfo.isOnline == 0 ? '已下线' : '已上线') }}</up-button>
</view>
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view v-if="riderInfo.status==1" class="" style="padding: 0 30rpx;">
<!-- -->
<view v-for="(orderItem,orderIndex) in orderList " @click="gopage(orderItem.orderId)" :key="orderIndex"
class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="high-price-tag"
:style="{'border':activeTab==0?'1rpx solid #FF7875':(activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14'),'color':activeTab==0?'#FF7875':(activeTab==1?'#52C41A':'#FAAD14') }"
style="background-color: #fff;">
<text class="high-price-text">{{activeTab==0? '新订单':(activeTab==1? '待取单':'配送中')}}</text>
</view>
<view class="" @click.stop="call(orderItem.phone)" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">{{ orderItem.storeName }}</text>
<text v-if="activeTab==1" class="distance">{{ distance(orderItem.distance) }}</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text"> {{orderItem.address }}</text>
<!-- <text v-if="activeTab!==0" class="address-text">共3件商品</text> -->
</view>
<!-- 商品次数-->
<!-- <view class="address-info">
<text class="address-text">共3件商品</text>
<view class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
</view> -->
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">{{orderItem.pickCode }}</text>
</view>
<view v-if="activeTab!==1" class="">
<text class="address-text">据您{{ distance(orderItem.distance) }}</text>
</view>
<view class="pickup-time">
<up-icon name="clock" :color="activeTab==0?'#FAAD14':'#FF0000'" size="16" />
<text class="time-text"
:style="{'color':activeTab==0?'#FAAD14':'#FF0000'}">{{ orderItem.makeTime.split(' ').length==2?'预计'+orderItem.makeTime.split(' ')[1]+'送达':orderItem.makeTime }}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<view class="" style="display: flex; ">
<view class="" style="width: 100%;"
@click.stop="activeTab==0?takeOrder(orderItem.orderId):pickFood(orderItem.orderId)">
<button type="primary" :color="activeTab==0?'#1890FF':'#52C41A'" size="large"
class="grab-btn">{{ activeTab==0?'立即接单':(activeTab==1?'我已取餐':'确认送达')}}</button>
</view>
<view class="" style="width: 20rpx;" v-if="activeTab!=0">
</view>
<view style="width: 100%;" v-if="activeTab!=0" @click.stop="Service.GoPage('/pages/order/orderMap?orderId='+orderItem.orderId)" class="">
<button type="primary" color="#1890FF"
class="grab-btn">{{activeTab==1?'导航取餐':'导航送餐'}}</button>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
<view v-if="riderInfo.status==0" style=" margin-top: 20rpx; text-align: center; font-weight: bold; font-size: 34rpx;" class="">
信息审核中·请等待审核
</view>
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const isLoading = ref(true);
let status = ref('nomore')
let page = ref(1)
let userData = ref({
dayAmount: 0,
dayOrderCount: 0,
takeOrderCount: 0
})
const tabs = ['新订单', '待取货', '配送中']
const activeTab = ref(0)
let riderInfo = ref<any>({})
let orderList = ref<Array<any>>([])
onLoad(() => {
uni.$on(`newOrder`, (data) => {
newOrder()
});
})
onShow(() => {
getData()
getOrderData()
})
// 有新订单
const newOrder = () =>{
if(activeTab.value==0){
getOrderData()
}
audioPlay()
}
const getData = () => {
CNRiderDataService.GetRiderHomeInfo().then(res => {
isLoading.value = false
riderInfo.value = res.data.riderInfo
userData.value = res.data
if (res.data.riderInfo.status === -1) {
Service.GoPage('/pages/my/completeData')
}
})
}
const getOrderData = () => {
status.value = 'loadmore'
page.value = 1
orderList.value = []
getOrderList()
}
//获取订单
const getOrderList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
if (activeTab.value == 0) {
CNRiderOrderService.GetRiderOrderList(page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
} else {
CNRiderOrderService.GetRiderTakeOrderList(activeTab.value == 1 ? 0 : 1, page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
}
// 接单
const takeOrder = (orderId : string) => {
CNRiderOrderService.RiderTakeOrder(orderId).then(res => {
if (res.data) {
activeTab.value=1
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 取餐
const pickFood = (orderId : string) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId, activeTab.value).then(res => {
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
getOrderData()
}
// 切换接单状态
const toggleAcceptOrders = () => {
CNRiderDataService.UpdateRiderOnline().then(res => {
if (res.data) {
getData()
} else {
Service.Msg(res.msg)
}
})
};
// 距离计算
const distance = (item : any) => {
if (item < 0) {
return Number(item * 100).toFixed(2) + 'm'
} else {
return Number(item).toFixed(2) + 'km'
}
}
// 拨打电话
const call = (e : string) => {
uni.makePhoneCall({
phoneNumber: e
})
}
// 页面跳转
const gopage = (id) => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder?orderId=' + id)
} else {
Service.GoPage('/pages/order/orderDetail?orderId=' + id+'&type='+activeTab.value)
}
}
const audioPlay = () => {
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
innerAudioContext.src = '/static/order.mp3';
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
});
}
</script>
<style scoped lang="scss">
page {
background-color: #F6f6f6;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 顶部标签栏 */
.tab-bar {
margin-top: 20rpx;
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: baseline;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx;
}
/* 统计数据区域骨架 */
.skeleton-stats-section {
background-color: #ffffff;
margin: 0 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.skeleton-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.skeleton-stat-label {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-stat-value {
width: 150rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 接单按钮骨架 */
.skeleton-action-section {
margin: 20rpx;
}
.skeleton-accept-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 标签栏骨架 */
.skeleton-tab-bar {
display: flex;
background-color: #ffffff;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #e5e5e5;
}
.skeleton-tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.skeleton-tab-text {
width: 100rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 任务列表骨架 */
.skeleton-task-list {
padding: 0 20rpx;
}
.skeleton-task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签骨架 */
.skeleton-tag-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.skeleton-high-price-tag {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-phone-btn {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 商家信息骨架 */
.skeleton-merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-distance {
width: 80rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 地址信息骨架 */
.skeleton-address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.skeleton-address-text {
width: 100%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 价格和时间骨架 */
.skeleton-price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-pickup-code {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-pickup-time {
width: 200rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 按钮骨架 */
.skeleton-grab-btn {
margin-top: 25rpx;
height: 80rpx;
background-color: #e6e6e6;
border-radius: 40rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-color: #e6e6e6;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e6e6e6;
}
}
</style>

View File

@@ -0,0 +1,799 @@
<template>
<view v-if="isLoading" class="skeleton-container" style="padding-top: 80rpx;">
<!-- 统计数据区域骨架 -->
<view class="skeleton-stats-section">
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
</view>
<!-- 接单按钮骨架 -->
<view class="skeleton-action-section">
<view class="skeleton-accept-btn"></view>
</view>
<!-- 标签栏骨架 -->
<view class="skeleton-tab-bar">
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<!-- 任务列表骨架 -->
<view class="skeleton-task-list">
<!-- 任务卡片骨架 -->
<view class="skeleton-task-section" v-for="i in 3" :key="i">
<!-- 高价单标签骨架 -->
<view class="skeleton-tag-row">
<view class="skeleton-high-price-tag"></view>
<view class="skeleton-phone-btn"></view>
</view>
<!-- 商家信息骨架 -->
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<!-- 地址信息骨架 -->
<view class="skeleton-address-info">
<view class="skeleton-address-text"></view>
</view>
<!-- 价格和取餐时间骨架 -->
<view class="skeleton-price-time-row">
<view class="skeleton-pickup-code"></view>
<view class="skeleton-pickup-time"></view>
</view>
<!-- 立即抢单按钮骨架 -->
<view class="skeleton-grab-btn"></view>
</view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="rider-home" style="padding-top: 60rpx;">
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥{{userData.dayAmount.toFixed(2)}}</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">{{userData.dayOrderCount}}单</text>
</view>
<view class="stat-item">
<text class="stat-label">配送中</text>
<text class="stat-value ongoing">{{ userData.takeOrderCount }}单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button :disabled='riderInfo.status===0' type="primary" shape="circle" size="default"
class="accept-orders-btn"
@click="toggleAcceptOrders">{{ riderInfo.status==0?'审核中':(riderInfo.isOnline == 0 ? '已下线' : '已上线') }}</up-button>
</view>
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view v-if="riderInfo.status==1" class="" style="padding: 0 30rpx;">
<!-- -->
<view v-for="(orderItem,orderIndex) in orderList " @click="gopage(orderItem.orderId)" :key="orderIndex"
class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="high-price-tag"
:style="{'border':activeTab==0?'1rpx solid #FF7875':(activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14'),'color':activeTab==0?'#FF7875':(activeTab==1?'#52C41A':'#FAAD14') }"
style="background-color: #fff;">
<text class="high-price-text">{{activeTab==0? '新订单':(activeTab==1? '待取单':'配送中')}}</text>
</view>
<view class="" @click.stop="call(orderItem.phone)" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">{{ orderItem.storeName }}</text>
<text v-if="activeTab==1" class="distance">{{ distance(orderItem.distance) }}</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text"> {{orderItem.address }}</text>
<!-- <text v-if="activeTab!==0" class="address-text">共3件商品</text> -->
</view>
<!-- 商品次数-->
<!-- <view class="address-info">
<text class="address-text">共3件商品</text>
<view class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
</view> -->
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text
style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">{{orderItem.pickCode }}</text>
</view>
<view v-if="activeTab!==1" class="">
<text class="address-text">据您{{ distance(orderItem.distance) }}</text>
</view>
<view class="pickup-time">
<up-icon name="clock" :color="activeTab==0?'#FAAD14':'#FF0000'" size="16" />
<text class="time-text"
:style="{'color':activeTab==0?'#FAAD14':'#FF0000'}">{{ orderItem.makeTime.split(' ').length==2?'预计'+orderItem.makeTime.split(' ')[1]+'送达':orderItem.makeTime }}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<view class="" style="display: flex; ">
<view class="" style="width: 100%;"
@click.stop="activeTab==0?takeOrder(orderItem.orderId):pickFood(orderItem.orderId)">
<button type="primary" :color="activeTab==0?'#1890FF':'#52C41A'" size="large"
class="grab-btn">{{ activeTab==0?'立即接单':(activeTab==1?'我已取餐':'确认送达')}}</button>
</view>
<view class="" style="width: 20rpx;" v-if="activeTab!=0">
</view>
<view style="width: 100%;" v-if="activeTab!=0"
@click.stop="Service.GoPage('/pages/order/orderMap?orderId='+orderItem.orderId)" class="">
<button type="primary" color="#1890FF" class="grab-btn">{{activeTab==1?'导航取餐':'导航送餐'}}</button>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
<view v-if="riderInfo.status==0"
style=" margin-top: 20rpx; text-align: center; font-weight: bold; font-size: 34rpx;" class="">
信息审核中·请等待审核
</view>
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const isLoading = ref(true);
let status = ref('nomore')
let page = ref(1)
let userData = ref({
dayAmount: 0,
dayOrderCount: 0,
takeOrderCount: 0
})
const tabs = ['新订单', '待取货', '配送中']
const activeTab = ref(0)
let riderInfo = ref<any>({})
let orderList = ref<Array<any>>([])
let a = ref(1)
onLoad(() => {
uni.$on(`newOrder`, (data) => {
a.value++
console.log('消息推送1', a.value);
newOrder()
});
})
onShow(() => {
getData()
getOrderData()
})
// 有新订单
const newOrder = () => {
if (activeTab.value == 0) {
getOrderData()
}
}
const getData = () => {
CNRiderDataService.GetRiderHomeInfo().then(res => {
isLoading.value = false
riderInfo.value = res.data.riderInfo
userData.value = res.data
if (res.data.riderInfo.status === -1) {
Service.GoPage('/pages/my/completeData')
}
})
}
const getOrderData = () => {
status.value = 'loadmore'
page.value = 1
orderList.value = []
getOrderList()
}
//获取订单
const getOrderList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
if (activeTab.value == 0) {
CNRiderOrderService.GetRiderOrderList(page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
} else {
CNRiderOrderService.GetRiderTakeOrderList(activeTab.value == 1 ? 0 : 1, page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
}
// 接单
const takeOrder = (orderId : string) => {
CNRiderOrderService.RiderTakeOrder(orderId).then(res => {
if (res.data) {
activeTab.value = 1
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 取餐
const pickFood = (orderId : string) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId, activeTab.value).then(res => {
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
getOrderData()
}
// 切换接单状态
const toggleAcceptOrders = () => {
CNRiderDataService.UpdateRiderOnline().then(res => {
if (res.data) {
getData()
} else {
Service.Msg(res.msg)
}
})
};
// 距离计算
const distance = (item : any) => {
if (item < 0) {
return Number(item * 100).toFixed(2) + 'm'
} else {
return Number(item).toFixed(2) + 'km'
}
}
// 拨打电话
const call = (e : string) => {
uni.makePhoneCall({
phoneNumber: e
})
}
// 页面跳转
const gopage = (id) => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder?orderId=' + id)
} else {
Service.GoPage('/pages/order/orderDetail?orderId=' + id + '&type=' + activeTab.value)
}
}
</script>
<style scoped lang="scss">
page {
background-color: #F6f6f6;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 顶部标签栏 */
.tab-bar {
margin-top: 20rpx;
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: baseline;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx;
}
/* 统计数据区域骨架 */
.skeleton-stats-section {
background-color: #ffffff;
margin: 0 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.skeleton-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.skeleton-stat-label {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-stat-value {
width: 150rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 接单按钮骨架 */
.skeleton-action-section {
margin: 20rpx;
}
.skeleton-accept-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 标签栏骨架 */
.skeleton-tab-bar {
display: flex;
background-color: #ffffff;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #e5e5e5;
}
.skeleton-tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.skeleton-tab-text {
width: 100rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 任务列表骨架 */
.skeleton-task-list {
padding: 0 20rpx;
}
.skeleton-task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签骨架 */
.skeleton-tag-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.skeleton-high-price-tag {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-phone-btn {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 商家信息骨架 */
.skeleton-merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-distance {
width: 80rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 地址信息骨架 */
.skeleton-address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.skeleton-address-text {
width: 100%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 价格和时间骨架 */
.skeleton-price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-pickup-code {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-pickup-time {
width: 200rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 按钮骨架 */
.skeleton-grab-btn {
margin-top: 25rpx;
height: 80rpx;
background-color: #e6e6e6;
border-radius: 40rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-color: #e6e6e6;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e6e6e6;
}
}
</style>

View File

@@ -0,0 +1,78 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
@import 'uview-plus/theme.scss';
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; // 基本色
$uni-text-color-inverse: #fff; // 反色
$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); // 遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;

View File

@@ -0,0 +1,119 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
import { onMounted, ref } from "vue";
import { WebSocket } from '@/Service/Comm/TwWebSocket';
import { Service } from "@/Service/Service"
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let isios = ref(false)
const currentLatitude = ref(0);
const currentLongitude = ref(0);
let locationTimer: ReturnType<typeof setInterval> | null = null;
onLaunch(() => {
isios.value = uni.getSystemInfoSync().platform != 'ios'//是否为ios
//#ifdef APP-PLUS//app
if (isios.value) {
// getVersion()//更新
}
//#endif
// 在 App 启动时,立即初始化并建立 WebSocket 连接
WebSocket.ConnectSocketInit();
// 打开调用
uni.$on("ImCom", () => {
WebSocket.ConnectSocketInit();
})
// 关闭调用
uni.$on("ImComOff", () => {
WebSocket.CloseSocket();
})
});
onShow(() => {
WebSocket.ConnectSocketInit();
});
onHide(() => {
});
const startFetchingLocation = () => {
// 安全检查:如果定时器已存在,先清除,防止重复启动
if (locationTimer) {
clearInterval(locationTimer);
}
console.log("开始定时获取位置间隔1分钟...");
// 1. 立即执行第一次获取
getLocationNow();
// 2. 设置定时器,每 60000 毫秒 (1分钟) 执行一次
locationTimer = setInterval(() => {
getLocationNow();
}, 60000);
};
const getLocationNow = () => {
uni.getLocation({
type: 'wgs84',
isHighAccuracy: true,
success: (res) => {
if(Service.GetUserIsLogin()){
CNRiderOrderService.UpdateRiderLocation(res.longitude,res.latitude).then(res=>{})
}
},
fail: (err) => {
// console.error('获取经纬度失败:', err);
}
});
};
/**
* 停止定时获取位置
*/
const stopFetchingLocation = () => {
if (locationTimer) {
clearInterval(locationTimer);
locationTimer = null; // 清理 ID
}
};
const getUpData = () => {
// #ifdef APP
// plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
// NvpMerchService.GetAppVersion().then(res=>{
// console.log('wgtinfo.versionCode',wgtinfo.versionCode);
// if (res.data.version > wgtinfo.versionCode) {
// setTimeout(function() {
// uni.navigateTo({
// url: "/pages/upData/upData?info=" +
// encodeURIComponent(
// JSON.stringify(res.data))
// })
// }, 1000)
// }
// })
// })
// #endif
}
</script>
<style lang="scss">
@import "uview-plus/index.scss";
@import "colorui/main.css";
@import "colorui/icon.css";
page {
--nav-mian: #1890FF; //全局颜色
--nav-vice: #52C41A; //副颜色
--nav-diluted: #FF4D4F; //次颜色
}
</style>

View File

@@ -0,0 +1,22 @@
import { Service } from '@/Service/Service';
/*****登录接口*****/
class CNRiderLoginService {
private static RiderLoginPath : string = '/Login/RiderLogin';
/*****登录注册*****/
static RiderLogin(login : any) {
var result = Service.Request(this.RiderLoginPath, "POST", login);
return result;
}
private static GetKefuInfoPath: string = '/Home/GetKefuInfo';
/*****获取客服电话*****/
static GetKefuInfo() {
var result = Service.Request(this.GetKefuInfoPath, "GET", {});
return result;
}
}
export {
Service,
CNRiderLoginService
}

View File

@@ -0,0 +1,36 @@
<template>
<!-- 导航栏 -->
<view class=""
style="padding:50rpx 20rpx 18rpx; box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: space-between; ">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" color="#fff" size="32rpx"></up-icon>
</view>
<view class="" style="color: #fff; ">
配送中 · 2单
</view>
<image :src="Service.GetIconImg('/static/index/order/voice.png')"
style="width: 32rpx; height: 32rpx; " mode=""></image>
</view>
<view class="" style="width: 100%; height: 88rpx; ">
</view>
<view>
1111
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service } from "@/Service/Service"
onLoad(() => {
});
onShow(() => {
});
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,494 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="task-list-container skeleton-loading">
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<view class="" style="width: 100vw; height: 120rpx"> </view>
<!-- 骨架屏订单列表 -->
<view class="order-list">
<!-- 骨架屏任务卡片 1 -->
<view v-for="item in 3" class="task-section skeleton-card">
<view class="" style="display: flex; align-items: center; justify-content: space-between;" >
<view class="skeleton-tag"></view>
<view class="skeleton-time-group">
<view class="icon-placeholder"></view>
<view class="skeleton-time-text"></view>
</view>
</view>
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<view class="skeleton-address-info">
<view class="icon-placeholder"></view>
<view class="skeleton-address-text"></view>
</view>
<view class="skeleton-price-time-row">
<view class="skeleton-price"></view>
<view class="skeleton-time-group">
<view class="icon-placeholder"></view>
<view class="skeleton-time-text"></view>
</view>
</view>
<view class="skeleton-button"></view>
</view>
</view>
<view class="" style="width: 100vw; display: flex; justify-content: center; margin-top: 20rpx; " >
<view class="" style="width: 200rpx; height: 40rpx; background-color: #fff; border-radius: 4rpx; " >
</view>
</view>
</view>
<view v-else class="task-list-container" style="padding-top: 60rpx;" >
<!-- 顶部标签栏 -->
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view class="" style="width: 100vw; height: 120rpx; ">
</view>
<!-- 订单列表 -->
<view class="order-list">
<view @click="gopage()" v-for="(orderItem,orderIndex) in 3 " :key="orderIndex" class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view v-if="activeTab==0" class="high-price-tag" style="border-radius: 8rpx;" >
<image :src="Service.GetIconImg('/static/index/task/fire.png')" style="width: 24rpx; height: 24rpx;" mode=""></image>
<text class="high-price-text" style="margin-left: 4rpx;" >高价单</text>
</view>
<view v-else class="high-price-tag" :style="{'border':activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14','color':activeTab==1?'#52C41A':'#FAAD14' }" style="background-color: #fff;" >
<text class="high-price-text">{{activeTab==1?'待取单':'配送中'}}</text>
</view>
<view class="" v-if="activeTab!==0" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">老北京炸酱面</text>
<text class="distance">500m</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text">北京市朝阳区三里屯SOHO</text>
<text v-if="activeTab!==0" class="address-text">共3件商品</text>
</view>
<!-- 商品次数-->
<view v-if="activeTab==1" class="address-info">
<text class="address-text">共3件商品</text>
</view>
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==0" class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">A121</text>
</view>
<view v-if="activeTab==2" class="">
<text class="address-text">据您1.2km</text>
</view>
<view class="pickup-time">
<up-icon name="clock" color="#FF9500" size="24rpx" />
<text class="time-text" :style="{'color':activeTab==0?'#FAAD14':'#FF0000'}" >{{activeTab==0?'12:30 前取餐':'12:30 前送达'}}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<up-button type="primary" @click="buttonClick()" :color="activeTab==0?'#1890FF':(activeTab==1?'#52C41A':'#52C41A')" size="large" class="grab-btn">{{ activeTab==0?'立即抢单':(activeTab==1?'我已取餐':'确认送达') }}</up-button>
</view>
<!-- 没有更多任务提示 -->
<up-loadmore :status="status" />
<view class="" style="width: 100vw; height: 60rpx; " ></view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { Service } from '@/Service/Service'
let loading = ref(true)
// 标签数据
const tabs = ['新任务', '待取货', '配送中']
const activeTab = ref(0)
let status = ref('nomore')
onLoad(() => {
setTimeout(() => {
loading.value = false
}, 1000)
})
// 页面跳转
const gopage = () => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder')
} else {
Service.GoPage('/pages/order/orderDetail')
}
}
const buttonClick=()=>{
if(activeTab.value==2){
Service.GoPage('/pages/order/finish')
}
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
}
</script>
<style scoped>
page {
background-color: #F5F5F5;
}
/* 顶部标签栏 */
.tab-bar {
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
position: fixed;
top: 0;
left: 0;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
/* 订单列表 */
.order-list {
padding: 0rpx 30rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: center;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* 没有更多任务 */
.no-more-tasks {
margin-top: 40rpx;
text-align: center;
padding: 20rpx 0;
}
.no-more-text {
font-size: 28rpx;
color: #999999;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-card {
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
position: relative;
overflow: hidden;
}
.skeleton-tab-text {
width: 80rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-tag {
width: 100rpx;
height: 30rpx;
background-color: #f0f0f0;
border-radius: 15rpx;
margin-bottom: 20rpx;
}
.skeleton-merchant-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15rpx;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-distance {
width: 60rpx;
height: 28rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-address-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.icon-placeholder {
width: 24rpx;
height: 24rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
margin-right: 10rpx;
}
.skeleton-address-text {
width: 400rpx;
height: 28rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-price-time-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25rpx;
}
.skeleton-price {
width: 80rpx;
height: 36rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-time-group {
display: flex;
align-items: center;
}
.skeleton-time-text {
width: 150rpx;
height: 26rpx;
background-color: #f0f0f0;
border-radius: 4rpx;
}
.skeleton-button {
width: 100%;
height: 80rpx;
background-color: #f0f0f0;
border-radius: 40rpx;
}
/* 骨架屏动画 */
.skeleton-card .skeleton-tag::after,
.skeleton-card .skeleton-merchant-name::after,
.skeleton-card .skeleton-distance::after,
.skeleton-card .skeleton-address-text::after,
.skeleton-card .icon-placeholder::after,
.skeleton-card .skeleton-price::after,
.skeleton-card .skeleton-time-text::after,
.skeleton-card .skeleton-button::after,
.skeleton-loading .skeleton-tab-text::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,443 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="order-detail skeleton-loading">
<!-- 订单基本信息骨架屏 -->
<view class="order-basic-info skeleton-section">
<view class="" style="display: flex; justify-content: space-between;align-items: center; ">
<view class="skeleton-block skeleton-short"></view>
<view class="skeleton-block skeleton-short"></view>
</view>
<view class="skeleton-block skeleton-long"></view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-medium"></view>
</view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<!-- 物品清单骨架屏 -->
<view class="order-basic-info skeleton-section">
<view v-for="item in 4" class="skeleton-block" style="width: 100%; height: 40rpx; "></view>
<view class="" style="display: flex; justify-content: center;">
<view class="skeleton-block skeleton-short"></view>
</view>
</view>
<!-- 地图区域骨架屏 -->
<view class="map-section skeleton-section">
<view class="map-placeholder skeleton-map"></view>
</view>
<!-- 地址区域骨架屏 -->
<view class="address-section skeleton-section">
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<view class="bottom-padding"></view>
<!-- 底部按钮骨架屏 -->
<view class="bottom-action">
<view class="skeleton-button"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="info-item">
<view class="label" style="font-weight: 700; font-size: 30rpx;"> {{ orderInfo.distribution=='预约订单'?'预计'+orderInfo.makeTime.split(' ')[1]+'送达':orderInfo.makeTime }} </view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">{{ orderId }}</text>
</view>
<view class="info-item">
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="16" class="clock-icon"></u-icon>
{{ Service.formatDate(orderInfo.addTime,1) }} 下单
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
数量
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
金额
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'110rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in JSON.parse(orderInfo.detail) " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
{{goodsItem.goodsName}}
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×{{ goodsItem.count }}
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥{{ goodsItem.count*goodsItem.price }}
</view>
</view>
</view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;" >
<view class="">
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥{{ Number(orderInfo.postage).toFixed(2) }}</text>
</view>
</view>
<view class="" v-if="JSON.parse(orderInfo.detail).length>2" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<view class="" style=" border-bottom: 4rpx solid #e2e2e2; ">
<view class="section-title">取餐地址 : </view>
<view class="address-content">
<text class="store-name">{{ storeInfo.name }}</text>
<text class="address"> {{ storeInfo.city }}{{storeInfo.region }}{{ storeInfo.address }}</text>
</view>
</view>
<view style="margin: 10rpx 0; font-size: 30rpx;font-weight: 800;color: #333;">送餐地址 : </view>
<view class="address-content">
<text class="user-name">{{ JSON.parse(orderInfo.address).realName }}</text>
<text class="address">{{ JSON.parse(orderInfo.address).address }}</text>
<view v-if="orderInfo.remark" class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">请放门口,勿按门铃</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action">
<up-button @click="placeOrder()" color="var(--nav-mian)" class="confirm-btn">立即接单</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
let deliveryTime = ref('')
let orderInfo = ref<any>({})
let storeInfo = ref<any>({})
let storeLocation = ref<any>({})
let orderId = ref('')
onLoad((data : any) => {
orderId.value = data.orderId
getData()
})
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
loading.value = false
if (res.data) {
deliveryTime.value = res.data.deliveryTime
orderInfo.value = res.data.orderInfo
storeInfo.value = res.data.storeInfo
storeLocation.value = res.data.storeLocation
}
})
}
const placeOrder = () => {
CNRiderOrderService.RiderTakeOrder(orderId.value).then(res => {
if (res.data) {
Service.Msg('接单成功!')
setTimeout(() => {
Service.GoPageBack()
},1000)
} else {
Service.Msg(res.msg)
}
})
}
</script>
<style scoped>
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin: 10rpx 0;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 15rpx;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-section {
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
background-color: #fff;
}
.skeleton-block {
background-color: #f0f0f0;
margin-bottom: 20rpx;
border-radius: 4rpx;
position: relative;
overflow: hidden;
}
.skeleton-short {
height: 40rpx;
width: 30%;
}
.skeleton-medium {
height: 30rpx;
width: 30%;
}
.skeleton-long {
height: 30rpx;
width: 90%;
}
.skeleton-map {
height: 400rpx;
background-color: #f0f0f0;
border-radius: 20rpx;
margin: 0;
}
.skeleton-button {
height: 90rpx;
background-color: #f0f0f0;
border-radius: 45rpx;
}
.bottom-padding {
height: 140rpx;
}
/* 骨架屏动画 */
.skeleton-block::after,
.skeleton-map::after,
.skeleton-button::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,146 @@
{
"name" : "骑手端",
"appid" : "__UNI__06C2D6A",
"description" : "",
"versionName" : "1.0.2",
"versionCode" : 102,
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"compatible" : {
"ignoreVersion" : true //true表示忽略版本检查提示框HBuilderX1.9.0及以上版本支持
},
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {
"Barcode" : {},
"Maps" : {},
"Geolocation" : {},
"Camera" : {},
"Contacts" : {}
},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>"
],
"minSdkVersion" : 25,
"targetSdkVersion" : 25,
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
},
/* ios打包配置 */
"ios" : {
"idfa" : false,
"dSYMs" : false
},
/* SDK配置 */
"sdkConfigs" : {
"ad" : {},
"maps" : {
"amap" : {
"name" : "amapZAvZjTHj",
"appkey_ios" : "3caf9e6f01b0085be1e75e0d0e281fe7",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
},
"geolocation" : {
"amap" : {
"name" : "amapZAvZjTHj",
"__platform__" : [ "android" ],
"appkey_ios" : "",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wx6ef5a6a74620a3e8",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "7DIBZ-K4HCJ-ZR2FE-FOOOP-SALFT-RLFYW"
}
}
}
}
}

View File

@@ -0,0 +1,146 @@
{
"name" : "骑手端",
"appid" : "__UNI__06C2D6A",
"description" : "",
"versionName" : "1.0.6",
"versionCode" : 106,
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"compatible" : {
"ignoreVersion" : true //true表示忽略版本检查提示框HBuilderX1.9.0及以上版本支持
},
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : false,
"waiting" : false,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {
"Barcode" : {},
"Maps" : {},
"Geolocation" : {},
"Camera" : {},
"Contacts" : {}
},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>"
],
"minSdkVersion" : 25,
"targetSdkVersion" : 25,
"abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ]
},
/* ios打包配置 */
"ios" : {
"idfa" : false,
"dSYMs" : false
},
/* SDK配置 */
"sdkConfigs" : {
"ad" : {},
"maps" : {
"amap" : {
"name" : "amapZAvZjTHj",
"appkey_ios" : "3caf9e6f01b0085be1e75e0d0e281fe7",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
},
"geolocation" : {
"amap" : {
"name" : "amapZAvZjTHj",
"__platform__" : [ "android" ],
"appkey_ios" : "",
"appkey_android" : "3caf9e6f01b0085be1e75e0d0e281fe7"
}
}
},
"icons" : {
"android" : {
"hdpi" : "unpackage/res/icons/72x72.png",
"xhdpi" : "unpackage/res/icons/96x96.png",
"xxhdpi" : "unpackage/res/icons/144x144.png",
"xxxhdpi" : "unpackage/res/icons/192x192.png"
},
"ios" : {
"appstore" : "unpackage/res/icons/1024x1024.png",
"ipad" : {
"app" : "unpackage/res/icons/76x76.png",
"app@2x" : "unpackage/res/icons/152x152.png",
"notification" : "unpackage/res/icons/20x20.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"proapp@2x" : "unpackage/res/icons/167x167.png",
"settings" : "unpackage/res/icons/29x29.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"spotlight" : "unpackage/res/icons/40x40.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png"
},
"iphone" : {
"app@2x" : "unpackage/res/icons/120x120.png",
"app@3x" : "unpackage/res/icons/180x180.png",
"notification@2x" : "unpackage/res/icons/40x40.png",
"notification@3x" : "unpackage/res/icons/60x60.png",
"settings@2x" : "unpackage/res/icons/58x58.png",
"settings@3x" : "unpackage/res/icons/87x87.png",
"spotlight@2x" : "unpackage/res/icons/80x80.png",
"spotlight@3x" : "unpackage/res/icons/120x120.png"
}
}
}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wx6ef5a6a74620a3e8",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3",
"h5" : {
"sdkConfigs" : {
"maps" : {
"qqmap" : {
"key" : "7DIBZ-K4HCJ-ZR2FE-FOOOP-SALFT-RLFYW"
}
}
}
}
}

View File

@@ -0,0 +1,196 @@
<template>
<view class="contact-support-page">
<!-- 全新方案:纯 CSS 手动构建的骨架屏 -->
<view v-if="loading" class="skeleton-wrapper">
<view class="skeleton-card">
<view style="padding: 40rpx 0; ">
<view class="skeleton-item skeleton-text" style="width: 100%; height: 60rpx;"></view>
</view>
</view>
</view>
<!-- 页面实际内容 -->
<view v-else class="page-content">
<view class="page-container">
<view class="card emergency-card" @click="callEmergency">
<view class="emergency-title">
<up-icon name="phone-fill" color="#fa6400" size="18"></up-icon>
<text>紧急问题?直接拨打人工客服</text>
</view>
<text class="phone-number">{{ phone }}</text>
<text class="service-time">9:00-22:00</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { Service, CNRiderLoginService } from "@/Service/CN/CNRiderLoginService";
const loading = ref<boolean>(true);
let phone=ref('')
onLoad(() => {
getData()
});
onShow(() => { });
const getData=()=>{
CNRiderLoginService.GetKefuInfo().then(res=>{
loading.value = false;
if(res.code==0){
phone.value=res.data.phone
}
})
}
const callEmergency = () => {
uni.makePhoneCall({
phoneNumber: phone.value
});
};
</script>
<style lang="scss" scoped>
@keyframes skeleton-blink {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}
.skeleton-item {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-blink 1.5s infinite linear;
}
.skeleton-rect {
border-radius: 12rpx;
}
.skeleton-text {
border-radius: 4rpx;
}
.skeleton-wrapper {
padding: 24rpx;
background-color: #f7f7f7;
.skeleton-card {
background-color: #fff;
padding: 30rpx;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
}
.contact-support-page {
background-color: #f7f7f7;
min-height: 100vh;
}
.page-container {
padding: 24rpx;
}
.card {
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 24rpx;
}
.section {
.section-title {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
display: block;
}
}
.notice-box {
display: flex;
align-items: center;
gap: 16rpx;
border-radius: 16rpx;
padding: 24rpx;
font-size: 26rpx;
&.info-box {
background-color: #ecf5ff;
color: #40a9ff;
}
}
.menu-list {
padding: 0 30rpx;
:deep(.up-cell-group) {
.up-cell {
.up-cell__body {
padding: 28rpx 0;
}
}
}
.cell-title {
display: flex;
flex-direction: column;
margin-left: 20rpx;
.title {
font-size: 30rpx;
color: #333;
}
.desc {
font-size: 24rpx;
color: #999;
margin-top: 4rpx;
}
}
}
.emergency-card {
background-color: #ffffff;
text-align: center;
padding: 30rpx;
.emergency-title {
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
font-size: 28rpx;
color: #fa6400;
font-weight: 500;
}
.phone-number {
display: block;
font-size: 44rpx;
font-weight: bold;
color: #333;
margin: 16rpx 0;
}
.service-time {
font-size: 26rpx;
color: #999;
}
}
</style>

View File

@@ -0,0 +1,743 @@
<template>
<view class="image-cropper" :style="{ zIndex }" @wheel="cropper.mousewheel">
<canvas v-if="use2d" type="2d" id="imgCanvas" class="img-canvas" :style="{
width: `${canvansWidth}px`,
height: `${canvansHeight}px`
}"></canvas>
<canvas v-else id="imgCanvas" canvas-id="imgCanvas" class="img-canvas" :style="{
width: `${canvansWidth}px`,
height: `${canvansHeight}px`
}"></canvas>
<view id="pic-preview" class="pic-preview" :change:init="cropper.initObserver" :init="initData" @touchstart="cropper.touchstart" @touchmove="cropper.touchmove" @touchend="cropper.touchend">
<image v-if="imgSrc" id="crop-image" class="crop-image" :style="cropper.imageStyles" :src="imgSrc" webp></image>
<view v-for="(item, index) in maskList" :key="item.id" :id="item.id" class="crop-mask-block" :style="cropper.maskStylesList[index]"></view>
<view v-if="showBorder" id="crop-border" class="crop-border" :style="cropper.borderStyles"></view>
<view v-if="radius > 0" id="crop-circle-box" class="crop-circle-box" :style="cropper.circleBoxStyles">
<view class="crop-circle" id="crop-circle" :style="cropper.circleStyles"></view>
</view>
<block v-if="showGrid">
<view v-for="(item, index) in gridList" :key="item.id" :id="item.id" class="crop-grid" :style="cropper.gridStylesList[index]"></view>
</block>
<block v-if="showAngle">
<view v-for="(item, index) in angleList" :key="item.id" :id="item.id" class="crop-angle" :style="cropper.angleStylesList[index]">
<view :style="[{
width: `${angleSize}px`,
height: `${angleSize}px`
}]"></view>
</view>
</block>
</view>
<slot />
<view class="fixed-bottom safe-area-inset-bottom" :style="{ zIndex: initData.area.zIndex + 99 }">
<view v-if="(rotatable || reverseRotatable) && !!imgSrc" class="action-bar">
<view v-if="reverseRotatable" class="rotate-icon" @click="cropper.rotateImage270"></view>
<view v-if="rotatable" class="rotate-icon is-reverse" @click="cropper.rotateImage90"></view>
</view>
<view v-if="!choosable" class="choose-btn" @click="cropClick">确定</view>
<block v-else-if="!!imgSrc">
<view class="rechoose" @click="chooseImage">重选</view>
<button class="button" size="mini" @click="cropClick">确定</button>
</block>
<view v-else class="choose-btn" @click="chooseImage">选择图片</view>
</view>
</view>
</template>
<!-- #ifdef APP-VUE -->
<script module="cropper" lang="renderjs">
import cropper from './qf-image-cropper.render.js';
// vue3 app renderjs中条件编译无效
cropper.setPlatform('APP');
export default {
mixins: [ cropper ]
}
</script>
<!-- #endif -->
<!-- #ifdef H5 -->
<script module="cropper" lang="renderjs">
import cropper from './qf-image-cropper.render.js';
export default {
mixins: [ cropper ]
}
</script>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN || MP-QQ -->
<script module="cropper" lang="wxs" src="./qf-image-cropper.wxs"></script>
<!-- #endif -->
<script>
/** 裁剪区域最大宽高所占屏幕宽度百分比 */
const AREA_SIZE = 75;
/** 图片默认宽高 */
const IMG_SIZE = 300;
export default {
name:"qf-image-cropper",
// #ifdef MP-WEIXIN
options: {
// 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响
styleIsolation: "isolated"
},
// #endif
props: {
/** 图片资源地址 */
src: {
type: String,
default: ''
},
/** 裁剪宽度有些平台或设备对于canvas的尺寸有限制过大可能会导致无法正常绘制 */
width: {
type: Number,
default: IMG_SIZE
},
/** 裁剪高度有些平台或设备对于canvas的尺寸有限制过大可能会导致无法正常绘制 */
height: {
type: Number,
default: IMG_SIZE
},
/** 是否绘制裁剪区域边框 */
showBorder: {
type: Boolean,
default: true
},
/** 是否绘制裁剪区域网格参考线 */
showGrid: {
type: Boolean,
default: true
},
/** 是否展示四个支持伸缩的角 */
showAngle: {
type: Boolean,
default: true
},
/** 裁剪区域最小缩放倍数 */
areaScale: {
type: Number,
default: 0.3
},
/** 图片最小缩放倍数 */
minScale: {
type: Number,
default: 1
},
/** 图片最大缩放倍数 */
maxScale: {
type: Number,
default: 5
},
/** 检查图片位置是否超出裁剪边界,如果超出则会矫正位置 */
checkRange: {
type: Boolean,
default: true
},
/** 生成图片背景色:如果裁剪区域没有完全包含在图片中时,不设置该属性生成图片存在一定的透明块 */
backgroundColor: {
type: String
},
/** 是否有回弹效果:当 checkRange 为 true 时有效,拖动时可以拖出边界,释放时会弹回边界 */
bounce: {
type: Boolean,
default: true
},
/** 是否支持翻转 */
rotatable: {
type: Boolean,
default: true
},
/** 是否支持逆向翻转 */
reverseRotatable: {
type: Boolean,
default: false
},
/** 是否支持从本地选择素材 */
choosable: {
type: Boolean,
default: true
},
/** 是否开启硬件加速,图片缩放过程中如果出现元素的“留影”或“重影”效果,可通过该方式解决或减轻这一问题 */
gpu: {
type: Boolean,
default: false
},
/** 四个角尺寸单位px */
angleSize: {
type: Number,
default: 20
},
/** 四个角边框宽度单位px */
angleBorderWidth: {
type: Number,
default: 2
},
zIndex: {
type: [Number, String]
},
/** 裁剪图片圆角半径单位px */
radius: {
type: Number,
default: 0
},
/** 生成文件的类型,只支持 'jpg' 或 'png'。默认为 'png' */
fileType: {
type: String,
default: 'png'
},
/**
* 图片从绘制到生成所需时间单位ms
* 微信小程序平台使用 `Canvas 2D` 绘制时有效
* 如绘制大图或出现裁剪图片空白等情况应适当调大该值,因 `Canvas 2d` 采用同步绘制,需自己把控绘制完成时间
*/
delay: {
type: Number,
default: 1000
},
// #ifdef H5
/**
* 页面是否是原生标题栏
* H5平台当 showAngle 为 true 时,使用插件的页面在 `page.json` 中配置了 "navigationStyle": "custom" 时,必须将此值设为 false ,否则四个可拉伸角的触发位置会有偏差。
* 注因H5平台的窗口高度是包含标题栏的而屏幕触摸点的坐标是不包含的
*/
navigation: {
type: Boolean,
default: true
}
// #endif
},
emits: ["crop"],
data() {
return {
// 用不同 id 使 v-for key 不重复
maskList: [
{ id: 'crop-mask-block-1' },
{ id: 'crop-mask-block-2' },
{ id: 'crop-mask-block-3' },
{ id: 'crop-mask-block-4' },
],
gridList: [
{ id: 'crop-grid-1' },
{ id: 'crop-grid-2' },
{ id: 'crop-grid-3' },
{ id: 'crop-grid-4' },
],
angleList: [
{ id: 'crop-angle-1' },
{ id: 'crop-angle-2' },
{ id: 'crop-angle-3' },
{ id: 'crop-angle-4' },
],
/** 本地缓存的图片路径 */
imgSrc: '',
/** 图片的裁剪宽度 */
imgWidth: IMG_SIZE,
/** 图片的裁剪高度 */
imgHeight: IMG_SIZE,
/** 裁剪区域最大宽度所占屏幕宽度百分比 */
widthPercent: AREA_SIZE,
/** 裁剪区域最大高度所占屏幕宽度百分比 */
heightPercent: AREA_SIZE,
/** 裁剪区域布局信息 */
area: {},
/** 未被缩放过的图片宽 */
oldWidth: 0,
/** 未被缩放过的图片高 */
oldHeight: 0,
/** 系统信息 */
sys: uni.getSystemInfoSync(),
scaleWidth: 0,
scaleHeight: 0,
rotate: 0,
offsetX: 0,
offsetY: 0,
use2d: false,
canvansWidth: 0,
canvansHeight: 0,
// imageStyles: {},
// maskStylesList: [{}, {}, {}, {}],
// borderStyles: {},
// gridStylesList: [{}, {}, {}, {}],
// angleStylesList: [{}, {}, {}, {}],
// circleBoxStyles: {},
// circleStyles: {},
}
},
computed: {
initData() {
// console.log('initData')
return {
timestamp: new Date().getTime(),
area: {
...this.area,
bounce: this.bounce,
showBorder: this.showBorder,
showGrid: this.showGrid,
showAngle: this.showAngle,
angleSize: this.angleSize,
angleBorderWidth: this.angleBorderWidth,
minScale: this.areaScale,
widthPercent: this.widthPercent,
heightPercent: this.heightPercent,
radius: this.radius,
checkRange: this.checkRange,
zIndex: +this.zIndex || 0,
},
sys: this.sys,
img: {
minScale: this.minScale,
maxScale: this.maxScale,
src: this.imgSrc,
width: this.oldWidth,
height: this.oldHeight,
oldWidth: this.oldWidth,
oldHeight: this.oldHeight,
gpu: this.gpu,
}
}
},
imgProps() {
return {
width: this.width,
height: this.height,
src: this.src,
}
}
},
watch: {
imgProps: {
handler(val, oldVal) {
// 自定义裁剪尺,示例如下:
this.imgWidth = Number(val.width) || IMG_SIZE;
this.imgHeight = Number(val.height) || IMG_SIZE;
let use2d = true;
// #ifndef MP-WEIXIN
use2d = false;
// #endif
// if(use2d && (this.imgWidth > 1365 || this.imgHeight > 1365)) {
// use2d = false;
// }
let canvansWidth = this.imgWidth;
let canvansHeight = this.imgHeight;
let size = Math.max(canvansWidth, canvansHeight)
let scalc = 1;
if(size > 1365) {
scalc = 1365 / size;
}
this.canvansWidth = canvansWidth * scalc;
this.canvansHeight = canvansHeight * scalc;
this.use2d = use2d;
this.initArea();
const src = val.src || this.imgSrc;
src && this.initImage(src, oldVal === undefined);
},
immediate: true
},
},
methods: {
/** 提供给wxs调用用来接收图片变更数据 */
dataChange(e) {
// console.log('dataChange', e)
this.scaleWidth = e.width;
this.scaleHeight = e.height;
this.rotate = e.rotate;
this.offsetX = e.x;
this.offsetY = e.y;
},
/** 初始化裁剪区域布局信息 */
initArea() {
// 底部操作栏高度 = 底部底部操作栏内容高度 + 设备底部安全区域高度
this.sys.offsetBottom = uni.upx2px(100) + this.sys.safeAreaInsets.bottom;
// #ifndef H5
this.sys.windowTop = 0;
this.sys.navigation = true;
// #endif
// #ifdef H5
// h5平台的窗口高度是包含标题栏的
this.sys.windowTop = this.sys.windowTop || 44;
this.sys.navigation = this.navigation;
// #endif
let wp = this.widthPercent;
let hp = this.heightPercent;
if (this.imgWidth > this.imgHeight) {
hp = hp * this.imgHeight / this.imgWidth;
} else if (this.imgWidth < this.imgHeight) {
wp = wp * this.imgWidth / this.imgHeight;
}
const size = this.sys.windowWidth > this.sys.windowHeight ? this.sys.windowHeight : this.sys.windowWidth;
const width = size * wp / 100;
const height = size * hp / 100;
const left = (this.sys.windowWidth - width) / 2;
const right = left + width;
const top = (this.sys.windowHeight + this.sys.windowTop - this.sys.offsetBottom - height) / 2;
const bottom = this.sys.windowHeight + this.sys.windowTop - this.sys.offsetBottom - top;
this.area = { width, height, left, right, top, bottom };
this.scaleWidth = width;
this.scaleHeight = height;
},
/** 从本地选取图片 */
chooseImage(options) {
// #ifdef MP-WEIXIN || MP-JD
if(uni.chooseMedia) {
uni.chooseMedia({
...options,
count: 1,
mediaType: ['image'],
success: (res) => {
this.resetData();
this.initImage(res.tempFiles[0].tempFilePath);
}
});
return;
}
// #endif
uni.chooseImage({
...options,
count: 1,
success: (res) => {
this.resetData();
this.initImage(res.tempFiles[0].path);
}
});
},
/** 重置数据 */
resetData() {
this.imgSrc = '';
this.rotate = 0;
this.offsetX = 0;
this.offsetY = 0;
this.initArea();
},
/**
* 初始化图片信息
* @param {String} url 图片链接
*/
initImage(url, isFirst) {
uni.getImageInfo({
src: url,
success: async (res) => {
if (isFirst && this.src === url) await (new Promise((resolve) => setTimeout(resolve, 50)));
this.imgSrc = res.path;
let scale = res.width / res.height;
let areaScale = this.area.width / this.area.height;
if (scale > 1) { // 横向图片
if (scale >= areaScale) { // 图片宽不小于目标宽,则高固定,宽自适应
this.scaleWidth = (this.scaleHeight / res.height) * this.scaleWidth * (res.width / this.scaleWidth);
} else { // 否则宽固定、高自适应
this.scaleHeight = res.height * this.scaleWidth / res.width;
}
} else { // 纵向图片
if (scale <= areaScale) { // 图片高不小于目标高,宽固定,高自适应
this.scaleHeight = (this.scaleWidth / res.width) * this.scaleHeight / (this.scaleHeight / res.height);
} else { // 否则高固定,宽自适应
this.scaleWidth = res.width * this.scaleHeight / res.height;
}
}
// 记录原始宽高,为缩放比列做限制
this.oldWidth = +this.scaleWidth.toFixed(2);
this.oldHeight = +this.scaleHeight.toFixed(2);
},
fail: (err) => {
console.error(err)
}
});
},
/**
* 剪切图片圆角
* @param {Object} ctx canvas 的绘图上下文对象
* @param {Number} radius 圆角半径
* @param {Number} scale 生成图片的实际尺寸与截取区域比
* @param {Function} drawImage 执行剪切时所调用的绘图方法,入参为是否执行了剪切
*/
drawClipImage(ctx, radius, scale, drawImage) {
if(radius > 0) {
ctx.save();
ctx.beginPath();
const w = this.canvansWidth;
const h = this.canvansHeight;
if(w === h && radius >= w / 2) { // 圆形
ctx.arc(w / 2, h / 2, w / 2, 0, 2 * Math.PI);
} else { // 圆角矩形
if(w !== h) { // 限制圆角半径不能超过短边的一半
radius = Math.min(w / 2, h / 2, radius);
// radius = Math.min(Math.max(w, h) / 2, radius);
}
ctx.moveTo(radius, 0);
ctx.arcTo(w, 0, w, h, radius);
ctx.arcTo(w, h, 0, h, radius);
ctx.arcTo(0, h, 0, 0, radius);
ctx.arcTo(0, 0, w, 0, radius);
ctx.closePath();
}
ctx.clip();
drawImage && drawImage(true);
ctx.restore();
} else {
drawImage && drawImage(false);
}
},
/**
* 旋转图片
* @param {Object} ctx canvas 的绘图上下文对象
* @param {Number} rotate 旋转角度
* @param {Number} scale 生成图片的实际尺寸与截取区域比
*/
drawRotateImage(ctx, rotate, scale) {
if(rotate !== 0) {
// 1. 以图片中心点为旋转中心点
const x = this.scaleWidth * scale / 2;
const y = this.scaleHeight * scale / 2;
ctx.translate(x, y);
// 2. 旋转画布
ctx.rotate(rotate * Math.PI / 180);
// 3. 旋转完画布后恢复设置旋转中心时所做的偏移
ctx.translate(-x, -y);
}
},
drawImage(ctx, image, callback) {
// 生成图片的实际尺寸与截取区域比
const scale = this.canvansWidth / this.area.width;
if(this.backgroundColor) {
if(ctx.setFillStyle) ctx.setFillStyle(this.backgroundColor);
else ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, this.canvansWidth, this.canvansHeight);
}
this.drawClipImage(ctx, this.radius, scale, () => {
this.drawRotateImage(ctx, this.rotate, scale);
const r = this.rotate / 90;
ctx.drawImage(
image,
[
(this.offsetX - this.area.left),
(this.offsetY - this.area.top),
-(this.offsetX - this.area.left),
-(this.offsetY - this.area.top)
][r] * scale,
[
(this.offsetY - this.area.top),
-(this.offsetX - this.area.left),
-(this.offsetY - this.area.top),
(this.offsetX - this.area.left)
][r] * scale,
this.scaleWidth * scale,
this.scaleHeight * scale
);
});
},
/**
* 绘图
* @param {Object} canvas
* @param {Object} ctx canvas 的绘图上下文对象
* @param {String} src 图片路径
* @param {Function} callback 开始绘制时回调
*/
draw2DImage(canvas, ctx, src, callback) {
// console.log('draw2DImage', canvas, ctx, src, callback)
if(canvas) {
const image = canvas.createImage();
image.onload = () => {
this.drawImage(ctx, image);
// 如果觉得`生成时间过长`或`出现生成图片空白`可尝试调整延迟时间
callback && setTimeout(callback, this.delay);
};
image.onerror = (err) => {
console.error(err)
uni.hideLoading();
};
image.src = src;
} else {
this.drawImage(ctx, src);
setTimeout(() => {
ctx.draw(false, callback);
}, 200);
}
},
/**
* 画布转图片到本地缓存
* @param {Object} canvas
* @param {String} canvasId
*/
canvasToTempFilePath(canvas, canvasId) {
// console.log('canvasToTempFilePath', canvas, canvasId)
uni.canvasToTempFilePath({
canvas,
canvasId,
x: 0,
y: 0,
width: this.canvansWidth,
height: this.canvansHeight,
destWidth: this.imgWidth, // 必要,保证生成图片宽度不受设备分辨率影响
destHeight: this.imgHeight, // 必要,保证生成图片高度不受设备分辨率影响
fileType: this.fileType, // 目标文件的类型默认png
success: (res) => {
// 生成的图片临时文件路径
this.handleImage(res.tempFilePath);
},
fail: (err) => {
uni.hideLoading();
uni.showToast({ title: '裁剪失败,生成图片异常!', icon: 'none' });
}
}, this);
},
/** 确认裁剪 */
cropClick() {
uni.showLoading({ title: '裁剪中...', mask: true });
if(!this.use2d) {
const ctx = uni.createCanvasContext('imgCanvas', this);
ctx.clearRect(0, 0, this.canvansWidth, this.canvansHeight);
this.draw2DImage(null, ctx, this.imgSrc, () => {
this.canvasToTempFilePath(null, 'imgCanvas');
});
return;
}
// #ifdef MP-WEIXIN
const query = uni.createSelectorQuery().in(this);
query.select('#imgCanvas')
.fields({ node: true, size: true })
.exec((res) => {
const canvas = res[0].node;
const dpr = uni.getSystemInfoSync().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
ctx.clearRect(0, 0, this.canvansWidth, this.canvansHeight);
this.draw2DImage(canvas, ctx, this.imgSrc, () => {
this.canvasToTempFilePath(canvas);
});
});
// #endif
},
handleImage(tempFilePath){
// 在H5平台下tempFilePath 为 base64
// console.log(tempFilePath)
uni.hideLoading();
this.$emit('crop', { tempFilePath });
}
}
}
</script>
<style lang="scss" scoped>
.image-cropper {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: hidden;
display: flex;
flex-direction: column;
background-color: #000;
.img-canvas {
position: absolute !important;
transform: translateX(-100%);
}
.pic-preview {
width: 100%;
flex: 1;
position: relative;
.crop-mask-block {
background-color: rgba(51, 51, 51, 0.8);
z-index: 2;
position: fixed;
box-sizing: border-box;
pointer-events: none;
}
.crop-circle-box {
position: fixed;
box-sizing: border-box;
z-index: 2;
pointer-events: none;
overflow: hidden;
.crop-circle {
width: 100%;
height: 100%;
}
}
.crop-image {
padding: 0 !important;
margin: 0 !important;
border-radius: 0 !important;
display: block !important;
backface-visibility: hidden;
}
.crop-border {
position: fixed;
border: 1px solid #fff;
box-sizing: border-box;
z-index: 3;
pointer-events: none;
}
.crop-grid {
position: fixed;
z-index: 3;
border-style: dashed;
border-color: #fff;
pointer-events: none;
opacity: 0.5;
}
.crop-angle {
position: fixed;
z-index: 3;
border-style: solid;
border-color: #fff;
pointer-events: none;
}
}
.fixed-bottom {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
flex-direction: row;
background-color: $uni-bg-color-grey;
.action-bar {
position: absolute;
top: -90rpx;
left: 10rpx;
display: flex;
.rotate-icon {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAABCFJREFUaEPtml3IpVMUx3//ko/ChTIyiGFSMyhllI8bc4F85yuNC2FCqLmQC1+FZORiEkUMNW7UjKjJULgxV+NzSkxDhEkZgwsyigv119J63p7zvOc8z37OmXdOb51dz82711r7/99r7bXXXucVi3xokeNnRqCvB20fDmwAlgK/5bcD+FTSr33tHXQP2H4MeHQE0A+B5yRtLiUyDQJrgVc6AAaBpyV93kXkoBMIQLbfBS5NcK8BRwDXNcD+AdwnaVMbiWkRCPBBohpxHuK7M7865sclRdgNHVMhkF6IMIpwirFEUhzo8M7lwIvASTXEqyVtH8ZgagQSbOzsDknv18HZXpHn5IL8+94IOUm7miSmSqAttjPdbgGuTrnNktYsGgLpoYuAD2qg1zRTbG8P2D4SOC6/Q7vSHPALsE/S7wWy80RsPw/ckxMfSTq/LtRJwPbxwF3ASiCUTxwHCPAnEBfVF8AWSTtL7Ng+LfWOTfmlkn6udFsJ5K15R6a4kvX6yGyUFBvTOWzHXXFzCt4g6c1OArYj9iIGh43YgR+BvztXh1PSa4cMkd0jaVmXDduPAE+k3HpJD7cSGFKvfAc8FQUX8IOk/V2L1udtB/hTgdOBW4Aba/M7Ja1qs2f7euCNlHlZUlx4/495IWQ7Jl+qGbxX0gt9AHfJ2o6zFBVoNVrDKe+F3Sm8VdK1bQQ+A85JgXckXdkFaJx527cC9TpnVdvBtl3h2iapuhsGPdBw1b9xnUvaNw7AEh3bnwDnpuwGSfeP0rN9NvAMELXRXFkxEEK2nwQeSiOtRVQJwC4Z29cAW1Nuu6TVXTrN+SaBt4ErUug2Sa/2NdhH3vZy4NvU2S/p6D768w5xI3WOrAD7LtISFpGdIhVXKfaYvjd20wP13L9M0p4DBbaFRKToSLExVkr6qs+aIwlI6iwz+izUQqC+ab29PiMwqRcmPXczD8w8MFj1zg7xXEqbpdHCw7FgWSjafZL+KcQxtpjteCeflwYulFR/J3TabSslVkj6utPChAK2f6q9uZdLitKieLQRuExSvX9ZbLRUMFs09efpUZL+KtUfVo1GW/umNHC3pOhRLtiwfSbwZS6wV9IJfRdreuBBYH0a2STp9r4G+8jbXgc8mzoDT8VSO00ClwDv1ZR7XyylC4ec7ejaLUmdsV6Aw7oSbwFXpdFdks7qA6pU1na0aR6owgeIR/1cx63UzjAC0YXYVjMQHlkn6ZtSo21ytuPZGKFagQ/xsXZ/3iGuFrYdjafXG0DiQMeBi47c9/GV3BO247UV38n5o0UAP6xmu7jFOGxjRr66On5NPBDOCBsDTapxjHY1dyOcolNXnYlx1himE53p2PmNkxosevfavhg4Izt2k7TXPwZ2S6p6QZPin/2rwcQ7OKmBohCadJGF1P8PG6aaQBKVX/8AAAAASUVORK5CYII=');
background-size: 60% 60%;
background-repeat: no-repeat;
background-position: center;
width: 80rpx;
height: 80rpx;
&.is-reverse {
transform: rotateY(180deg);
}
}
}
.rechoose {
color: $uni-color-primary;
padding: 0 $uni-spacing-row-lg;
line-height: 100rpx;
}
.choose-btn {
color: $uni-color-primary;
text-align: center;
line-height: 100rpx;
flex: 1;
}
.button {
margin: auto $uni-spacing-row-lg auto auto;
background-color: $uni-color-primary;
color: #fff;
}
}
.safe-area-inset-bottom {
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom); // 兼容 IOS<11.2
padding-bottom: env(safe-area-inset-bottom); // 兼容 IOS>=11.2
}
}
</style>

View File

@@ -0,0 +1,180 @@
<template>
<view class="abnormal-reported-page">
<!-- 成功提示区域 -->
<view class="success-section">
<view class="success-icon">
<up-icon name="checkmark-circle-fill" size="80" color="#4CD964"></up-icon>
</view>
<view class="success-title">异常已上报!</view>
<view class="success-desc">配送计时已暂停,不影响您的准时率</view>
</view>
<!-- 订单信息卡片 -->
<view class="order-card">
<view class="order-item">
<text class="order-label">订单号</text>
<text class="order-value" style="font-weight: 600;" >MT20251017123456</text>
</view>
<view class="order-item">
<text class="order-label">商品信息</text>
<text class="order-value">共3件商品</text>
</view>
<view class="order-item">
<text class="order-label">配送地址</text>
<text class="order-value">XX小区3栋502室</text>
</view>
<view class="order-item">
<text class="order-label">用户备注</text>
<text class="order-value remark-text">请放门口,勿按门铃</text>
</view>
<!-- 查看详情按钮 -->
<view class="view-detail-btn">
<text>查看详情</text>
<up-icon name="arrow-right" size="20" color="#999"></up-icon>
</view>
</view>
<!-- 底部占位 -->
<view class="bottom-space"></view>
<!-- 底部按钮区域 -->
<view class="bottom-buttons">
<up-button type="primary" size="default" class="continue-btn">
继续接单
</up-button>
<up-button type="default" size="default" class="view-progress-btn">
查看异常进度
</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app'
// 页面加载时的逻辑
onLoad(() => {
console.log('异常上报成功页面加载')
})
</script>
<style lang="scss">
page{
background-color: #fff;
}
// 成功提示区域样式
.success-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 30rpx;
background-color: #F6FFFB;
margin: 20rpx;
}
.success-icon {
margin-bottom: 30rpx;
}
.success-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
margin-bottom: 16rpx;
}
.success-desc {
font-size: 28rpx;
color: #999;
}
// 订单卡片样式
.order-card {
background-color: #fff;
padding: 30rpx;
border-radius: 12rpx;
margin: 0 30rpx;
box-shadow: 0 0 10rpx 0 #e2e2e2;
}
.order-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
}
.order-item:last-child {
margin-bottom: 0;
}
.order-label {
font-size: 28rpx;
color: #666;
}
.order-value {
font-size: 28rpx;
color: #333;
text-align: right;
flex: 1;
margin-left: 30rpx;
}
.remark-text {
color: #FF6F00;
}
// 查看详情按钮样式
.view-detail-btn {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 30rpx;
padding-top: 30rpx;
border-top: 1rpx solid #f0f0f0;
color: #999;
font-size: 28rpx;
}
// 底部占位
.bottom-space {
height: 280rpx;
}
// 底部按钮区域样式
.bottom-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
gap: 20rpx;
padding: 20rpx;
background-color: #fff;
box-shadow: 0 -2rpx 20rpx rgba(0, 0, 0, 0.05);
display: flex;
}
.continue-btn {
border-radius: 50rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
background-color: #007AFF;
margin-bottom: 20rpx;
}
.view-progress-btn {
border-radius: 50rpx;
height: 100rpx;
line-height: 98rpx;
font-size: 32rpx;
color: #007AFF;
border: 1rpx solid #007AFF;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,103 @@
## Calendar 日历
> **组件名uni-calendar**
> 代码块: `uCalendar`
日历组件
> **注意事项**
> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。
> - 本组件农历转换使用的js是 [@1900-2100区间内的公历、农历互转](https://github.com/jjonline/calendar.js)
> - 仅支持自定义组件模式
> - `date`属性传入的应该是一个 String ,如: 2019-06-27 ,而不是 new Date()
> - 通过 `insert` 属性来确定当前的事件是 @change 还是 @confirm 。理应合并为一个事件,但是为了区分模式,现使用两个事件,这里需要注意
> - 弹窗模式下无法阻止后面的元素滚动,如有需要阻止,请在弹窗弹出后,手动设置滚动元素为不可滚动
### 安装方式
本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。
如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55)
### 基本用法
在 ``template`` 中使用组件
```html
<view>
<uni-calendar
:insert="true"
:lunar="true"
:start-date="'2019-3-2'"
:end-date="'2019-5-20'"
@change="change"
/>
</view>
```
### 通过方法打开日历
需要设置 `insert` 为 `false`
```html
<view>
<uni-calendar
ref="calendar"
:insert="false"
@confirm="confirm"
/>
<button @click="open">打开日历</button>
</view>
```
```javascript
export default {
data() {
return {};
},
methods: {
open(){
this.$refs.calendar.open();
},
confirm(e) {
console.log(e);
}
}
};
```
## API
### Calendar Props
| 属性名 | 类型 | 默认值| 说明 |
| - | - | - | - |
| date | String |- | 自定义当前时间,默认为今天 |
| lunar | Boolean | false | 显示农历 |
| startDate | String |- | 日期选择范围-开始日期 |
| endDate | String |- | 日期选择范围-结束日期 |
| range | Boolean | false | 范围选择 |
| insert | Boolean | false | 插入模式,可选值ture插入模式false弹窗模式默认为插入模式 |
|clearDate |Boolean |true |弹窗模式是否清空上次选择内容 |
| selected | Array |- | 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}] |
|showMonth | Boolean | true | 是否显示月份为背景 |
### Calendar Events
| 事件名 | 说明 |返回值|
| - | - | - |
| open | 弹出日历组件,`insert :false` 时生效|- |
## 组件示例
点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar](https://hellouniapp.dcloud.net.cn/pages/extUI/calendar/calendar)

View File

@@ -0,0 +1,588 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 余额区域骨架屏 -->
<view class="skeleton-balance-section">
<view class="skeleton-balance-label"></view>
<view class="skeleton-balance-amount"></view>
<view class="skeleton-balance-current"></view>
</view>
<!-- 账户选择区域骨架屏 -->
<view class="skeleton-account-section">
<view class="skeleton-section-title"></view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
</view>
<!-- 提示信息骨架屏 -->
<view class="skeleton-tip-section">
<view class="skeleton-tip-icon"></view>
<view class="skeleton-tip-content">
<view class="skeleton-tip-line"></view>
<view class="skeleton-tip-line-small"></view>
</view>
</view>
<!-- 确认按钮骨架屏 -->
<view class="skeleton-confirm-section">
<view class="skeleton-confirm-button"></view>
</view>
</view>
<!-- 真实内容 -->
<view v-else class="withdraw-container">
<!-- 可提现金额 -->
<view class="balance-section">
<text class="balance-label">可提现金额</text>
<text class="balance-amount">¥{{ riderAcc.account }}</text>
<text class="current-balance">当前钱包余额</text>
</view>
<!-- 提现账户选择 -->
<view class="account-section">
<text class="section-title">提现账户</text>
<view class=""
style="display: flex;align-items: center; border-bottom: 4rpx solid #e2e2e2;padding-bottom: 10px; margin-bottom: 10px;">
<up-icon name="zhifubao-circle-fill" size="30" color="#1890ff"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
支付宝
</view>
<view @click="changePay(0)" class="">
<view v-if="current==0" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
<view class="" style="display: flex;align-items: center;">
<up-icon name="weixin-circle-fill" size="30" color="#28C445"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
微信
</view>
<view @click="changePay(1)" class="">
<view v-if="current==1" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
</view>
<!-- 提现账号 -->
<view class="" v-if="current!=null" style="margin: 20rpx 30rpx; ">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
账号信息
</view>
<up-form labelPosition="top" label-width="200" :model="account" ref="form1">
<up-form-item label="姓名" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.name" @change="changePrice" placeholder="请输入姓名" border="none"></up-input>
</up-form-item>
<up-form-item :label="current==0? '支付宝账号':'微信账号'" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.id" :placeholder="current==0? '请输入支付宝账号':'请输入微信账号'"
border="none"></up-input>
</up-form-item>
<up-form-item label="提现金额" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.price" type="digit" placeholder="请输入提现金额" border="none"></up-input>
</up-form-item>
</up-form>
</view>
<!-- 提示信息 -->
<view class="tip-section">
<up-icon name="clock" size="18" color="#1890ff"></up-icon>
<view class="" style="margin-left: 10rpx;">
<view class="tip-text">预计 T+1 工作日到账</view>
<view class="tip-text" style="color: #666666; font-size: 24rpx; margin-top: 10rpx; ">无手续费</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="confirm-section">
<button class="confirm-button" @click="confirmWithdraw">确认提现</button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
let current = ref(null)
let loading = ref(true)
let account = ref({
id: '',
price: "",
name: ''
})
let riderAcc = ref<any>({})
onLoad(() => {
getData()
})
const getData = () => {
CNRiderDataService.GetRiderAccInfo().then(res => {
loading.value = false
if (res.data) {
riderAcc.value = res.data.riderAcc
}
})
}
const changePay = (item : any) => {
current.value = item
}
const changePrice = (e : string) => {
console.log(e);
}
// 确认提现
const confirmWithdraw = () => {
if (!account.value.name) {
Service.Msg('请输入姓名!')
return
}
if (!account.value.price) {
Service.Msg('请输入提现金额!')
return
}
if (!account.value.id) {
Service.Msg('请输入账户号!')
return
}
uni.showModal({
title: '提示',
content: '请仔细确认身份信息?',
success: function (res) {
if (res.confirm) {
CNRiderOrderService.AddRiderWith(Number(account.value.price), current.value == 0 ? '支付宝' : '微信', account.value.name, account.value.id).then(res => {
if (res.code == 0) {
Service.Msg('提现成功')
setTimeout(() => {
Service.GoPage('/pages/my/withDrowList')
}, 1000)
} else {
Service.Msg(res.msg)
}
})
} else {
// 用户点击取消后的操作
}
}
});
};
</script>
<style scoped>
.withdraw-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 选择 */
.radio-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid var(--nav-mian);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.radio-inner {
width: 20rpx;
height: 20rpx;
background-color: var(--nav-mian);
border-radius: 50%;
}
.radio-no-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #dadada;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #fff;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #000;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.help-icon {
width: 40rpx;
height: 40rpx;
}
/* 余额显示区域 */
.balance-section {
background-color: #fff;
padding: 40rpx 30rpx;
}
.balance-label {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 60rpx;
font-weight: bold;
color: #ff4757;
display: block;
margin-bottom: 10rpx;
}
.current-balance {
font-size: 28rpx;
color: #999;
}
/* 账户选择区域 */
.account-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 20rpx;
}
.account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.account-item:last-child {
border-bottom: none;
}
.account-info {
display: flex;
align-items: center;
}
.account-icon {
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
background-color: #1677ff;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
border: 2rpx solid #000;
}
.account-icon.wechat {
background-color: #07c160;
}
.icon-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.account-name {
font-size: 32rpx;
color: #333;
}
/* 提示信息区域 */
.tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.tip-text {
font-size: 28rpx;
margin-left: 10rpx;
}
/* 确认按钮区域 */
.confirm-section {
background-color: #fff;
position: fixed;
width: 100vw;
left: 0;
bottom: 0;
padding: 30rpx;
}
.confirm-button {
width: 100%;
height: 90rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
display: flex;
align-items: center;
justify-content: center;
border: none;
}
.confirm-button:active {
background-color: #0958d9;
}
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
/* 骨架屏通用样式 */
.skeleton-balance-section,
.skeleton-account-section,
.skeleton-tip-section {
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
/* 余额区域骨架屏 */
.skeleton-balance-section {
background-color: #fff;
}
.skeleton-balance-label {
width: 30%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-amount {
width: 40%;
height: 60rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-current {
width: 25%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 账户选择区域骨架屏 */
.skeleton-account-section {
background-color: #fff;
}
.skeleton-section-title {
width: 25%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 30rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 4rpx solid #e2e2e2;
}
.skeleton-account-item:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 0;
}
.skeleton-account-icon {
width: 60rpx;
height: 60rpx;
background-color: #e6e6e6;
border-radius: 12rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-name {
flex: 1;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-radio {
width: 36rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 50%;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 提示信息骨架屏 */
.skeleton-tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.skeleton-tip-icon {
width: 40rpx;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 50%;
margin-right: 15rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-content {
flex: 1;
}
.skeleton-tip-line {
width: 70%;
height: 28rpx;
background-color: #e6e6e6;
margin-bottom: 15rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-line-small {
width: 50%;
height: 24rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 确认按钮骨架屏 */
.skeleton-confirm-section {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
padding: 30rpx;
}
.skeleton-confirm-button {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
</style>

View File

@@ -0,0 +1,112 @@
<template>
<view class="home">
<view class=""
style=" margin-top: 300rpx; display: flex; flex-direction: column; justify-content: center; align-items: center;">
<image :src="Service.GetIconImg('/static/dele/logo.png')" style="width: 150rpx; height: 150rpx; "
mode=""></image>
<view class="" style="font-size: 36rpx; font-weight: 600; ">
美味到家
</view>
</view>
<view class="" style="margin: 30rpx 30rpx;">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
欢迎登陆
</view>
<view class="" style="font-size: 28rpx; margin-top: 20rpx; ">
手机号登录,安全又便捷
</view>
<view class="" style="margin-top: 30rpx;">
<up-input v-model="login.phone" type="number" shape='circle'
:customStyle="{'padding':'20rpx 30rpx','font-size':'32rpx'}" placeholder="请输入手机号" clearable='true'
border="surround"></up-input>
</view>
<view class=""
style=" display: flex; align-items: center; justify-content: space-between; margin-top: 30rpx; border: 1rpx solid #dadbde; box-sizing: border-box; padding: 20rpx 30rpx; border-radius: 200rpx; ">
<up-code-input v-model="login.code" mode="line" size='24'></up-code-input>
<view class="wrap">
<up-toast ref="uToastRef"></up-toast>
<up-code :seconds="seconds" @end="end" @start="start" ref="uCodeRef" @change="codeChange"></up-code>
<view @click="getCode">{{tips}}</view>
</view>
</view>
<view class="" style="text-align: center; color: #888; margin-top: 30rpx; ">
登录即代表同意
<text style="color: var(--nav-banbacor);">《用户协议》</text>
<text style="color: var(--nav-banbacor);">《隐私政策》</text>
</view>
<view class="" style="text-align: center; margin-top: 20rpx;">
新用户?系统将自动为您注册
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service } from "@/Service/Service"
import { ref } from "vue";
let login = ref({
phone: '',
code: ''
})
const tips = ref('');
const seconds = ref(60);
const uCodeRef = ref(null);
onLoad(() => {
});
onShow(() => {
});
const codeChange = (text) => {
tips.value = text;
};
const getCode = () => {
if (uCodeRef.value.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码',
});
setTimeout(() => {
uni.hideLoading();
// 这里此提示会被start()方法中的提示覆盖
Service.Msg('验证码已发送')
// 通知验证码组件内部开始倒计时
uCodeRef.value.start();
}, 2000);
} else {
Service.Msg('倒计时结束后再发送')
}
};
const end = () => {
console.log('倒计时结束');
};
const start = () => {
console.log('倒计时开始');
};
</script>
<style lang="scss">
.home {
width: 100vw;
height: 100vh;
background: linear-gradient(to bottom, var(--nav-mian), #fff 40%);
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,807 @@
<template>
<view v-if="isLoading" class="skeleton-container" style="padding-top: 80rpx;" >
<!-- 统计数据区域骨架 -->
<view class="skeleton-stats-section">
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
</view>
<!-- 接单按钮骨架 -->
<view class="skeleton-action-section">
<view class="skeleton-accept-btn"></view>
</view>
<!-- 标签栏骨架 -->
<view class="skeleton-tab-bar">
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<!-- 任务列表骨架 -->
<view class="skeleton-task-list">
<!-- 任务卡片骨架 -->
<view class="skeleton-task-section" v-for="i in 3" :key="i">
<!-- 高价单标签骨架 -->
<view class="skeleton-tag-row">
<view class="skeleton-high-price-tag"></view>
<view class="skeleton-phone-btn"></view>
</view>
<!-- 商家信息骨架 -->
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<!-- 地址信息骨架 -->
<view class="skeleton-address-info">
<view class="skeleton-address-text"></view>
</view>
<!-- 价格和取餐时间骨架 -->
<view class="skeleton-price-time-row">
<view class="skeleton-pickup-code"></view>
<view class="skeleton-pickup-time"></view>
</view>
<!-- 立即抢单按钮骨架 -->
<view class="skeleton-grab-btn"></view>
</view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="rider-home" style="padding-top: 60rpx;" >
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥{{userData.dayAmount.toFixed(2)}}</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">{{userData.dayOrderCount}}单</text>
</view>
<view class="stat-item">
<text class="stat-label">配送中</text>
<text class="stat-value ongoing">{{ userData.takeOrderCount }}单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button :disabled='riderInfo.status===0' type="primary" shape="circle" size="default"
class="accept-orders-btn"
@click="toggleAcceptOrders">{{ riderInfo.status==0?'审核中':(riderInfo.isOnline == 0 ? '已下线' : '已上线') }}</up-button>
</view>
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view v-if="riderInfo.status===1" class="" style="padding: 0 30rpx;">
<!-- -->
<view v-for="(orderItem,orderIndex) in orderList " @click="gopage(orderItem.orderId)" :key="orderIndex"
class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="high-price-tag"
:style="{'border':activeTab==0?'1rpx solid #FF7875':(activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14'),'color':activeTab==0?'#FF7875':(activeTab==1?'#52C41A':'#FAAD14') }"
style="background-color: #fff;">
<text class="high-price-text">{{activeTab==0? '新订单':(activeTab==1? '待取单':'配送中')}}</text>
</view>
<view class="" @click.stop="call(orderItem.phone)" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">{{ orderItem.storeName }}</text>
<text v-if="activeTab==1" class="distance">{{ distance(orderItem.distance) }}</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text"> {{orderItem.address }}</text>
<!-- <text v-if="activeTab!==0" class="address-text">共3件商品</text> -->
</view>
<!-- 商品次数-->
<!-- <view class="address-info">
<text class="address-text">共3件商品</text>
<view class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
</view> -->
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">A121</text>
</view>
<view v-if="activeTab!==1" class="">
<text class="address-text">据您{{ distance(orderItem.distance) }}</text>
</view>
<view class="pickup-time">
<up-icon name="clock" :color="activeTab==0?'#FAAD14':'#FF0000'" size="16" />
<text class="time-text"
:style="{'color':activeTab==0?'#FAAD14':'#FF0000'}">{{ orderItem.makeTime.split(' ').length==2?'预计'+orderItem.makeTime.split(' ')[1]+'送达':orderItem.makeTime }}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<view class="" style="display: flex; ">
<view class="" style="width: 100%;"
@click.stop="activeTab==0?takeOrder(orderItem.orderId):pickFood(orderItem.orderId)">
<button type="primary" :color="activeTab==0?'#1890FF':'#52C41A'" size="large"
class="grab-btn">{{ activeTab==0?'立即接单':(activeTab==1?'我已取餐':'确认送达')}}</button>
</view>
<view class="" style="width: 20rpx;" v-if="activeTab!=0">
</view>
<view style="width: 100%;" v-if="activeTab!=0" @click.stop="Service.GoPage('/pages/order/orderMap?orderId='+orderItem.orderId)" class="">
<button type="primary" color="#1890FF"
class="grab-btn">{{activeTab==1?'导航取餐':'导航送餐'}}</button>
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
<view v-else class="" style="font-weight: bold; text-align: center; font-size: 32rpx; margin-top: 100rpx; ">
信息审核中·暂时无法接单
</view>
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const isLoading = ref(true);
let status = ref('nomore')
let page = ref(1)
let userData = ref({
dayAmount: 0,
dayOrderCount: 0,
takeOrderCount: 0
})
const tabs = ['新订单', '待取货', '配送中']
const activeTab = ref(0)
let riderInfo = ref<any>({})
let orderList = ref<Array<any>>([])
onLoad(() => {
uni.$on(`newOrder`, (data) => {
newOrder()
});
})
onShow(() => {
getData()
getOrderData()
})
// 有新订单
const newOrder = () =>{
audioPlay()
}
const getData = () => {
CNRiderDataService.GetRiderHomeInfo().then(res => {
isLoading.value = false
riderInfo.value = res.data.riderInfo
userData.value = res.data
if (res.data.riderInfo.status === -1) {
Service.GoPage('/pages/my/completeData')
}
})
}
const getOrderData = () => {
status.value = 'loadmore'
page.value = 1
orderList.value = []
getOrderList()
}
//获取订单
const getOrderList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
if (activeTab.value == 0) {
CNRiderOrderService.GetRiderOrderList(page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
} else {
CNRiderOrderService.GetRiderTakeOrderList(activeTab.value == 1 ? 0 : 1, page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
}
// 接单
const takeOrder = (orderId : string) => {
CNRiderOrderService.RiderTakeOrder(orderId).then(res => {
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 取餐
const pickFood = (orderId : string) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId, activeTab.value).then(res => {
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
getOrderData()
}
// 切换接单状态
const toggleAcceptOrders = () => {
CNRiderDataService.UpdateRiderOnline().then(res => {
if (res.data) {
getData()
} else {
Service.Msg(res.msg)
}
})
};
// 距离计算
const distance = (item : any) => {
if (item < 0) {
return Number(item * 100).toFixed(2) + 'm'
} else {
return Number(item).toFixed(2) + 'km'
}
}
// 拨打电话
const call = (e : string) => {
uni.makePhoneCall({
phoneNumber: e
})
}
// 页面跳转
const gopage = (id) => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder?orderId=' + id)
} else {
Service.GoPage('/pages/order/orderDetail?orderId=' + id+'&type='+activeTab.value)
}
}
const audioPlay = () => {
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = true;
innerAudioContext.src = '/static/order.mp3';
innerAudioContext.onPlay(() => {
console.log('开始播放');
});
innerAudioContext.onError((res) => {
console.log(res.errMsg);
console.log(res.errCode);
});
}
</script>
<style scoped lang="scss">
page {
background-color: #F6f6f6;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 顶部标签栏 */
.tab-bar {
margin-top: 20rpx;
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: baseline;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx;
}
/* 统计数据区域骨架 */
.skeleton-stats-section {
background-color: #ffffff;
margin: 0 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.skeleton-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.skeleton-stat-label {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-stat-value {
width: 150rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 接单按钮骨架 */
.skeleton-action-section {
margin: 20rpx;
}
.skeleton-accept-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 标签栏骨架 */
.skeleton-tab-bar {
display: flex;
background-color: #ffffff;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #e5e5e5;
}
.skeleton-tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.skeleton-tab-text {
width: 100rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 任务列表骨架 */
.skeleton-task-list {
padding: 0 20rpx;
}
.skeleton-task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签骨架 */
.skeleton-tag-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.skeleton-high-price-tag {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-phone-btn {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 商家信息骨架 */
.skeleton-merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-distance {
width: 80rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 地址信息骨架 */
.skeleton-address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.skeleton-address-text {
width: 100%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 价格和时间骨架 */
.skeleton-price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-pickup-code {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-pickup-time {
width: 200rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 按钮骨架 */
.skeleton-grab-btn {
margin-top: 25rpx;
height: 80rpx;
background-color: #e6e6e6;
border-radius: 40rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-color: #e6e6e6;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e6e6e6;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

View File

@@ -0,0 +1,580 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 余额区域骨架屏 -->
<view class="skeleton-balance-section">
<view class="skeleton-balance-label"></view>
<view class="skeleton-balance-amount"></view>
<view class="skeleton-balance-current"></view>
</view>
<!-- 账户选择区域骨架屏 -->
<view class="skeleton-account-section">
<view class="skeleton-section-title"></view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
</view>
<!-- 提示信息骨架屏 -->
<view class="skeleton-tip-section">
<view class="skeleton-tip-icon"></view>
<view class="skeleton-tip-content">
<view class="skeleton-tip-line"></view>
<view class="skeleton-tip-line-small"></view>
</view>
</view>
<!-- 确认按钮骨架屏 -->
<view class="skeleton-confirm-section">
<view class="skeleton-confirm-button"></view>
</view>
</view>
<!-- 真实内容 -->
<view v-else class="withdraw-container">
<!-- 可提现金额 -->
<view class="balance-section">
<text class="balance-label">可提现金额</text>
<text class="balance-amount">¥{{ riderAcc.account }}</text>
<text class="current-balance">当前钱包余额</text>
</view>
<!-- 提现账户选择 -->
<view class="account-section">
<text class="section-title">提现账户</text>
<view class=""
style="display: flex;align-items: center; border-bottom: 4rpx solid #e2e2e2;padding-bottom: 10px; margin-bottom: 10px;">
<up-icon name="zhifubao-circle-fill" size="30" color="#1890ff"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
支付宝
</view>
<view @click="changePay(0)" class="">
<view v-if="current==0" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
<view class="" style="display: flex;align-items: center;">
<up-icon name="weixin-circle-fill" size="30" color="#28C445"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
微信
</view>
<view @click="changePay(1)" class="">
<view v-if="current==1" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
</view>
<!-- 提现账号 -->
<view class="" v-if="current!=null" style="margin: 20rpx 30rpx; ">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
支付宝账号信息
</view>
<up-form labelPosition="top" label-width="200" :model="account" ref="form1">
<up-form-item label="姓名" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.name" @change="changePrice" placeholder="请输入姓名" border="none"></up-input>
</up-form-item>
<up-form-item :label="current==0? '支付宝账号':'微信账号'" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.id" :placeholder="current==0? '请输入支付宝账号':'请输入微信账号'"
border="none"></up-input>
</up-form-item>
<up-form-item label="提现金额" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.price" type="digit" placeholder="请输入提现金额" border="none"></up-input>
</up-form-item>
</up-form>
</view>
<!-- 提示信息 -->
<view class="tip-section">
<up-icon name="clock" size="18" color="#1890ff"></up-icon>
<view class="" style="margin-left: 10rpx;">
<view class="tip-text">预计 T+1 工作日到账</view>
<view class="tip-text" style="color: #666666; font-size: 24rpx; margin-top: 10rpx; ">无手续费</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="confirm-section">
<button class="confirm-button" @click="confirmWithdraw">确认提现</button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
let current = ref(null)
let loading = ref(true)
let account = ref({
id: '',
price: "",
name: ''
})
let riderAcc=ref<any>({})
onLoad(() => {
getData()
})
const getData=()=>{
CNRiderDataService.GetRiderAccInfo().then(res=>{
loading.value = false
if(res.data){
riderAcc.value=res.data.riderAcc
}
})
}
const changePay = (item : any) => {
current.value = item
}
const changePrice = (e : string) => {
console.log(e);
}
// 确认提现
const confirmWithdraw = () => {
if(!account.value.name){
Service.Msg('请输入姓名!')
return
}
if(!account.value.price){
Service.Msg('请输入提现金额!')
return
}
if(!account.value.id){
Service.Msg('请输入账户号!')
return
}
uni.showModal({
title: '提示',
content: '请仔细确认身份信息?',
success: function (res) {
if (res.confirm) {
CNRiderOrderService.AddRiderWith(Number(account.value.price),current.value==0?'支付宝':'微信',account.value.name,account.value.id).then(res=>{
})
} else {
// 用户点击取消后的操作
}
}
});
};
</script>
<style scoped>
.withdraw-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 选择 */
.radio-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid var(--nav-mian);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.radio-inner {
width: 20rpx;
height: 20rpx;
background-color: var(--nav-mian);
border-radius: 50%;
}
.radio-no-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #dadada;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #fff;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #000;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.help-icon {
width: 40rpx;
height: 40rpx;
}
/* 余额显示区域 */
.balance-section {
background-color: #fff;
padding: 40rpx 30rpx;
}
.balance-label {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 60rpx;
font-weight: bold;
color: #ff4757;
display: block;
margin-bottom: 10rpx;
}
.current-balance {
font-size: 28rpx;
color: #999;
}
/* 账户选择区域 */
.account-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 20rpx;
}
.account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.account-item:last-child {
border-bottom: none;
}
.account-info {
display: flex;
align-items: center;
}
.account-icon {
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
background-color: #1677ff;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
border: 2rpx solid #000;
}
.account-icon.wechat {
background-color: #07c160;
}
.icon-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.account-name {
font-size: 32rpx;
color: #333;
}
/* 提示信息区域 */
.tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.tip-text {
font-size: 28rpx;
margin-left: 10rpx;
}
/* 确认按钮区域 */
.confirm-section {
background-color: #fff;
position: fixed;
width: 100vw;
left: 0;
bottom: 0;
padding: 30rpx;
}
.confirm-button {
width: 100%;
height: 90rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
display: flex;
align-items: center;
justify-content: center;
border: none;
}
.confirm-button:active {
background-color: #0958d9;
}
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
/* 骨架屏通用样式 */
.skeleton-balance-section,
.skeleton-account-section,
.skeleton-tip-section {
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
/* 余额区域骨架屏 */
.skeleton-balance-section {
background-color: #fff;
}
.skeleton-balance-label {
width: 30%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-amount {
width: 40%;
height: 60rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-current {
width: 25%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 账户选择区域骨架屏 */
.skeleton-account-section {
background-color: #fff;
}
.skeleton-section-title {
width: 25%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 30rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 4rpx solid #e2e2e2;
}
.skeleton-account-item:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 0;
}
.skeleton-account-icon {
width: 60rpx;
height: 60rpx;
background-color: #e6e6e6;
border-radius: 12rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-name {
flex: 1;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-radio {
width: 36rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 50%;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 提示信息骨架屏 */
.skeleton-tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.skeleton-tip-icon {
width: 40rpx;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 50%;
margin-right: 15rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-content {
flex: 1;
}
.skeleton-tip-line {
width: 70%;
height: 28rpx;
background-color: #e6e6e6;
margin-bottom: 15rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-line-small {
width: 50%;
height: 24rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 确认按钮骨架屏 */
.skeleton-confirm-section {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
padding: 30rpx;
}
.skeleton-confirm-button {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,661 @@
<template>
<!-- 骨架屏 -->
<view v-if="isLoading" class="skeleton-container">
<!-- 顶部提示栏骨架 -->
<view class="skeleton-top-tip"></view>
<!-- 头像上传区域骨架 -->
<view class="skeleton-avatar-section">
<view class="skeleton-avatar"></view>
<view class="skeleton-avatar-text"></view>
</view>
<!-- 表单内容骨架 -->
<view class="skeleton-form-content">
<!-- 身份信息区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-section-title"></view>
<!-- 姓名输入骨架 -->
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
<!-- 身份证号输入骨架 -->
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
<!-- 年龄输入骨架 -->
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-input"></view>
</view>
<!-- 性别选择骨架 -->
<view class="skeleton-form-item">
<view class="skeleton-label"></view>
<view class="skeleton-radio-group">
<view class="skeleton-radio-item"></view>
<view class="skeleton-radio-item"></view>
</view>
</view>
</view>
<!-- 身份证照片区域骨架 -->
<view class="skeleton-section">
<view class="skeleton-section-title"></view>
<!-- 身份证正面上传骨架 -->
<view class="skeleton-upload-item">
<view class="skeleton-upload-area"></view>
</view>
<!-- 身份证反面上传骨架 -->
<view class="skeleton-upload-item">
<view class="skeleton-upload-area"></view>
</view>
</view>
<!-- 占位空间骨架 -->
<view class="skeleton-placeholder"></view>
<!-- 提交按钮骨架 -->
<view class="skeleton-submit-section">
<view class="skeleton-submit-btn"></view>
</view>
</view>
</view>
<view v-else class="real-name-auth-container">
<!-- 顶部提示栏 -->
<view class="top-tip">
<text class="tip-text">请完成实名认证,保障您的接单权益</text>
</view>
<view @click="uploadFImg(1)" class=""
style=" display: flex; flex-direction: column; justify-content: center; align-items: center; ">
<img v-if="formData.headImg!=''" :src="Service.GetMateUrlByImg(formData.headImg)"
style="width: 140rpx; height: 140rpx; border-radius: 50%; " alt="" />
<view v-else class=""
style="background-color: #EBEBEB; width: 140rpx; height: 140rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; ">
<image :src="Service.GetIconImg('/static/index/my/edit/photo.png')"
style="width: 50rpx; height: 50rpx; " alt=""> </image>
</view>
<view class="" style="margin-top: 15rpx; font-size: 26rpx; color: #999999; ">
点击更换头像
</view>
</view>
<!-- 表单内容 -->
<view class="form-content">
<!-- 身份信息区域 -->
<view class="section">
<view class="section-title">身份信息</view>
<!-- 姓名输入 -->
<view class="form-item">
<view class="label">姓名</view>
<u-input v-model="formData.name" placeholder="请输入真实姓名" placeholder-color="#999" border="none"
class="input" input-align="right" />
</view>
<!-- 身份证号输入 -->
<view class="form-item">
<view class="label">身份证号</view>
<u-input v-model="formData.idCard" placeholder="请输入18位身份证号" placeholder-color="#999" border="none"
class="input" input-align="right" maxlength="18" />
</view>
<!-- 年龄 -->
<view class="form-item">
<view class="label">年龄</view>
<u-input v-model="formData.age" placeholder="请输入年龄" placeholder-color="#999" border="none"
class="input" input-align="right" maxlength="18" />
</view>
<view class="form-item" style="justify-content: space-between;">
<view class="label">性别</view>
<view class="" style="">
<up-radio-group v-model="formData.sex" placement="row">
<up-radio v-for="(item, index) in radiolist1" activeColor='#FF6A00' :key="index"
iconPlacement="right" :label="item.name" :name="item.name">
</up-radio>
</up-radio-group>
</view>
</view>
</view>
<!-- 身份证照片区域 -->
<view class="section">
<view class="section-title">身份证照片</view>
<!-- 上传身份证正面 -->
<view @click="uploadFImg(2)" class="upload-item">
<view class="upload-area bordered-area">
<view v-if="!formData.frontImage" class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证正面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-else class="uploaded-placeholder">
<image :src="Service.GetMateUrlByImg(formData.frontImage)"
style="width: 100%; height: 100%; " mode=""></image>
</view>
</view>
</view>
<!-- 上传身份证反面 -->
<view @click="uploadFImg(3)" class="upload-item">
<view class="upload-area bordered-area">
<view v-if="!formData.backImage" class="upload-content">
<view class="upload-icon">+</view>
<view class="upload-text">上传身份证反面</view>
</view>
<!-- 显示上传后的占位图 -->
<view v-else class="uploaded-placeholder">
<image :src="Service.GetMateUrlByImg(formData.backImage)"
style="width: 100%; height: 100%; " mode=""></image>
</view>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 180rpx;">
</view>
<!-- 提交按钮 -->
<view class="submit-section">
<button @click="save()" type="primary" class="submit-btn">
提交信息
</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, computed } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService';
let isLoading = ref(true)
// 表单数据
const formData = ref({
name: '',
idCard: '',
sex: '男',
frontImage: '',
backImage: '',
agreed: false,
headImg: '',
age: '',
phone: ''
});
const radiolist1 = ref([
{
name: '男',
disabled: false,
},
{
name: '女',
disabled: false,
}
]);
let addressInfo = ref({
province: '',
city: '',
region: '',
lat: 0,
lon: 0
})
onLoad(() => {
getLocation()
})
const getLocation = () => {
console.log('开始定位');
uni.getLocation({
type: 'wgs84',
success: function (res) {
addressInfo.value.lat = res.latitude
addressInfo.value.lon = res.longitude
CNRiderDataService.GetAddressInfo(res.latitude, res.longitude).then(data => {
console.log(1111);
isLoading.value = false
addressInfo.value.province = data.data.addrInfo.province
addressInfo.value.city = data.data.addrInfo.city
addressInfo.value.region = data.data.addrInfo.district
})
}
});
}
const uploadFImg = (index : number) => {
uni.chooseImage({
count: 1, // 最多选择3张图片
sizeType: ['original', 'compressed'], // 支持原图和压缩图
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
success: function (res) {
let path = res.tempFiles[0].path
Service.uploadH5(path, 'Avatar', data => {
if (index === 1) {
formData.value.headImg = data
} else if (index === 2) {
formData.value.frontImage = data
} else {
formData.value.backImage = data
}
})
},
fail: function (err) {
console.error('选择失败:', err.errMsg);
}
})
}
const save = () => {
if (rules() === 1) {
return
}
CNRiderDataService.CompleteRider(formData.value.name, formData.value.headImg, formData.value.idCard, formData.value.sex, Number(formData.value.age), formData.value.frontImage, formData.value.backImage, addressInfo.value.province, addressInfo.value.city, addressInfo.value.region, addressInfo.value.lat, addressInfo.value.lon).then(res => {
if (res.data) {
Service.Msg('提交成功')
setTimeout(()=>{
Service.GoPageTab('/pages/index/index')
},1000)
} else {
Service.Msg(res.msg)
}
})
}
const rules = () => {
if (!formData.value.headImg) {
Service.Msg('请上传头像!')
return 1
}
if (!formData.value.name) {
Service.Msg('请输入真实姓名!')
return 1
}
if (formData.value.idCard.split('').length !== 18) {
Service.Msg('请输入正确身份证号!')
return 1
}
if (!formData.value.idCard) {
Service.Msg('请输入身份证号!')
return 1
}
if (!formData.value.age) {
Service.Msg('请输入年龄!')
return 1
}
if (!formData.value.frontImage) {
Service.Msg('请输入身份证正面!')
return 1
}
if (!formData.value.backImage) {
Service.Msg('请输入身份证背面!')
return 1
}
}
</script>
<style scoped lang="scss">
page {
background-color: #f5f5f5;
}
/* 骨架屏样式 */
.skeleton-container {
padding: 0 20rpx;
}
.skeleton-top-tip {
width: 100%;
height: 80rpx;
background-color: #e6f7ff;
border-radius: 20rpx;
margin: 20rpx 0;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-avatar-section {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30rpx;
}
.skeleton-avatar {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
background-color: #e6e6e6;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-avatar-text {
width: 200rpx;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-form-content {
padding: 0 10rpx;
}
.skeleton-section {
margin-bottom: 40rpx;
}
.skeleton-section-title {
width: 150rpx;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
margin-bottom: 20rpx;
padding-left: 10rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-form-item {
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #f0f0f0;
}
.skeleton-form-item:last-child {
border-bottom: none;
}
.skeleton-label {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-input {
flex: 1;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-radio-group {
display: flex;
gap: 40rpx;
}
.skeleton-radio-item {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-upload-item {
margin-bottom: 20rpx;
}
.skeleton-upload-area {
width: 100%;
height: 250rpx;
background-color: #fff;
border-radius: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-placeholder {
width: 100%;
height: 180rpx;
}
.skeleton-submit-section {
z-index: 1000;
width: 100%;
margin-top: 60rpx;
padding: 20rpx;
background-color: #fff;
position: fixed;
bottom: 0;
left: 0;
}
.skeleton-submit-btn {
width: 100%;
height: 92rpx;
background-color: #e6e6e6;
border-radius: 46rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-color: #e6e6e6;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e6e6e6;
}
}
.top-tip {
background-color: #E6F7FF;
padding: 20rpx 30rpx;
margin: 20rpx;
border-radius: 20rpx;
}
.tip-text {
color: #1890ff;
font-size: 28rpx;
}
.form-content {
padding: 0 30rpx;
}
.section {
margin-bottom: 40rpx;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
padding-left: 10rpx;
}
.form-item {
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid #f0f0f0;
}
.form-item:last-child {
border-bottom: none;
}
.label {
width: 120rpx;
font-size: 28rpx;
color: #333;
}
.input {
flex: 1;
font-size: 28rpx;
}
.upload-item {
margin-bottom: 20rpx;
}
.upload-area {
height: 250rpx;
overflow: hidden;
border-radius: 6rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
}
.bordered-area {
background-color: #fff;
border-radius: 20rpx;
width: 100%;
}
.upload-content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
z-index: 1;
}
.upload-icon {
font-size: 60rpx;
color: #1890ff;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #1890ff;
}
.uploaded-placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
color: #333;
font-size: 28rpx;
}
.face-verify-area {
background-color: #fff;
height: 240rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
border-radius: 8rpx;
}
.face-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #e6f7ff;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
}
.face-text {
font-size: 28rpx;
color: #1890ff;
font-weight: 500;
}
.agreement-section {
display: flex;
align-items: center;
margin-bottom: 40rpx;
padding: 0 10rpx;
}
.checkbox {
margin-right: 10rpx;
}
.agreement-text {
font-size: 26rpx;
color: #666;
}
.agreement-link {
font-size: 26rpx;
color: #1890ff;
margin-left: 4rpx;
}
.input {
flex: 1;
font-size: 28rpx;
color: #333;
}
.submit-section {
z-index: 1000;
width: 100%;
margin-top: 60rpx;
padding: 20rpx;
background-color: #fff;
position: fixed;
bottom: 0;
left: 0;
}
.submit-btn {
height: 92rpx;
font-size: 32rpx;
border-radius: 46rpx;
background-color: #1890ff;
}
.submit-btn[disabled] {
background-color: #a0cfff;
color: #ffffff;
}
</style>

View File

@@ -0,0 +1,770 @@
<template>
<view v-if="isLoading" class="skeleton-container">
<!-- 统计数据区域骨架 -->
<view class="skeleton-stats-section">
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
<view class="skeleton-stat-item">
<view class="skeleton-stat-label"></view>
<view class="skeleton-stat-value"></view>
</view>
</view>
<!-- 接单按钮骨架 -->
<view class="skeleton-action-section">
<view class="skeleton-accept-btn"></view>
</view>
<!-- 标签栏骨架 -->
<view class="skeleton-tab-bar">
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
<view class="skeleton-tab-item">
<view class="skeleton-tab-text"></view>
</view>
</view>
<!-- 任务列表骨架 -->
<view class="skeleton-task-list">
<!-- 任务卡片骨架 -->
<view class="skeleton-task-section" v-for="i in 3" :key="i">
<!-- 高价单标签骨架 -->
<view class="skeleton-tag-row">
<view class="skeleton-high-price-tag"></view>
<view class="skeleton-phone-btn"></view>
</view>
<!-- 商家信息骨架 -->
<view class="skeleton-merchant-info">
<view class="skeleton-merchant-name"></view>
<view class="skeleton-distance"></view>
</view>
<!-- 地址信息骨架 -->
<view class="skeleton-address-info">
<view class="skeleton-address-text"></view>
</view>
<!-- 价格和取餐时间骨架 -->
<view class="skeleton-price-time-row">
<view class="skeleton-pickup-code"></view>
<view class="skeleton-pickup-time"></view>
</view>
<!-- 立即抢单按钮骨架 -->
<view class="skeleton-grab-btn"></view>
</view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="rider-home">
<!-- 统计数据区域 -->
<view class="stats-section">
<view class="stat-item">
<text class="stat-label">今日收入</text>
<text class="stat-value income">¥{{userData.dayAmount.toFixed(2)}}</text>
</view>
<view class="stat-item">
<text class="stat-label">已完成</text>
<text class="stat-value completed">{{userData.dayOrderCount}}单</text>
</view>
<view class="stat-item">
<text class="stat-label">配送中</text>
<text class="stat-value ongoing">{{ userData.takeOrderCount }}单</text>
</view>
</view>
<!-- 接单按钮 -->
<view class="action-section">
<up-button :disabled='riderInfo.status===0' type="primary" shape="circle" size="default"
class="accept-orders-btn"
@click="toggleAcceptOrders">{{ riderInfo.status==0?'审核中':(riderInfo.isOnline == 0 ? '已下线' : '已上线') }}</up-button>
</view>
<view class="tab-bar">
<view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: activeTab === index }"
@click="switchTab(index)">
<text class="tab-text">{{ tab }}</text>
<view v-if="activeTab === index" class="active-line"></view>
</view>
</view>
<view v-if="riderInfo.status===1" class="" style="padding: 0 30rpx;">
<view @click="gopage(orderItem.orderId)" v-for="(orderItem,orderIndex) in orderList " :key="orderIndex"
class="task-section">
<!-- 高价单标签 -->
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="high-price-tag"
:style="{'border':activeTab==0?'1rpx solid #FF7875':(activeTab==1?'1rpx solid #52C41A':'1rpx solid #FAAD14'),'color':activeTab==0?'#FF7875':(activeTab==1?'#52C41A':'#FAAD14') }"
style="background-color: #fff;">
<text class="high-price-text">{{activeTab==0? '新订单':(activeTab==1? '待取单':'配送中')}}</text>
</view>
<view class="" @click.stop="call(orderItem.phone)" style="display: flex; align-items: baseline;">
<up-icon name="phone" color="var(--nav-mian)" size="20"></up-icon>
<text style="margin-left: 10rpx; color: var(--nav-mian); ">拨打商家</text>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-info">
<text class="merchant-name">{{ orderItem.storeName }}</text>
<text v-if="activeTab==1" class="distance">{{ distance(orderItem.distance) }}</text>
</view>
<!-- 地址信息 -->
<view class="address-info">
<up-icon name="map" color="#999" size="24rpx" />
<text class="address-text"> {{orderItem.address }}</text>
<!-- <text v-if="activeTab!==0" class="address-text">共3件商品</text> -->
</view>
<!-- 商品次数-->
<!-- <view class="address-info">
<text class="address-text">共3件商品</text>
<view class="">
<text class="price">¥5.50</text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">/单</text>
</view>
</view> -->
<!-- 价格和取餐时间 -->
<view class="price-time-row">
<view v-if="activeTab==1" class="">
<text style="font-size: 30rpx; font-weight: 600; color: #1890FF; ">取件码: </text>
<text style="color: var(--nav-mian); font-weight: 600; margin-left: 10rpx; ">A121</text>
</view>
<view v-if="activeTab!==1" class="">
<text class="address-text">据您{{ distance(orderItem.distance) }}</text>
</view>
<view class="pickup-time">
<up-icon name="clock" :color="activeTab==0?'#FAAD14':'#FF0000'" size="16" />
<text class="time-text"
:style="{'color':activeTab==0?'#FAAD14':'#FF0000'}">{{ orderItem.makeTime.split(' ').length==2?'预计'+orderItem.makeTime.split(' ')[1]+'送达':orderItem.makeTime }}</text>
</view>
</view>
<!-- 立即抢单按钮 -->
<up-button type="primary"
@click="activeTab==0?takeOrder(orderItem.orderId):pickFood(orderItem.orderId)"
:color="activeTab==0?'#1890FF':'#52C41A'" size="large"
class="grab-btn">{{ activeTab==0?'立即接单':(activeTab==1?'我已取餐':'确认送达')}}</up-button>
</view>
<up-loadmore :status="status" />
</view>
<view v-else class="" style="font-weight: bold; text-align: center; font-size: 32rpx; margin-top: 100rpx; ">
信息审核中·暂时无法接单
</view>
<view class="" style="width: 100%; height: 60rpx; ">
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const isLoading = ref(true);
let status = ref('nomore')
let page = ref(1)
let userData = ref({
dayAmount: 0,
dayOrderCount: 0,
takeOrderCount: 0
})
const tabs = ['新订单', '待取货', '配送中']
const activeTab = ref(0)
let riderInfo = ref<any>({})
let orderList = ref<Array<any>>([])
onLoad(() => {
getData()
})
onShow(() => {
getOrderData()
})
const getData = () => {
CNRiderDataService.GetRiderHomeInfo().then(res => {
isLoading.value = false
riderInfo.value = res.data.riderInfo
userData.value=res.data
if (res.data.riderInfo.status === -1) {
Service.GoPage('/pages/my/completeData')
}
})
}
const getOrderData = () => {
status.value = 'loadmore'
page.value = 1
orderList.value = []
getOrderList()
}
//获取订单
const getOrderList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
if (activeTab.value == 0) {
CNRiderOrderService.GetRiderOrderList(page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
} else {
CNRiderOrderService.GetRiderTakeOrderList(activeTab.value == 1 ? 0 : 1, page.value).then(res => {
orderList.value = [...orderList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
}
// 接单
const takeOrder = (orderId : string) => {
CNRiderOrderService.RiderTakeOrder(orderId).then(res => {
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 取餐
const pickFood = (orderId : string) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId,activeTab.value).then(res=>{
if (res.data) {
getOrderData()
getData()
} else {
Service.Msg(res.msg)
}
})
}
// 切换标签
const switchTab = (index : number) => {
activeTab.value = index
getOrderData()
}
// 切换接单状态
const toggleAcceptOrders = () => {
CNRiderDataService.UpdateRiderOnline().then(res => {
if (res.data) {
getData()
} else {
Service.Msg(res.msg)
}
})
};
// 距离计算
const distance = (item : any) => {
if (item < 0) {
return Number(item * 100).toFixed(2) + 'm'
} else {
return Number(item).toFixed(2) + 'km'
}
}
// 拨打电话
const call = (e : string) => {
uni.makePhoneCall({
phoneNumber: e
})
}
// 页面跳转
const gopage = (id) => {
if (activeTab.value == 0) {
Service.GoPage('/pages/order/grabOrder?orderId=' + id)
} else {
Service.GoPage('/pages/order/orderDetail?orderId=' + id)
}
}
</script>
<style scoped lang="scss">
page {
background-color: #F6f6f6;
}
/* 统计数据区域 */
.stats-section {
background-color: #ffffff;
margin: 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-label {
font-size: 28rpx;
color: #666666;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 36rpx;
font-weight: 600;
}
.income {
color: var(--nav-diluted);
}
.completed {
color: var(--nav-vice);
}
.ongoing {
color: #FF9500;
}
/* 接单按钮 */
.action-section {
margin: 0 20rpx;
}
.accept-orders-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
background-color: #52C41A;
}
/* 顶部标签栏 */
.tab-bar {
margin-top: 20rpx;
display: flex;
background-color: #FFFFFF;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #E5E5E5;
width: 100vw;
background-color: #fff;
}
.tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.tab-text {
font-size: 28rpx;
color: #666666;
}
.tab-item.active .tab-text {
color: var(--nav-mian);
font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
width: 110rpx;
height: 6rpx;
background-color: var(--nav-mian);
border-radius: 3rpx;
}
.task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签样式 */
.high-price-tag {
width: fit-content;
background-color: #FF7875;
color: #FFFFFF;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
display: flex;
align-items: center;
}
.status-tag {
position: absolute;
top: 20rpx;
left: 20rpx;
padding: 5rpx 20rpx;
border-radius: 20rpx;
font-size: 24rpx;
z-index: 10;
}
.status-tag.pending {
background-color: #4CD964;
color: #FFFFFF;
}
.status-tag.delivering {
background-color: #FF9500;
color: #FFFFFF;
}
/* 右侧操作按钮 */
.right-action {
position: absolute;
top: 20rpx;
right: 20rpx;
}
.call-btn {
padding: 5rpx 15rpx;
font-size: 24rpx;
line-height: 36rpx;
}
/* 信息展示 */
.merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.merchant-name {
font-size: 34rpx;
font-weight: 600;
color: #333333;
}
.distance {
font-size: 28rpx;
color: #666666;
}
.address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.address-text {
margin-left: 10rpx;
font-size: 28rpx;
color: #666666;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 价格和时间 */
.price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.price {
font-size: 36rpx;
font-weight: 600;
color: #FF3B30;
}
.pickup-code {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.pickup-time {
display: flex;
align-items: baseline;
}
.time-text {
margin-left: 8rpx;
font-size: 26rpx;
}
/* 按钮样式 */
.grab-btn {
margin-top: 25rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
}
.grab-btn {
background-color: #007AFF;
}
/* uview组件样式覆盖 */
::v-deep .u-button--primary {
background-color: var(--nav-vice);
border-color: var(--nav-vice);
}
::v-deep .u-button--mini {
background-color: var(--nav-mian);
border-color: var(--nav-mian);
}
/* 骨架屏样式 */
.skeleton-container {
padding: 20rpx;
}
/* 统计数据区域骨架 */
.skeleton-stats-section {
background-color: #ffffff;
margin: 0 20rpx;
border-radius: 16rpx;
display: flex;
justify-content: space-around;
padding: 30rpx 0;
}
.skeleton-stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.skeleton-stat-label {
width: 120rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-stat-value {
width: 150rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 接单按钮骨架 */
.skeleton-action-section {
margin: 20rpx;
}
.skeleton-accept-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 标签栏骨架 */
.skeleton-tab-bar {
display: flex;
background-color: #ffffff;
padding: 20rpx 0 0;
border-bottom: 1rpx solid #e5e5e5;
}
.skeleton-tab-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx 0;
position: relative;
}
.skeleton-tab-text {
width: 100rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 任务列表骨架 */
.skeleton-task-list {
padding: 0 20rpx;
}
.skeleton-task-section {
margin-top: 20rpx;
display: flex;
flex-direction: column;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
}
/* 标签骨架 */
.skeleton-tag-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.skeleton-high-price-tag {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-phone-btn {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 商家信息骨架 */
.skeleton-merchant-info {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-merchant-name {
width: 200rpx;
height: 34rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-distance {
width: 80rpx;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 地址信息骨架 */
.skeleton-address-info {
display: flex;
align-items: baseline;
margin-top: 15rpx;
}
.skeleton-address-text {
width: 100%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 价格和时间骨架 */
.skeleton-price-time-row {
margin-top: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.skeleton-pickup-code {
width: 150rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
.skeleton-pickup-time {
width: 200rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 按钮骨架 */
.skeleton-grab-btn {
margin-top: 25rpx;
height: 80rpx;
background-color: #e6e6e6;
border-radius: 40rpx;
animation: skeleton-loading 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-color: #e6e6e6;
}
50% {
background-color: #f0f0f0;
}
100% {
background-color: #e6e6e6;
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

View File

@@ -0,0 +1,46 @@
import { Service } from '@/Service/Service';
/*****连接*****/
class ImConnectService {
private static GetConnectPath: string = '/Im/GetConnect';
/*****获取连接*****/
static GetConnect() {
var result = Service.Request(this.GetConnectPath, 'POST', {type:3});
return result;
}
private static IsOnlinePath: string = '/Im/IsOnline';
/*****判断是否在线*****/
static IsOnline(id:number) {
var result = Service.Request(this.IsOnlinePath, 'POST', id);
return result;
}
private static JoinChatPath: string = '/Im/JoinChat';
/*****加入聊天室*****/
static JoinChat(id: number,chan:number) {
var result = Service.Request(this.JoinChatPath, 'POST', { id,chan });
return result;
}
private static SendChanMsgPath: string = '/Im/SendChanMsg';
/*****发送聊天室消息*****/
static SendChanMsg(id: number, user: string, chan: string, type: string,msg:string,media:string) {
var result = Service.Request(this.SendChanMsgPath, 'POST', { id, user, chan, type , msg , media });
return result;
}
private static ExitChatPath: string = '/Im/ExitChat';
/*****离开聊天室*****/
static ExitChat(id:number, chan: string) {
var result = Service.Request(this.ExitChatPath, 'POST', {id, chan });
return result;
}
private static GetOrderMessagePath: string = '/Order/GetOrderMessage';
/*****聊天记录*****/
static GetOrderMessage(orderId:string) {
var result = Service.Request(this.GetOrderMessagePath, 'GET', {orderId });
return result;
}
}
export { Service, ImConnectService };

View File

@@ -0,0 +1,186 @@
<template>
<view style=" padding: 20rpx 40rpx; background-color: #fff; ">
<up-form v-if="type!=='1'" labelPosition="left" labelWidth='90' :model="userData" ref="form1">
<up-form-item label="姓名:" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input inputAlign='right' v-model="userData.name" clearable='true' placeholder="请输入联系人姓名"
border="none"></up-input>
</up-form-item>
<up-form-item label="手机号:" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input inputAlign='right' v-model="userData.phone" clearable='true' placeholder="请输入手机号"
border="none"></up-input>
</up-form-item>
</up-form>
<up-form v-else labelPosition="left" labelWidth='90' :model="password" ref="form1">
<up-form-item label="手机号:" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input clearable='true' v-model="password.phone" placeholder="请输入手机号" border="none"></up-input>
</up-form-item>
<up-form-item label="验证码:" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input clearable='true' v-model="password.code" placeholder="请输入验证码" border="none">
<template #suffix>
<up-code :seconds="seconds" @end="end" @start="start" ref="uCodeRef"
@change="codeChange"></up-code>
<up-button @tap="getCode">{{tips}}</up-button>
</template>
</up-input>
</up-form-item>
</up-form>
</view>
<view class="" style=" position: fixed; bottom: 0; left: 0; width: 100%; padding: 20rpx; ">
<button @click="save()" class="logout-btn">确认修改</button>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { ref } from "vue";
import { Service } from "../../Service/Service";
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
import { CNRiderLoginService } from '@/Service/CN/CNRiderLoginService'
const tips = ref('');
const seconds = ref(60);
const uCodeRef = ref(null);
let password = ref({
phone: '',
code: ''
})
let userData = ref({
name: '',
phone: ''
})
let type = ref('')
onLoad((data : any) => {
if (data.type === '1') {
uni.setNavigationBarTitle({
title: '修改手机号'
})
}
type.value = data.type
console.log(type.value);
getData()
});
onShow(() => {
});
const getData = () => {
CNRiderDataService.GetRiderExigency().then(res => {
if (res.code == 0) {
if (res.data.info) {
userData.value = res.data.info
}
}
})
}
const save = () => {
if (type.value === '0') {
if (!userData.value.name) {
Service.Msg('请输入联系人姓名!')
return
}
if (!userData.value.phone) {
Service.Msg('请输入联系人手机号!')
return
}
if (userData.value.phone.split('').length !== 11) {
Service.Msg('请输入正确手机号!')
return
}
CNRiderDataService.AddRiderExigency(userData.value.name, userData.value.phone).then(res => {
if (res.data) {
Service.Msg('添加成功!')
setTimeout(() => {
Service.GoPageBack()
}, 1000)
} else {
Service.Msg(res.msg)
}
})
} else {
if (!password.value.phone) {
Service.Msg('请输入手机号!')
return
}
if (password.value.phone.split('').length !== 11) {
Service.Msg('请输入正确手机号!')
return
}
if (!password.value.code) {
Service.Msg('请输入验证码!')
return
}
CNRiderLoginService.UpdateRiderPhone(password.value.phone, password.value.code).then(res => {
if (res.data) {
Service.Msg('修改成功!')
setTimeout(() => {
Service.GoPageBack()
}, 1000)
} else {
Service.Msg(res.msg)
}
})
}
}
const codeChange = (text) => {
tips.value = text;
};
const getCode = () => {
if (uCodeRef.value.canGetCode) {
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码',
});
CNRiderLoginService.SendUserSms(password.value.phone, 'RiderUpPhone').then(res => {
if (res.code == 0) {
uni.hideLoading();
// 这里此提示会被start()方法中的提示覆盖
Service.Msg('验证码已发送')
uCodeRef.value.start();
}
})
} else {
Service.Msg('倒计时结束后再发送')
}
};
const end = () => {
};
const start = () => {
};
</script>
<style lang="scss">
page {
background-color: #fff;
}
.logout-btn {
background-color: var(--nav-mian);
color: #fff;
font-weight: 500;
border-radius: 60rpx;
height: 90rpx;
line-height: 90rpx;
font-size: 30rpx;
margin: 0;
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<view class="">
<web-view ref="webviewRef" v-if="isshow" :src="url" @message="handleMessage" @logData = "logData"></web-view>
<!-- ✅ 新增:一个绝对定位的遮罩层,用于在刷新时覆盖 web-view -->
<view v-else class="reloading-mask">
<up-loading-icon text="正在获取订单状态..." v-if="orderOver" textSize="16"></up-loading-icon>
<up-loading-icon text="订单已完成" v-else textSize="16"></up-loading-icon>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { Service } from "@/Service/Service";
import { ref, onMounted } from 'vue';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let orderId = ref<string>('')
let url = ref<string>('')
let isshow = ref<false>(false)
let orderInfo = ref<any>({
isFood: 0
})
let riderOrder = ref<any>({
status:0
})
let orderOver = ref<true>(false)
onLoad((data) => {
if (data.orderId) {
orderId.value = data.orderId
getData()
} else {
Service.Msg('为获取到订单ID')
}
});
// 初始化url
const getUrl = () => {
isshow.value = false
url.value = 'https://hmjz.327gzs.top?orderId=' + orderId.value + '&isFood=' + riderOrder.value.status
isshow.value = true
}
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
if (res.code==0) {
orderInfo.value = res.data.orderInfo
riderOrder.value = res.data.riderOrder
getUrl()
}else{
Service.Msg(res.mgs)
}
})
}
// 点击完成送餐取餐调用
const handleMessage = (data) => {
let preat = data.detail.data[0]
if(preat.action =='message'){
if (riderOrder.value.status == 0) {
// 去商家、取餐
pickFood(1)
} else {
// 去用户,送餐
pickFood(2)
}
}else if(preat.action =='logData'){
CNRiderOrderService.UpdateRiderLocation(preat.data[0],preat.data[1]).then(res=>{
if(res.code==0){
}
})
}
return
};
// 骑手定位
const logData = (data) =>{
console.log(data,'骑手定位')
}
// 取餐
const pickFood = ( type:number) => {
CNRiderOrderService.UpdateRiderOrderTake(orderId.value, type).then(res => {
if (res.data) {
Service.Msg(type==1?'取餐成功':'订单完成')
setTimeout(()=>{Service.GoPageTab('/pages/index/index')},500)
getData()
} else {
Service.Msg(res.msg)
}
})
};
</script>
<style lang="scss" scoped>
.reloading-mask {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #f7f7f7; // 使用页面底色
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,331 @@
import { HttpRequest, StoreAssist, UploadAssist, ResultData } from '@/common/Common';
import { BaseConfig } from './BaseConfig';
export class Service extends BaseConfig {
//获取API地址
static ApiUrl(path : string) {
return `${this.servesUrl}${path}`;
}
//获取图片地址
static GetpayImg(path : string) {
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.payuploadUrl}${path}`;
}
}
//获取图标地址
static GetIconImg(path : string) {
return path
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.imgUrl}${path}`;
}
}
//获取图片地址
static GetMateUrlByImg(path : string) {
return path
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.imgUrl}${path}`;
}
}
//获取音视频地址
static GetMateUrlByMedia(path : string) {
if (path.startsWith('http') || path.startsWith('https')) {
return path;
} else {
return `${this.mediaUrl}${path}`;
}
}
//获取登录账号token
static GetUserToken() {
return Service.GetStorageCache('token');
}
// 获取登录状态
static GetUserIsLogin() {
var token = this.GetUserToken();
if (token == null || token == '') {
return false;
} else {
return true;
}
}
//设置登录账户Token
static SetUserToken(token : string) {
this.SetStorageCache('token', token);
}
//清理登录账户Token
static OffUserToken() {
Service.DelStorageCache('token');
uni.$emit('ImComOff', 'user');
this.ClearUserStateData();
}
//获取登录账号状态信息
static GetUserStateData() {
return Service.GetStorageCache('StateDomain');
}
//设置当前登录账号状态信息
static SetUserStateData() {
return Service.GetStorageCache('StateDomain');
}
//清理当前登录账号状态信息
static ClearUserStateData() {
Service.DelStorageCache('StateDomain');
}
//获取缓存
static GetStorageCache(key : string) {
return StoreAssist.Get(key);
}
//删除缓存
static DelStorageCache(key : string) {
StoreAssist.Delete(key);
}
//设置缓存
static SetStorageCache(key : string, data : any) {
StoreAssist.Set(key, data);
}
/*****以下是基础方法调用与拦截器*****/
static Request(url : string, method : 'GET' | 'POST' | 'PUT' | undefined, data : object | any) {
const token = Service.GetUserToken();
const _url = Service.ApiUrl(url);
var result = HttpRequest.RequestWithToken(_url, method, token, data).then((retResult : any) => {
if (retResult.statusCode == '200') {
var obj = retResult.data;
if (obj.code == 401) {
//过期
this.OffUserToken();
this.Msg('登录过期,请重新登录')
this.GoPage('/pages/login/login')
return Promise.reject();
} else if (obj.code == 40101) {
//失效
this.OffUserToken();
this.GoPageDelse('/pages/mine/login/login');
return Promise.reject();
} else if (obj.code == 1004) {
//资源不存在
this.GoPageDelse('/pages/AppSet/404/404');
return Promise.reject();
// return new ResultData(-1, '', '');
} else if (obj.code == 40188) {
//无权限
this.GoPageDelse('/pages/AppSet/40188/40188');
return Promise.reject();
// return new ResultData(-1, '', '');
} else if (obj.code == 1008) {
//业务提示
return new ResultData(obj.code, obj.msg, obj.data);
} else {
return new ResultData(obj.code, obj.msg, obj.data);
}
} else {
return new ResultData(-1, '', '');
}
});
return result;
}
/*****以下是腾讯云oss上传*****/
static UpLoadMedia(code : string, fileName : string, desire : string, path : string) {
var result = this.Request(this.uploadUrl, 'GET', { code, fileName, desire }).then((retResult) => {
if (retResult.code == 0) {
var upOk = UploadAssist.Upload(retResult.data.url, path, retResult.data.cosData).then((upRet : any) => {
if (upRet.statusCode == 200) {
const retData : any = { code: retResult.data.code, file: retResult.data.file, cache: retResult.data.cache };
return new ResultData(0, '上传成功!', retData);
} else {
this.Msg('上传失败!');
return new ResultData(-1, '', '');
}
});
return upOk;
} else {
this.Msg('上传失败!');
return new ResultData(-1, retResult.msg,retResult.data);
}
});
return result;
}
/***********消息操作**************/
static Msg(message : any, icon ?: any) : void {
if (icon != null) {
uni.showToast({
title: message,
icon: icon
});
} else {
uni.showToast({
title: message,
icon: 'none'
});
}
}
static Alert(msg : string, cb ?: any) {
uni.showModal({
title: '提示',
content: msg,
showCancel: false,
cancelText: '取消',
confirmText: '确定',
success: res => {
if (res.confirm) {
cb && cb();
}
}
})
}
static LoadIng(text : any) : void {
uni.showLoading({
title: text,
icon: 'none'
});
}
static LoadClose() : void {
uni.hideLoading();
}
/**********跳转操作*********/
static GoPageTab(path : string) : void {
uni.switchTab({
url: path
});
}
/**********跳转操作*********/
static GoPage(path : string) : void {
uni.navigateTo({
url: path, //跳转的页面
success: function (res) {
// 通过eventChannel向被打开页面传送数据
}
});
}
/**********跳转并删除当前页面操作*********/
static GoPageDelse(path : string) : void {
uni.redirectTo({
url: path //跳转的页面
});
}
/**********返回上一页*********/
static GoPageBack() : void {
uni.navigateBack({ delta: 1 });
}
/*****获取图片base64*****/
static UpLoadMediaBase64(path : string) {
return new Promise(function (resolve, reject) {
uni.uploadFile({
url: 'http://cloud.pccsh.com/DefUp/UploadFileImgBase64', //仅为示例,非真实的接口地址
filePath: path,
name: 'file',
success: (uploadFileRes) => {
resolve(uploadFileRes);
},
fail: (err) => {
reject(err);
}
});
});
}
/*****获取图片位置信息*****/
//获取时间戳
static GetTimeSpan(milliSecond : number) {
return Date.now() + milliSecond;
}
// 时间戳处理
static formatDate(time : any, type : number) : string {
const date = new Date(time);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始所以加1并用0填充
const day = String(date.getDate()).padStart(2, '0'); // 用0填充
const hours = String(date.getHours()).padStart(2, '0'); // 用0填充
const minutes = String(date.getMinutes()).padStart(2, '0'); // 用0填充
const seconds = String(date.getSeconds()).padStart(2, '0'); // 用0填充
if (type == 0) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
else if (type == 1) {
return `${year}-${month}-${day} ${hours}:${minutes}`;
} else if (type == 2) {
return `${year}-${month}-${day}`;
} else if (type == 3) {
return `${hours}:${minutes}`;
} else if (type == 4) {
return `${year}${month}${day}`;
}
else {
return `${hours}:${minutes}`;
}
}
/*****节流*****/
static throttle(fn: () => void, time: number) {
let canRun: boolean = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn(); //可以不执行
canRun = true;
}, time);
};
}
/*****防抖*****/
static debounce<T extends (...args: any[]) => void>(fn: T, time: number): (...args: Parameters<T>) => void {
let timerId: NodeJS.Timeout | null = null;
return (...args: Parameters<T>) => {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args); // 执行传入的函数
timerId = null; // 清除定时器ID
}, time);
};
}
// 普通图片上传
static uploadH5(path, dic, callback) {
console.log(this.payuploadUrl,'xxx')
uni.uploadFile({
url: this.payuploadUrl+'/Upload/UploadFile',
method: "POST",
header: {
'Authorization': 'Bearer ' + Service.GetUserToken(),
},
formData: {
"path": dic,
},
filePath: path,
name: 'file',
success: (data) => {
let info = data.data
callback(info)
}
})
}
}

View File

@@ -0,0 +1,372 @@
<template>
<!-- 收入概览区域 -->
<view class="income-container">
<view class="income-overview">
<view class="" style="display: flex; align-items: center; justify-content: space-between; ">
<view class="">
<text class="income-title">账户余额</text>
<view class="" style="display: flex; align-items: center;">
<text class="income-amount">¥{{riderAcc.account}}</text>
</view>
</view>
<u-button class="withdraw-button" @click="Service.GoPage('/pages/order/withdraw')"
type="primary">立即提现</u-button>
</view>
</view>
<!-- 收入明细区域 -->
<view class="detail-header">
<text class="detail-title">收支明细</text>
</view>
<!-- 时间选择标签 -->
<view class="time-tabs">
<text v-for="(item ,index) in timeList" :key="index" @click="changeTab(index)"
:class="{ 'active':currentTime==index }" class="tab-item">{{item}}</text>
</view>
<view class="detail-list" v-for="(item, index) in accList" :key="index">
<view class="detail-content">
<view class="icon-placeholder">
<image :src="Service.GetIconImg('/static/index/income/order.png')"
style="width: 55rpx; height: 55rpx;" mode=""></image>
</view>
<view class="detail-info">
<text class="order-id">{{item.name}}</text>
<text class="order-time">{{ Service.formatDate(item.addTime,1) }}</text>
</view>
<view class="" style="" >
<view class="order-amount" style="text-align: right;" >{{ item.code=='收入'?'+':'-' }}{{item.amount}}</view>
<view class="order-time" >
账户余额 {{ item.balance }}
</view>
</view>
</view>
</view>
<up-loadmore :status="status" />
<view class="" style="width: 100vw; height: 100rpx; ">
</view>
</view>
<calender ref="calendar" :range='true' :insert="false" @confirm='dataConfirm' />
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts';
import { Service } from '@/Service/Service';
import { CNRiderDataService } from "@/Service/CN/CNRiderDataService"
import { CNRiderOrderService } from "@/Service/CN/CNRiderOrderService"
import calender from "@/uni_modules/uni-calendar/components/uni-calendar/uni-calendar"
let loading = ref(true)
let calendar = ref(null)
let timeList = ref([
'今日',
'本周',
'本月',
'自定义'
])
let currentTime = ref(0)
let status = ref('nomore')
let page = ref(1)
let riderAcc = ref<any>({})
let accList = ref<Array<any>>([])
let timeString=ref('')
onLoad(() => {
getData()
getIncome()
})
onMounted(() => {
})
const getData = () => {
CNRiderDataService.GetRiderAccInfo().then(res => {
loading.value = false
if (res.data) {
riderAcc.value = res.data.riderAcc
}
})
}
// 收入列表
const getIncome = () => {
status.value = 'loadmore'
page.value = 1
accList.value=[]
getIncomeList()
}
const getIncomeList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderAccLog(currentTime.value==0?'0':(currentTime.value==3?timeString.value:String(currentTime.value)), page.value).then(res => {
accList.value = [...accList.value, ...res.data.accLog]
status.value = res.data.accLog == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
const changeTab = (index : number) => {
currentTime.value = index
if (index == 3) {
calendar.value.open()
return
}
getIncome()
}
const dataConfirm = (e) => {
timeString.value = e.range.data[0] + '_' + e.range.data.slice(-1)
if(e.range.data.length==0){
timeString.value=e.fulldate+'_'
getIncome()
return
}
getIncome()
}
</script>
<style scoped>
.income-container {
padding: 20rpx;
}
/* 收入概览区域 */
.income-overview {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.income-title {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.income-amount {
font-size: 48rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 时间标签 */
.time-tabs {
display: flex;
margin-bottom: 10rpx;
}
.tab-item {
font-size: 28rpx;
color: #666;
margin-right: 40rpx;
padding-bottom: 10rpx;
}
.tab-item.active {
color: #1890ff;
border-bottom: 3rpx solid #1890ff;
}
.month-total {
font-size: 30rpx;
color: #666;
}
/* 收入构成区域 */
.income-composition {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
/* 饼图占位 */
.pie-chart-placeholder {
display: flex;
align-items: center;
}
.pie-chart {
width: 200rpx;
height: 200rpx;
margin-right: 30rpx;
position: relative;
overflow: hidden;
}
.chart-legend {
flex: 1;
}
.legend-item {
display: flex;
align-items: center;
margin-bottom: 15rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.legend-dot.blue {
background-color: var(--nav-mian);
}
.legend-dot.orange {
background-color: var(--nav-vice);
}
.legend-dot.green {
background-color: var(--nav-diluted);
}
.legend-text {
font-size: 26rpx;
color: #666;
}
/* 钱包区域 */
.wallet-section {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
margin-bottom: 20rpx;
}
.wallet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.wallet-title {
font-size: 28rpx;
color: #333;
}
.wallet-amount {
font-size: 36rpx;
font-weight: bold;
color: #ff4d4f;
}
.withdraw-button {
margin: 0;
width: fit-content;
height: 60rpx;
line-height: 60rpx;
font-size: 24rpx;
border-radius: 40rpx;
background-color: #1890ff;
color: #fff;
}
.withdraw-tip {
display: block;
font-size: 22rpx;
color: #999;
text-align: center;
}
/* 收入明细区域 */
.income-detail {
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
}
.detail-header {
margin-bottom: 20rpx;
}
.detail-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.detail-content {
display: flex;
align-items: center;
}
.detail-list {
margin: 15rpx 0 0;
background-color: #fff;
padding: 20rpx 30rpx;
border-radius: 20rpx;
}
/* 图标占位符 - 黑边白底 */
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
}
.detail-info {
flex: 1;
}
.order-id {
display: block;
font-size: 26rpx;
color: #333;
margin-bottom: 5rpx;
}
.order-time {
font-size: 24rpx;
color: #999;
}
.order-amount {
font-size: 32rpx;
font-weight: bold;
color: #ff4d4f;
}
/* 没有更多记录 */
.no-more {
text-align: center;
padding: 40rpx 0;
}
.no-more-text {
font-size: 24rpx;
color: #999;
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<view style="padding: 10rpx 30rpx;">
<view class="" v-for="(item,index) in withdrowList" :key="index"
style="margin-top: 20rpx; gap: 20rpx; background-color: #fff; border-radius: 20rpx; padding: 30rpx; display: flex; align-items: center; justify-content: space-between; ">
<view class="icon-placeholder">
<image :src="Service.GetIconImg('/static/index/income/order.png')" style="width: 55rpx; height: 55rpx;"
mode=""></image>
</view>
<view class="" style="flex: 1;">
<view class="" style="font-weight: bold;">
余额提现-到{{item.payway}}
</view>
<view style=" margin-top: 4rpx; color: #999; font-size: 24rpx;">2025-12-15</view>
</view>
<view class="" style="color: red; font-weight: bold; ">
+15.6
</view>
</view>
<up-loadmore :status="status" />
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service } from '@/Service/Service';
import { ref } from "vue";
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let withdrowList = ref<Array<any>>([])
let status = ref('nomore')
let page = ref(1)
onLoad(() => {
getData()
});
onShow(() => {
});
const getData = () => {
status.value = 'loadmore'
page.value = 1
withdrowList.value = []
getList()
}
//获取订单
const getList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderWithList(page.value).then(res => {
withdrowList.value = [...withdrowList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
})
}
</script>
<style lang="scss">
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,680 @@
<template>
<!-- 导航栏 -->
<view class=""
style=" z-index: 100; padding:50rpx 20rpx 18rpx; box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: #fff; display: flex; align-items: center; justify-content: space-between;">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" size="32rpx"></up-icon>
</view>
<view class="">
订单详情
</view>
<view class="" @click="Service.GoPage('/pages/my/myKF')" style="color: var(--nav-banbacor);">
<image :src="Service.GetIconImg('/static/index/order/message.png')" style="width: 32rpx; height: 32rpx; "
mode=""></image>
</view>
</view>
<view class="" style="width: 100%; height: 88rpx; ">
</view>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 骨架屏订单状态 -->
<view class="skeleton-status"></view>
<!-- 骨架屏订单基本信息 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="skeleton-info-half"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-line"></view>
<view class="skeleton-row">
<view class="skeleton-info-third"></view>
<view class="skeleton-info-btn"></view>
</view>
</view>
<!-- 骨架屏物品清单 -->
<view class="skeleton-basic-info">
<view class="skeleton-row">
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;">
<view class="" style="background-color: #e6e6e6; width: 60%;height: 40rpx; ">
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-row" v-for="(item,index) in 3" :key="index">
<view class="" style="width: 45%;height: 40rpx;border-radius: 4rpx;animation: shimmer 1.5s infinite;">
<view class="" style="background-color: #e6e6e6; width: 90%;height: 40rpx; ">
</view>
</view>
<view class="skeleton-info-half skeleton-right"></view>
<view class="skeleton-info-half skeleton-right"></view>
</view>
<view class="skeleton-list-status"></view>
</view>
<!-- 骨架屏地图区域 -->
<view class="skeleton-map"></view>
<!-- 骨架屏取餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-code"></view>
</view>
<!-- 骨架屏送餐地址 -->
<view class="skeleton-address">
<view class="skeleton-title"></view>
<view class="skeleton-store-name"></view>
<view class="skeleton-address-line"></view>
<view class="skeleton-btn"></view>
<view class="skeleton-remark"></view>
</view>
<!-- 骨架屏底部按钮 -->
<view class="skeleton-bottom">
<view class="skeleton-bottom-btn"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单状态 -->
<view class="order-status"
:style="{ 'background-color':orderStatus==0?'#E6F7FF':(orderStatus==1?'#FFFBE6':'#FFF2F0') }">
<text :style="{ 'color':orderStatus==0?'#1890FF':(orderStatus==1?'#FAAD14':'#FF4D4F') }"
style="font-size: 34rpx; font-weight: 600;">待取餐 · 请尽快到店取餐</text>
</view>
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="info-item">
<view class="label" style="font-weight: 700; font-size: 30rpx;"> {{ orderInfo.distribution=='预约订单'?'预计'+orderInfo.makeTime.split(' ')[1]+'送达':orderInfo.makeTime }} </view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">{{ orderId }}</text>
</view>
<view class="info-item" style="justify-content: space-between;" >
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="16" class="clock-icon"></u-icon>
{{ Service.formatDate(orderInfo.addTime,1) }} 下单
</view>
<view class="">
<button @click="Service.GoPage('/pages/order/abnormal')" class="" style="padding: 0rpx 30rpx; height: 60rpx; font-size: 24rpx; border-radius: 40rpx; background-color: red; color: #fff; " >提交异常</button>
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
数量
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
金额
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'110rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in JSON.parse(orderInfo.detail) " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
{{goodsItem.goodsName}}
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×{{ goodsItem.count }}
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥{{ goodsItem.count*goodsItem.price }}
</view>
</view>
</view>
<view class="" style="display: flex; align-items: center;justify-content: space-between; margin-top: 10rpx; " >
<view class="">
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥{{ Number(orderInfo.postage).toFixed(2) }}</text>
</view>
</view>
<view class="" v-if="JSON.parse(orderInfo.detail).length>2" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 地图区域 -->
<view class="map-section">
<view class="map-placeholder">
<text @click="Service.GoPage('/pages/order/navigation')" class="map-hint">点击查看完整导航</text>
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<text class="section-title">取餐地址</text>
<view class="address-content">
<view class="store-name">{{ storeInfo.name }}</view>
<text class="address">{{ storeInfo.city }}{{storeInfo.region }}{{ storeInfo.address }}</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button @click="call(storeInfo.phone)" icon="phone" type="primary" shape="circle" text="拨打商家"></up-button>
</view>
<view class="pickup-code">
<text class="code-label">取餐号:</text>
<text class="code-value">A123</text>
</view>
</view>
</view>
<!-- 送餐地址 -->
<view class="address-section">
<text class="section-title">送餐地址</text>
<view class="address-content">
<text class="user-name">{{ JSON.parse(orderInfo.address).realName }}</text>
<text class="address">{{ JSON.parse(orderInfo.address).address }}</text>
<view class="" style="margin-bottom: 20rpx;">
<up-button @click="call(JSON.parse(orderInfo.address).phone)" icon="phone" type="primary" shape="circle" text="拨打商家"></up-button>
</view>
<view v-if="orderInfo.remark" class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">{{ orderInfo.remark }}</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action">
<up-button color="var(--nav-vice)" class="confirm-btn">我已取餐</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
let deliveryTime = ref('')
let orderInfo = ref<any>({})
let storeInfo = ref<any>({})
let storeLocation = ref<any>({})
let orderId = ref('')
onLoad((data : any) => {
orderId.value = data.orderId
getData()
})
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
loading.value = false
if (res.data) {
deliveryTime.value = res.data.deliveryTime
orderInfo.value = res.data.orderInfo
storeInfo.value = res.data.storeInfo
storeLocation.value = res.data.storeLocation
}
})
}
const call=(phone:string)=>{
uni.makePhoneCall({
phoneNumber:phone
})
}
</script>
<style scoped>
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 140rpx;
}
/* 骨架屏导航栏 */
.skeleton-nav {
height: 88rpx;
position: fixed;
top: 0;
left: 0;
width: 100vw;
background-color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20rpx;
z-index: 100;
}
.skeleton-nav-item {
width: 32rpx;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-nav-title {
width: 180rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单状态 */
.skeleton-status {
height: 100rpx;
background-color: #fff;
padding: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-status::after {
content: '';
width: 350rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏订单基本信息 */
.skeleton-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-row {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
}
.skeleton-info-half {
width: 45%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-right {
width: 10%;
}
.skeleton-line {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-third {
width: 60%;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-info-btn {
width: 20%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 25rpx;
animation: shimmer 1.5s infinite;
}
/* 列表状态 */
.skeleton-list-status {
height: 60rpx;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.skeleton-list-status::after {
content: '';
width: 200rpx;
height: 45rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏地图区域 */
.skeleton-map {
margin: 20rpx;
height: 400rpx;
background-color: #e6e6e6;
border-radius: 20rpx;
animation: shimmer 1.5s infinite;
position: relative;
overflow: hidden;
}
.skeleton-map::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, transparent 25%, rgba(255, 255, 255, 0.2) 25%, rgba(255, 255, 255, 0.2) 50%, transparent 50%, transparent 75%, rgba(255, 255, 255, 0.2) 75%, rgba(255, 255, 255, 0.2));
background-size: 100rpx 100rpx;
animation: shimmer 1.5s infinite linear;
}
/* 骨架屏地址区域 */
.skeleton-address {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.skeleton-title {
width: 120rpx;
height: 30rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 25rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-store-name {
width: 70%;
height: 50rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-address-line {
width: 90%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-bottom: 15rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-btn {
height: 70rpx;
background-color: #e6e6e6;
border-radius: 35rpx;
margin: 20rpx 0;
animation: shimmer 1.5s infinite;
}
.skeleton-code {
width: 50%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
.skeleton-remark {
width: 80%;
height: 26rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏底部按钮 */
.skeleton-bottom {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.skeleton-bottom-btn {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
opacity: 0.6;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.6;
}
}
/* 骨架屏滑动动画 */
@keyframes shimmer-slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
/* end */
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin-bottom: 25rpx;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin: 10rpx 0;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
</style>

View File

@@ -0,0 +1,14 @@
export class StoreAssist{
static Get(key:string):any
{
return uni.getStorageSync(key);
}
static Set(key:string,value:any):void
{
uni.setStorageSync(key, value);
}
static Delete(key:string):void
{
uni.removeStorageSync(key);
}
}

View File

@@ -0,0 +1,274 @@
<template>
<view v-if="loading" class="pure-css-skeleton">
<view style=" margin: 20rpx 0; padding: 20rpx; background-color: #fff; border-radius: 10rpx;">
<!-- 头像区域骨架 -->
<view class="avatar-skeleton-wrapper"
style="display: flex; flex-direction: column; justify-content: center; align-items: center;">
<view class="skeleton-circle animate-pulse"></view>
<view class="skeleton-text animate-pulse" style="margin-top: 15rpx;"></view>
</view>
<!-- 表单区域骨架 -->
<view class="form-skeleton-wrapper" style="margin-top: 30rpx;">
<!-- 昵称 -->
<view class="form-item-skeleton">
<view class="form-label-skeleton animate-pulse"></view>
<view class="form-input-skeleton animate-pulse"></view>
</view>
<!-- 性别 -->
<view class="form-item-skeleton">
<view class="form-label-skeleton animate-pulse"></view>
<view class="form-radio-skeleton">
<view class="radio-item-skeleton animate-pulse"></view>
<view class="radio-item-skeleton animate-pulse"></view>
</view>
</view>
<!-- 生日 -->
<view class="form-item-skeleton">
<view class="form-label-skeleton animate-pulse"></view>
<view class="form-input-skeleton animate-pulse"></view>
</view>
<!-- 手机号 -->
<view class="form-item-skeleton">
<view class="form-label-skeleton animate-pulse"></view>
<view class="form-input-skeleton animate-pulse"></view>
</view>
<!-- 邮箱 -->
<view class="form-item-skeleton">
<view class="form-label-skeleton animate-pulse"></view>
<view class="form-input-skeleton animate-pulse"></view>
</view>
</view>
<!-- 底部空间占位 -->
<view style="width: 100%; height: 200rpx;"></view>
</view>
<!-- 底部保存按钮骨架 -->
<view class="bottom-button-skeleton animate-pulse"></view>
</view>
<view v-else style=" margin: 20rpx; padding: 20rpx;">
<view @click="uploadFImg()" class=""
style=" display: flex; flex-direction: column; justify-content: center; align-items: center; ">
<img v-if="userInfo.headImg!=''" :src="Service.GetMateUrlByImg(userInfo.headImg)"
style="width: 140rpx; height: 140rpx; border-radius: 50%; " alt="" />
<view v-else class=""
style="background-color: #EBEBEB; width: 140rpx; height: 140rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; ">
<image :src="Service.GetIconImg('/static/index/my/edit/photo.png')"
style="width: 50rpx; height: 50rpx; " alt=""> </image>
</view>
<view class="" style="margin-top: 15rpx; font-size: 26rpx; color: #999999; ">
点击更换头像
</view>
</view>
<view class="" style=" margin-top: 30rpx; ">
<up-form labelWidth='90' labelPosition="left" :borderBottom="true" :model="userInfo" ref="form1">
<up-form-item label="昵称" prop="userInfo.name" ref="item1" :borderBottom="true">
<up-input inputAlign='right' v-model="userInfo.nick" border="none"></up-input>
</up-form-item>
<up-form-item label="性别" :borderBottom="true" style="position: relative;" prop="userInfo.sex">
<view class="" style=" position: absolute; top: 10rpx; right: 0; ">
<up-radio-group v-model="userInfo.sex" placement="row">
<up-radio v-for="(item, index) in radiolist1" activeColor='#FF6A00' :key="index"
iconPlacement="right" :label="item.name" :name="item.name">
</up-radio>
</up-radio-group>
</view>
</up-form-item>
<up-form-item label="生日" :borderBottom="true" prop="userInfo.sex" style="position: relative;">
<view @click="showDate=true" class=""
style=" position: absolute; top: 25rpx; right: 0; display: flex; align-items: center; ">
{{Service.formatDate(userInfo.date,2)}}
<u-icon name="arrow-right" size="24rpx" color="#000"></u-icon>
</view>
</up-form-item>
<up-form-item label="手机号" :borderBottom="true" prop="userInfo.sex">
<up-input inputAlign='right' v-model="userInfo.phone" border="none"></up-input>
</up-form-item>
</up-form>
</view>
<up-datetime-picker :maxDate="nowDate" :minDate="631123200000" :show="showDate" @cancel="showDate=!showDate"
@confirm="dateConfirm" v-model="userInfo.date" mode="date"></up-datetime-picker>
<view class="" style="width: 100%; height: 200rpx;">
</view>
<view class="" style=" width: 100% ; background-color: #fff; position: fixed; bottom: 15rpx; left: 0; ">
<view class=""
style=" margin: 0 20rpx; padding: 26rpx 0; color: #fff; display: flex; align-items: center; justify-content: center; border-radius: 60rpx; background-color: var(--nav-mian);">
保存信息
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad } from "@dcloudio/uni-app";
import { Service } from '@/Service/Service';
import { ref } from "vue";
let loading = ref(true)
let showDate = ref(false)
const userInfo = ref({
headImg: '',
age: '1',
sex: '',
phone: '1',
date: Date.now(),
nick: '大大怪将军'
})
let nowDate = ref()
const radiolist1 = ref([
{
name: '男',
disabled: false,
},
{
name: '女',
disabled: false,
}
]);
onLoad(() => {
setTimeout(() => {
loading.value = false;
}, 1500);
});
onShow(() => {
nowDate.value = new Date()
});
const dateConfirm = (e) => {
showDate.value = !showDate.value
userInfo.value.date = e.value
}
const uploadFImg = () => {
uni.chooseImage({
count: 1, // 最多选择3张图片
sizeType: ['original', 'compressed'], // 支持原图和压缩图
sourceType: ['album', 'camera'], // 可从相册选择或使用相机拍照
success: function (res) {
let path = res.tempFiles[0].path
userInfo.value.headImg = path
// Service.uploadH5(path, 'Avatar', data => {
// userInfo.value.headImg = data.split(',')[2].split(':')[1].split('"')[1]
// })
},
fail: function (err) {
console.error('选择失败:', err.errMsg);
}
})
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.pure-css-skeleton {
background-color: #f8f8f8;
}
/* 骨架屏基础样式 */
.skeleton-circle {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
background-color: #e0e0e0;
}
.skeleton-text {
width: 120rpx;
height: 26rpx;
background-color: #e0e0e0;
border-radius: 4rpx;
}
.form-item-skeleton {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1px solid #f5f5f5;
position: relative;
}
.form-item-skeleton:last-child {
border-bottom: none;
}
.form-label-skeleton {
width: 90rpx;
height: 30rpx;
background-color: #e0e0e0;
border-radius: 4rpx;
}
.form-input-skeleton {
position: absolute;
right: 0;
width: 50%;
height: 30rpx;
background-color: #e0e0e0;
border-radius: 4rpx;
}
.form-radio-skeleton {
position: absolute;
right: 0;
display: flex;
gap: 20rpx;
}
.radio-item-skeleton {
width: 100rpx;
height: 30rpx;
background-color: #e0e0e0;
border-radius: 4rpx;
}
.bottom-button-skeleton {
position: fixed;
bottom: 15rpx;
left: 20rpx;
right: 20rpx;
height: 90rpx;
background-color: #e0e0e0;
border-radius: 60rpx;
}
/* 动画效果 - 不使用组件纯CSS实现 */
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.6;
}
100% {
opacity: 1;
}
}
.animate-pulse {
animation: pulse 1.5s ease-in-out infinite;
}
</style>

View File

@@ -0,0 +1,361 @@
<template>
<view v-if="loading" class="skeleton-container">
<!-- 安全等级卡片骨架 -->
<view class="security-level-card skeleton-card">
<view class="security-level-header">
<view class="skeleton-line skeleton-line-sm"></view>
</view>
<view class="progress-bar skeleton-progress"></view>
<view class="skeleton-line skeleton-line-xs"></view>
</view>
<!-- 安全设置列表骨架 -->
<view class="security-settings skeleton-settings">
<view class="security-item skeleton-item" v-for="i in 3" :key="i">
<view class="item-left">
<view class="item-icon skeleton-icon"></view>
<view class="skeleton-line skeleton-line-md"></view>
</view>
<view class="item-right">
<view class="skeleton-line skeleton-line-sm"></view>
</view>
</view>
</view>
<!-- 安全提示卡片骨架 -->
<view class="security-tip-card skeleton-tip-card">
<view class="tip-content">
<view class="skeleton-circle"></view>
<view class="skeleton-line skeleton-line-full"></view>
</view>
</view>
</view>
<view v-else class="account-security-page" style="overflow: hidden;" >
<!-- 安全等级卡片 -->
<view class="security-level-card">
<view class="security-level-header">
<text class="security-level-label">账号安全等级:</text>
<text class="security-level-value">高</text>
</view>
<view class="progress-bar">
<view class="progress-fill"></view>
</view>
<view class="security-desc">您已完成所有安全设置</view>
</view>
<!-- 安全设置列表 -->
<view class="security-settings">
<!-- 实名认证 -->
<view class="security-item" @click="Service.GoPage(item.path)" v-for="(item,index) in funcList" :key="index">
<view class="item-left">
<view class="item-icon">
<image :src="Service.GetIconImg(item.icon)" style="width: 100%; height: 100%; " mode=""></image>
</view>
<text class="item-label">{{item.name}}</text>
</view>
<view class="item-right">
<text class="item-status">{{ item.des }}</text>
<up-icon name="arrow-right" size="19" color="#999"></up-icon>
</view>
</view>
</view>
<!-- 安全提示卡片 -->
<view class="security-tip-card">
<view class="tip-content">
<up-icon name="info-circle-fill" size="24" color="#999"></up-icon>
<text class="tip-text">为保障您的账号安全,请勿泄露验证码,定期更新密码。</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app'
import { Service } from "@/Service/Service"
import { ref } from 'vue'
import { CNRiderDataService } from "@/Service/CN/CNRiderDataService"
let loading = ref(true)
let riderInfo=ref<any>({})
const funcList = ref([
{
name: '实名认证',
icon: '/static/index/my/security/security.png',
des: "已认证",
path:'/pages/my/authentication'
},
{
name: '手机号绑定',
icon: '/static/index/my/security/phone.png',
des: "",
path:'/pages/my/setConnect?type=1'
},
{
name: '紧急联系人',
icon: '/static/index/my/security/user.png',
des: "",
path:'/pages/my/setConnect?type=0'
},
{
name: '修改密码',
icon: '/static/index/my/security/mima.png',
des: "",
path:'/pages/my/editPasssword'
}
])
// 页面加载时的逻辑
onLoad(() => {
})
onShow(()=>{
getData()
getConnect()
})
const getData = () => {
CNRiderDataService.GetRiderInfo().then(res => {
loading.value = false
if(res.data){
riderInfo.value=res.data.riderInfo
funcList.value[0].des=riderInfo.value.status===-1?'未认证':(riderInfo.value.status===0?'审核中':'已认证')
funcList.value[1].des='已绑定'+' '+riderInfo.value.phone.slice(0,3)+'****'+riderInfo.value.phone.slice(-4)
}
})
}
const getConnect=()=>{
CNRiderDataService.GetRiderExigency().then(res=>{
if(res.data){
funcList.value[2].des= res.data.info? res.data.info.phone : '未绑定'
}
})
}
</script>
<style lang="scss">
page {
background-color: #f5f5f5;
}
// 安全等级卡片样式
.security-level-card {
background-color: #E6F7FF;
margin: 30rpx 30rpx 30rpx;
padding: 30rpx;
border-radius: 16rpx;
}
.security-level-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.security-level-label {
font-size: 28rpx;
color: #333;
}
.security-level-value {
font-size: 32rpx;
font-weight: 600;
color: #007AFF;
}
.progress-bar {
width: 100%;
height: 8rpx;
background-color: #B3D8FF;
border-radius: 4rpx;
overflow: hidden;
margin-bottom: 16rpx;
}
.progress-fill {
width: 100%;
height: 100%;
background-color: #007AFF;
border-radius: 4rpx;
}
.security-desc {
font-size: 24rpx;
color: #666;
}
// 安全设置列表样式
.security-settings {
background-color: #fff;
margin: 0 30rpx 30rpx;
border-radius: 16rpx;
overflow: hidden;
}
.security-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.security-item:last-child {
border-bottom: none;
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.item-label {
font-size: 32rpx;
color: #333;
}
.item-right {
display: flex;
align-items: center;
}
.item-status {
font-size: 28rpx;
color: #4CD964;
margin-right: 10rpx;
}
// 安全提示卡片样式
.security-tip-card {
background-color: #FFF8E8;
margin: 0 30rpx;
padding: 24rpx;
border-radius: 16rpx;
}
.tip-content {
display: flex;
align-items: flex-start;
}
.tip-text {
flex: 1;
font-size: 24rpx;
color: #999;
line-height: 36rpx;
margin-left: 10rpx;
}
// 骨架屏基础样式
.skeleton-line {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 4rpx;
}
.skeleton-icon {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 8rpx;
}
.skeleton-circle {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: 50%;
width: 40rpx;
height: 40rpx;
margin-right: 16rpx;
}
.skeleton-progress {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
// 骨架屏线条尺寸
.skeleton-line-xs {
height: 28rpx;
width: 200rpx;
margin-top: 16rpx;
}
.skeleton-line-sm {
height: 32rpx;
width: 300rpx;
}
.skeleton-line-md {
height: 36rpx;
width: 240rpx;
}
.skeleton-line-full {
height: 36rpx;
width: calc(100% - 60rpx);
}
// 骨架屏动画
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
// 骨架屏容器样式
.skeleton-container {
overflow: hidden;
}
// 骨架屏卡片样式
.skeleton-card {
background-color: #fff;
}
.skeleton-settings {
background-color: #fff;
}
.skeleton-item {
border-bottom: 1rpx solid #f0f0f0;
}
.skeleton-item:last-child {
border-bottom: none;
}
.skeleton-tip-card {
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,600 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton-container">
<!-- 余额区域骨架屏 -->
<view class="skeleton-balance-section">
<view class="skeleton-balance-label"></view>
<view class="skeleton-balance-amount"></view>
<view class="skeleton-balance-current"></view>
</view>
<!-- 账户选择区域骨架屏 -->
<view class="skeleton-account-section">
<view class="skeleton-section-title"></view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
<view class="skeleton-account-item">
<view class="skeleton-account-icon"></view>
<view class="skeleton-account-name"></view>
<view class="skeleton-account-radio"></view>
</view>
</view>
<!-- 提示信息骨架屏 -->
<view class="skeleton-tip-section">
<view class="skeleton-tip-icon"></view>
<view class="skeleton-tip-content">
<view class="skeleton-tip-line"></view>
<view class="skeleton-tip-line-small"></view>
</view>
</view>
<!-- 确认按钮骨架屏 -->
<view class="skeleton-confirm-section">
<view class="skeleton-confirm-button"></view>
</view>
</view>
<!-- 真实内容 -->
<view v-else class="withdraw-container">
<!-- 可提现金额 -->
<view class="balance-section">
<text class="balance-label">可提现金额</text>
<text class="balance-amount">¥{{ riderAcc.account }}</text>
<text class="current-balance">当前钱包余额</text>
</view>
<!-- 提现账户选择 -->
<view class="account-section">
<text class="section-title">提现账户</text>
<view class=""
style="display: flex;align-items: center; border-bottom: 4rpx solid #e2e2e2;padding-bottom: 10px; margin-bottom: 10px;">
<up-icon name="zhifubao-circle-fill" size="30" color="#1890ff"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
支付宝
</view>
<view @click="changePay(0)" class="">
<view v-if="current==0" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
<view class="" style="display: flex;align-items: center;">
<up-icon name="weixin-circle-fill" size="30" color="#28C445"></up-icon>
<view style="flex: 1; margin-left: 30rpx; " class="">
微信
</view>
<view @click="changePay(1)" class="">
<view v-if="current==1" class="radio-circle">
<view class="radio-inner"></view>
</view>
<view v-else class="radio-no-circle">
</view>
</view>
</view>
</view>
<!-- 提现账号 -->
<view class="" v-if="current!=null" style="margin: 20rpx 30rpx; ">
<view class="" style="font-size: 34rpx; font-weight: 600; ">
账号信息
</view>
<up-form labelPosition="top" label-width="200" :model="account" ref="form1">
<up-form-item label="姓名" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.name" @change="changePrice" placeholder="请输入姓名" border="none"></up-input>
</up-form-item>
<up-form-item :label="current==0? '支付宝账号':'微信账号'" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.id" :placeholder="current==0? '请输入支付宝账号':'请输入微信账号'"
border="none"></up-input>
</up-form-item>
<up-form-item label="提现金额" prop="userInfo.name" :borderBottom="true" ref="item1">
<up-input v-model="account.price" type="digit" placeholder="请输入提现金额" border="none"></up-input>
</up-form-item>
</up-form>
</view>
<!-- 提示信息 -->
<view class="tip-section">
<up-icon name="clock" size="18" color="#1890ff"></up-icon>
<view class="" style="margin-left: 10rpx;">
<view class="tip-text">预计 T+1 工作日到账</view>
<view class="tip-text" style="color: #666666; font-size: 24rpx; margin-top: 10rpx; ">无手续费</view>
</view>
</view>
<!-- 确认按钮 -->
<view class="confirm-section">
<button class="confirm-button" @click="confirmWithdraw">确认提现</button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad, onShow } from '@dcloudio/uni-app';
import { ref } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
import { CNRiderDataService } from '@/Service/CN/CNRiderDataService'
let current = ref(null)
let loading = ref(true)
let account = ref({
id: '',
price: "",
name: ''
})
let riderAcc = ref<any>({})
onLoad(() => {
})
onShow(()=>{
getData()
})
const getData = () => {
CNRiderDataService.GetRiderAccInfo().then(res => {
loading.value = false
if (res.data) {
riderAcc.value = res.data.riderAcc
}
})
}
const changePay = (item : any) => {
current.value = item
}
const changePrice = (e : string) => {
console.log(e);
}
// 确认提现
const confirmWithdraw = () => {
if(!current.value){
Service.Msg('请选择提现方式!')
return
}
if (!account.value.name) {
Service.Msg('请输入姓名!')
return
}
if (!account.value.price) {
Service.Msg('请输入提现金额!')
return
}
if (!account.value.id) {
Service.Msg('请输入账户号!')
return
}
uni.showModal({
title: '提示',
content: '请仔细确认身份信息?',
success: function (res) {
if (res.confirm) {
CNRiderOrderService.AddRiderWith(Number(account.value.price), current.value == 0 ? '支付宝' : '微信', account.value.name, account.value.id).then(res => {
if (res.code == 0) {
Service.Msg('提现成功')
account.value.name=''
account.value.price=null
account.value.id=''
setTimeout(() => {
Service.GoPage('/pages/my/withDrowList')
}, 1000)
} else {
Service.Msg(res.msg)
}
})
} else {
// 用户点击取消后的操作
}
}
});
};
</script>
<style scoped>
.withdraw-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 选择 */
.radio-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid var(--nav-mian);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.radio-inner {
width: 20rpx;
height: 20rpx;
background-color: var(--nav-mian);
border-radius: 50%;
}
.radio-no-circle {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #dadada;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
/* 顶部导航栏 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
background-color: #fff;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.nav-title {
font-size: 36rpx;
font-weight: 600;
color: #000;
}
.back-icon {
width: 40rpx;
height: 40rpx;
}
.help-icon {
width: 40rpx;
height: 40rpx;
}
/* 余额显示区域 */
.balance-section {
background-color: #fff;
padding: 40rpx 30rpx;
}
.balance-label {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.balance-amount {
font-size: 60rpx;
font-weight: bold;
color: #ff4757;
display: block;
margin-bottom: 10rpx;
}
.current-balance {
font-size: 28rpx;
color: #999;
}
/* 账户选择区域 */
.account-section {
background-color: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 20rpx;
}
.account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 25rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.account-item:last-child {
border-bottom: none;
}
.account-info {
display: flex;
align-items: center;
}
.account-icon {
width: 60rpx;
height: 60rpx;
border-radius: 12rpx;
background-color: #1677ff;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
border: 2rpx solid #000;
}
.account-icon.wechat {
background-color: #07c160;
}
.icon-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
.account-name {
font-size: 32rpx;
color: #333;
}
/* 提示信息区域 */
.tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.tip-text {
font-size: 28rpx;
margin-left: 10rpx;
}
/* 确认按钮区域 */
.confirm-section {
background-color: #fff;
position: fixed;
width: 100vw;
left: 0;
bottom: 0;
padding: 30rpx;
}
.confirm-button {
width: 100%;
height: 90rpx;
background-color: #1677ff;
color: #fff;
font-size: 36rpx;
font-weight: 600;
border-radius: 45rpx;
display: flex;
align-items: center;
justify-content: center;
border: none;
}
.confirm-button:active {
background-color: #0958d9;
}
/* 骨架屏样式 */
.skeleton-container {
min-height: 100vh;
background-color: #fff;
padding-bottom: 40rpx;
}
/* 骨架屏动画 */
@keyframes shimmer {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}
/* 骨架屏通用样式 */
.skeleton-balance-section,
.skeleton-account-section,
.skeleton-tip-section {
padding: 40rpx 30rpx;
margin-bottom: 20rpx;
}
/* 余额区域骨架屏 */
.skeleton-balance-section {
background-color: #fff;
}
.skeleton-balance-label {
width: 30%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-amount {
width: 40%;
height: 60rpx;
background-color: #e6e6e6;
margin-bottom: 20rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-balance-current {
width: 25%;
height: 28rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 账户选择区域骨架屏 */
.skeleton-account-section {
background-color: #fff;
}
.skeleton-section-title {
width: 25%;
height: 32rpx;
background-color: #e6e6e6;
margin-bottom: 30rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 4rpx solid #e2e2e2;
}
.skeleton-account-item:last-child {
border-bottom: none;
padding-bottom: 0;
margin-bottom: 0;
}
.skeleton-account-icon {
width: 60rpx;
height: 60rpx;
background-color: #e6e6e6;
border-radius: 12rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-name {
flex: 1;
height: 32rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
margin-right: 20rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-account-radio {
width: 36rpx;
height: 36rpx;
background-color: #e6e6e6;
border-radius: 50%;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 提示信息骨架屏 */
.skeleton-tip-section {
display: flex;
align-items: flex-start;
background-color: #e6f7ff;
padding: 20rpx 30rpx;
margin: 0 30rpx 20rpx;
border-radius: 10rpx;
}
.skeleton-tip-icon {
width: 40rpx;
height: 40rpx;
background-color: #e6e6e6;
border-radius: 50%;
margin-right: 15rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-content {
flex: 1;
}
.skeleton-tip-line {
width: 70%;
height: 28rpx;
background-color: #e6e6e6;
margin-bottom: 15rpx;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
.skeleton-tip-line-small {
width: 50%;
height: 24rpx;
background-color: #e6e6e6;
border-radius: 4rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
/* 确认按钮骨架屏 */
.skeleton-confirm-section {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background-color: #fff;
padding: 30rpx;
}
.skeleton-confirm-button {
width: 100%;
height: 90rpx;
background-color: #e6e6e6;
border-radius: 45rpx;
animation: shimmer 1.5s infinite;
background-image: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 1000px 100%;
}
</style>

View File

@@ -0,0 +1,451 @@
<template>
<!-- 骨架屏 -->
<view v-if="loading" class="order-detail skeleton-loading">
<!-- 订单基本信息骨架屏 -->
<view class="order-basic-info skeleton-section">
<view class="" style="display: flex; justify-content: space-between;align-items: center; ">
<view class="skeleton-block skeleton-short"></view>
<view class="skeleton-block skeleton-short"></view>
</view>
<view class="skeleton-block skeleton-long"></view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;">
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-medium"></view>
</view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<!-- 物品清单骨架屏 -->
<view class="order-basic-info skeleton-section">
<view v-for="item in 4" class="skeleton-block" style="width: 100%; height: 40rpx; "></view>
<view class="" style="display: flex; justify-content: center;">
<view class="skeleton-block skeleton-short"></view>
</view>
</view>
<!-- 地图区域骨架屏 -->
<view class="map-section skeleton-section">
<view class="map-placeholder skeleton-map"></view>
</view>
<!-- 地址区域骨架屏 -->
<view class="address-section skeleton-section">
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-medium"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
<view class="skeleton-block skeleton-long"></view>
</view>
<view class="bottom-padding"></view>
<!-- 底部按钮骨架屏 -->
<view class="bottom-action">
<view class="skeleton-button"></view>
</view>
</view>
<!-- 实际内容 -->
<view v-else class="order-detail">
<!-- 订单基本信息 -->
<view class="order-basic-info">
<view class="info-item">
<view class="label" style="font-weight: 700; font-size: 30rpx;"> {{ orderInfo.distribution=='预约订单'?'预计'+orderInfo.makeTime.split(' ')[1]+'送达':orderInfo.makeTime }} </view>
</view>
<view class="info-item">
<text class="label">订单编号 : </text>
<text class="value">{{ orderId }}</text>
</view>
<view class="info-item">
<view class="label" style="display: flex; align-items: baseline;">
<u-icon name="clock" size="16" class="clock-icon"></u-icon>
{{ Service.formatDate(orderInfo.addTime,1) }} 下单
</view>
</view>
</view>
<!-- 物品清单 -->
<view class="order-basic-info">
<view class="" style="display: flex; align-items: center; ">
<view class="" style="flex: 1; font-size: 34rpx; font-weight: 600; ">
物品清单
</view>
<view class="" style="width: 100rpx; text-align: right; font-size: 30rpx; ">
数量
</view>
<view class="" style="width: 120rpx; text-align: right; font-size: 30rpx; ">
金额
</view>
</view>
<!-- 商品列表 -->
<view class="" :style="{'height':isShow?'110rpx':'fit-content' }" style="overflow: hidden;">
<view class="" v-for="(goodsItem,goodsIndex) in JSON.parse(orderInfo.detail) " :key="goodsIndex"
style="display: flex; align-items: center; margin-top: 15rpx; ">
<view class="" style="flex: 1; ">
{{goodsItem.goodsName}}
</view>
<view class="" style="width: 100rpx; text-align: right; ">
×{{ goodsItem.count }}
</view>
<view class="" style="width: 120rpx; text-align: right; ">
¥{{ goodsItem.count*goodsItem.price }}
</view>
</view>
</view>
<view class="" style="display: flex; align-items: center; justify-content: space-between;" >
<view class="">
</view>
<view class="info-item">
<text class="label" style="font-weight: 700;">配送费</text>
<text class="value price">¥{{ Number(orderInfo.postage).toFixed(2) }}</text>
</view>
</view>
<view class="" v-if="JSON.parse(orderInfo.detail).length>2" @click="isShow=!isShow"
style=" margin-top: 20rpx; display: flex; align-items: center; justify-content: center; color: #666; ">
<up-icon :name="isShow?'arrow-down':'arrow-up'" color="#666" size="18"></up-icon>
{{isShow?'展开':'收入'}}
</view>
</view>
<!-- 地图区域 -->
<view class="map-section">
<view class="map-placeholder">
<text @click="Service.GoPage('/pages/order/navigation')" class="map-hint">点击查看完整导航</text>
</view>
</view>
<!-- 取餐地址 -->
<view class="address-section">
<view class="" style=" border-bottom: 4rpx solid #e2e2e2; ">
<view class="section-title">取餐地址 : </view>
<view class="address-content">
<text class="store-name">{{ storeInfo.name }}</text>
<text class="address"> {{ storeInfo.city }}{{storeInfo.region }}{{ storeInfo.address }}</text>
</view>
</view>
<view style="margin: 10rpx 0; font-size: 30rpx;font-weight: 800;color: #333;">送餐地址 : </view>
<view class="address-content">
<text class="user-name">{{ JSON.parse(orderInfo.address).realName }}</text>
<text class="address">{{ JSON.parse(orderInfo.address).address }}</text>
<view v-if="orderInfo.remark" class="remark">
<text class="remark-label">备注:</text>
<text class="remark-content">请放门口,勿按门铃</text>
</view>
</view>
</view>
<view class="" style="width: 100vw; height: 140rpx; ">
</view>
<!-- 底部按钮 -->
<view class="bottom-action">
<up-button @click="placeOrder()" color="var(--nav-mian)" class="confirm-btn">立即接单</up-button>
</view>
</view>
</template>
<script setup lang="ts">
import { onLoad } from '@dcloudio/uni-app';
import { ref, onMounted } from 'vue';
import { Service } from '@/Service/Service';
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
// 加载状态
const loading = ref(true);
let orderStatus = ref(0)
let isShow = ref(true)
let deliveryTime = ref('')
let orderInfo = ref<any>({})
let storeInfo = ref<any>({})
let storeLocation = ref<any>({})
let orderId = ref('')
onLoad((data : any) => {
orderId.value = data.orderId
getData()
})
const getData = () => {
CNRiderOrderService.GetUnitOrderInfo(orderId.value).then(res => {
loading.value = false
if (res.data) {
deliveryTime.value = res.data.deliveryTime
orderInfo.value = res.data.orderInfo
storeInfo.value = res.data.storeInfo
storeLocation.value = res.data.storeLocation
}
})
}
const placeOrder = () => {
CNRiderOrderService.RiderTakeOrder(orderId.value).then(res => {
if (res.data) {
Service.Msg('接单成功!')
setTimeout(() => {
Service.GoPageBack()
},1000)
} else {
Service.Msg(res.msg)
}
})
}
</script>
<style scoped>
.order-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
/* 订单状态样式 */
.order-status {
background-color: #fff;
padding: 30rpx;
text-align: center;
}
/* 订单基本信息样式 */
.order-basic-info {
background-color: #fff;
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.info-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
font-size: 28rpx;
}
.info-item:last-child {
margin-bottom: 0;
}
.label {
color: #666;
margin-right: 10rpx;
}
.value {
color: #333;
}
.value.highlight {
color: var(--nav-diluted);
font-weight: 500;
}
.value.price {
color: var(--nav-diluted);
font-weight: 700;
}
.clock-icon {
color: #666;
margin-right: 8rpx;
}
/* 地图区域样式 */
.map-section {
margin: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.map-placeholder {
width: 100%;
height: 400rpx;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid #e8e8e8;
}
.map-placeholder::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #f5f5f5 25%, #e6e6e6 25%, #e6e6e6 50%, #f5f5f5 50%, #f5f5f5 75%, #e6e6e6 75%, #e6e6e6 100%);
background-size: 20rpx 20rpx;
opacity: 0.3;
}
.map-hint {
font-size: 28rpx;
color: #666;
position: relative;
z-index: 1;
}
/* 地址区域样式 */
.address-section {
background-color: #fff;
margin: 20rpx;
padding: 30rpx;
border-radius: 20rpx;
}
.section-title {
font-size: 30rpx;
font-weight: 800;
color: #333;
margin: 10rpx 0;
}
.address-content {
position: relative;
}
.store-name,
.user-name {
font-size: 34rpx;
font-weight: 600;
margin-bottom: 15rpx;
display: block;
}
.address {
font-size: 26rpx;
color: #666;
line-height: 1.5;
margin-bottom: 25rpx;
display: block;
}
.pickup-code,
.remark {
font-size: 26rpx;
}
.code-label,
.code-value {
color: var(--nav-mian);
font-weight: 600;
}
.remark-label,
.remark-content {
color: #FAAD14;
font-weight: 600;
}
/* 底部按钮样式 */
.bottom-action {
background-color: #fff;
width: 100vw;
position: fixed;
bottom: 0;
left: 0;
padding: 20rpx 30rpx;
}
.confirm-btn {
width: 100%;
height: 90rpx;
font-size: 32rpx;
line-height: 90rpx;
border-radius: 45rpx;
}
/* 骨架屏样式 */
.skeleton-loading .skeleton-section {
margin: 20rpx 20rpx;
padding: 30rpx;
border-radius: 20rpx;
background-color: #fff;
}
.skeleton-block {
background-color: #f0f0f0;
margin-bottom: 20rpx;
border-radius: 4rpx;
position: relative;
overflow: hidden;
}
.skeleton-short {
height: 40rpx;
width: 30%;
}
.skeleton-medium {
height: 30rpx;
width: 30%;
}
.skeleton-long {
height: 30rpx;
width: 90%;
}
.skeleton-map {
height: 400rpx;
background-color: #f0f0f0;
border-radius: 20rpx;
margin: 0;
}
.skeleton-button {
height: 90rpx;
background-color: #f0f0f0;
border-radius: 45rpx;
}
.bottom-padding {
height: 140rpx;
}
/* 骨架屏动画 */
.skeleton-block::after,
.skeleton-map::after,
.skeleton-button::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
}
</style>

View File

@@ -0,0 +1,131 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
import { onMounted, ref } from "vue";
import { WebSocket } from "@/Service/Comm/TwWebSocket";
import { Service } from "@/Service/Service"
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let isios = ref(false)
var socket = new WebSocket();
const currentLatitude = ref(0);
const currentLongitude = ref(0);
let locationTimer: ReturnType<typeof setInterval> | null = null;
onLaunch(() => {
isios.value = uni.getSystemInfoSync().platform != 'ios'//是否为ios
//#ifdef APP-PLUS//app
if (isios.value) {
// getVersion()//更新
}
//#endif
//链接服务器
uni.$on("ImCom", () => {
socket.ConnectSocketInit();
})
uni.$on("ImComOff", function (data) {
socket.CloseSocket(data);
})
startFetchingLocation();
});
onShow(() => {
Service.SetStorageCache('isHede',false)
//链接服务器
if (Service.GetUserIsLogin()) {
uni.$emit('ImCom')
}
});
onHide(() => {
Service.SetStorageCache('isHede',true)
});
const startFetchingLocation = () => {
// 安全检查:如果定时器已存在,先清除,防止重复启动
if (locationTimer) {
clearInterval(locationTimer);
}
console.log("开始定时获取位置间隔1分钟...");
// 1. 立即执行第一次获取
getLocationNow();
// 2. 设置定时器,每 60000 毫秒 (1分钟) 执行一次
locationTimer = setInterval(() => {
getLocationNow();
}, 60000);
};
const getLocationNow = () => {
console.log("正在获取当前经纬度...");
uni.getLocation({
type: 'wgs84',
isHighAccuracy: true,
success: (res) => {
console.log('成功获取到新位置:', res);
// 更新页面上的数据显示
console.log(res.latitude,res.longitude,'===')
if(Service.GetUserIsLogin()){
CNRiderOrderService.UpdateRiderLocation(res.longitude,res.latitude).then(res=>{})
}
},
fail: (err) => {
console.error('获取经纬度失败:', err);
// (可选) 可以在这里添加失败提示
// uni.showToast({ title: '获取位置失败', icon: 'none' });
}
});
};
/**
* 停止定时获取位置
*/
const stopFetchingLocation = () => {
if (locationTimer) {
clearInterval(locationTimer);
locationTimer = null; // 清理 ID
}
};
const getUpData = () => {
// #ifdef APP
// plus.runtime.getProperty(plus.runtime.appid, (wgtinfo) => {
// NvpMerchService.GetAppVersion().then(res=>{
// console.log('wgtinfo.versionCode',wgtinfo.versionCode);
// if (res.data.version > wgtinfo.versionCode) {
// setTimeout(function() {
// uni.navigateTo({
// url: "/pages/upData/upData?info=" +
// encodeURIComponent(
// JSON.stringify(res.data))
// })
// }, 1000)
// }
// })
// })
// #endif
}
</script>
<style lang="scss">
@import "uview-plus/index.scss";
@import "colorui/main.css";
@import "colorui/icon.css";
page {
--nav-mian: #1890FF; //全局颜色
--nav-vice: #52C41A; //副颜色
--nav-diluted: #FF4D4F; //次颜色
}
</style>

View File

@@ -0,0 +1,357 @@
<template>
<!-- 导航栏 -->
<view class=""
style=" z-index: 100; padding:50rpx 20rpx 18rpx;box-sizing: border-box; position: fixed;top: 0; left: 0; width: 100vw; background-color: #fff; display: flex; align-items: center; justify-content: space-between; ">
<view class="" @click="Service.GoPageBack()">
<up-icon name="arrow-left" size="32rpx"></up-icon>
</view>
<view class="">
消息通知
</view>
<view class="" style="font-size: 22rpx; color: #999999; " >
全部已读
</view>
</view>
<view class="" style="width: 100%; height: 108rpx; ">
</view>
<view v-if="loading" class="">
<!-- 消息列表骨架 -->
<view class="skeleton-list">
<!-- 模拟4条消息 -->
<view v-for="index in 4" :key="index" class="skeleton-message-item">
<view class="skeleton-message-icon">
<view class="skeleton-icon-container"></view>
<view class="skeleton-unread-dot"></view>
</view>
<view class="skeleton-message-content">
<view class="skeleton-message-header">
<view class="skeleton-message-title"></view>
<view class="skeleton-message-time"></view>
</view>
<view class="skeleton-message-desc">
<view class="skeleton-desc-line"></view>
<view class="skeleton-desc-line short"></view>
</view>
</view>
</view>
<!-- 加载更多骨架 -->
<view class="skeleton-load-more"></view>
</view>
</view>
<view v-else class="message-center-container">
<!-- 标签页 -->
<up-tabs :list="tabList" :current="currentTab" @change="handleTabChange" lineWidth='60' :scrollable="false"
lineColor="var(--nav-mian)"
:activeStyle="{ color: 'var(--nav-mian)', fontWeight: 'bold', transform: 'scale(1.05)'}"
:inactiveStyle="{color: '#606266', transform: 'scale(1)'}"></up-tabs>
<!-- 消息列表 -->
<scroll-view scroll-y class="message-list" :show-scrollbar="false">
<!-- 根据不同标签显示不同的消息 -->
<view v-for="(message, index) in 4" :key="index" class="message-item">
<view class="message-icon">
<view class="icon-container">
<image v-if="currentTab === 1" style="width: 100%; height: 100%;"
:src=" Service.GetIconImg('/static/index/home/shop.png')" mode=""></image>
<image v-if="currentTab === 2" style="width: 100%; height: 100%;"
:src=" Service.GetIconImg('/static/index/home/custom.png')" mode=""></image>
<image v-if="currentTab === 3" style="width: 100%; height: 100%;"
:src=" Service.GetIconImg('/static/index/home/system.png')" mode=""></image>
</view>
<view class="unread-dot"></view>
</view>
<view class="message-content">
<view class="message-header">
<text class="message-title">系统维护通知</text>
<text class="message-time">10分钟前</text>
</view>
<text class="message-desc">为了给您提供更好的服务体验系统将于今晚24:00-次日凌晨2:00进行例行维护</text>
</view>
</view>
<up-loadmore :status="status" />
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { Service } from "@/Service/Service"
import { onLoad } from '@dcloudio/uni-app';
// 标签页数据
const tabList = ref([
{ name: '全部' },
{ name: '订单通知' },
{ name: '客户消息' },
{ name: '系统通知' }
]);
// 当前选中标签
const currentTab = ref(0);
// 加载状态
let loading = ref(true);
let status = ref('nomore')
onLoad(() => {
setTimeout(() => { loading.value = false; }, 1500);
})
// 处理标签切换
const handleTabChange = (e) => {
currentTab.value = e.index;
};
</script>
<style scoped lang="scss">
/* 消息列表骨架 */
.skeleton-list {
padding: 30rpx;
height: calc(100vh - 220rpx);
overflow: hidden;
}
.skeleton-message-item {
display: flex;
align-items: flex-start;
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-message-icon {
position: relative;
margin-right: 24rpx;
}
.skeleton-icon-container {
width: 80rpx;
height: 80rpx;
background-color: #e0e0e0;
border-radius: 16rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-unread-dot {
position: absolute;
top: -8rpx;
right: -8rpx;
width: 24rpx;
height: 24rpx;
background-color: #e0e0e0;
border-radius: 50%;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-message-content {
flex: 1;
}
.skeleton-message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.skeleton-message-title {
width: 200rpx;
height: 44rpx;
background-color: #e0e0e0;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-message-time {
width: 100rpx;
height: 32rpx;
background-color: #e0e0e0;
border-radius: 6rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-message-desc {
display: flex;
flex-direction: column;
gap: 10rpx;
}
.skeleton-desc-line {
height: 40rpx;
background-color: #e0e0e0;
border-radius: 8rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-desc-line.short {
width: 80%;
}
.skeleton-load-more {
height: 60rpx;
background-color: #e0e0e0;
border-radius: 30rpx;
margin-top: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏加载动画 */
@keyframes skeleton-loading {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
/* 设置延迟,让骨架屏各部分加载动画错开 */
.skeleton-tab:nth-child(1) {
animation-delay: 0s;
}
.skeleton-tab:nth-child(2) {
animation-delay: 0.1s;
}
.skeleton-tab:nth-child(3) {
animation-delay: 0.2s;
}
.skeleton-tab:nth-child(4) {
animation-delay: 0.3s;
}
.skeleton-message-item:nth-child(1) {
animation-delay: 0s;
}
.skeleton-message-item:nth-child(2) {
animation-delay: 0.15s;
}
.skeleton-message-item:nth-child(3) {
animation-delay: 0.3s;
}
.skeleton-message-item:nth-child(4) {
animation-delay: 0.45s;
}
// end
.message-center-container {
background-color: #f5f5f5;
}
.read-all-btn {
position: absolute;
top: 88rpx;
right: 30rpx;
font-size: 26rpx;
color: #666666;
z-index: 10;
}
.message-list {
padding: 30rpx;
height: calc(100vh - 220rpx);
}
.message-item {
display: flex;
align-items: flex-start;
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 24rpx;
transition: transform 0.2s;
&:active {
transform: scale(0.98);
}
}
.message-icon {
position: relative;
margin-right: 24rpx;
}
.icon-container {
width: 80rpx;
height: 80rpx;
background-color: #ffffff;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
}
.unread-dot {
position: absolute;
top: -8rpx;
right: -8rpx;
width: 24rpx;
height: 24rpx;
background-color: #FF4444;
border-radius: 50%;
border: 2rpx solid #ffffff;
}
.message-content {
flex: 1;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.message-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
line-height: 44rpx;
}
.message-time {
font-size: 24rpx;
color: #999999;
}
.message-desc {
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
word-break: break-all;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 30rpx;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
</style>

View File

@@ -0,0 +1,246 @@
<template>
<view v-if="isLoading" class="skeleton-container" style="padding: 10rpx 30rpx">
<!-- 骨架屏记录项 -->
<view class="skeleton-record-item" v-for="i in 3" :key="i">
<!-- 标题骨架 -->
<view class="skeleton-title"></view>
<!-- 金额区域骨架 -->
<view class="skeleton-amount-section">
<view class="skeleton-amount-line"></view>
<view class="skeleton-amount-value"></view>
</view>
<!-- 信息行骨架 -->
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
<view class="skeleton-info-row">
<view class="skeleton-info-label"></view>
<view class="skeleton-info-value"></view>
</view>
</view>
<!-- 加载更多骨架 -->
<view class="skeleton-loadmore"></view>
</view>
<view v-else style="padding: 10rpx 30rpx;">
<view class="" v-for="(item,index) in withdrowList" :key="index"
style="margin-top: 20rpx; gap: 20rpx; background-color: #fff; border-radius: 20rpx; padding: 30rpx; ">
<view class="" style=" display: flex; align-items: center; justify-content: space-between; font-weight: bold; border-bottom: 1rpx solid #f6f6f6; padding-bottom: 15rpx;">
余额提现-{{ item.payway}}
<view class="" :style="{ 'color': item.status==0?'#1890FF':(item.status==1?'#52C41A':'#FF4D4F') }" >
{{ item.status==0?'待审核':(item.status==1?'已通过':'已拒绝') }}
</view>
</view>
<view class=""
style="width: 100%; height: 200rpx; display: flex;flex-direction: column; justify-content: center; align-items: center; ">
<view class="" style="">
提现金额 {{ item.amount}} 元
</view>
<view class="" style="font-size: 32rpx; font-weight: bold; margin-top: 10rpx; ">
实际到账 {{item.withAmount}} 元
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; ">
<view class="" style="width: 120rpx;">
收款人
</view>
<view class="" style="">
{{ item.name }}
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; margin-top: 10rpx; ">
<view class="" style="width: 120rpx;">
提现账号
</view>
<view class="" style="">
{{ item.account }}
</view>
</view>
<view class="" style="display: flex; align-items: center; gap: 30rpx; margin-top: 10rpx;">
<view class="" style="width: 120rpx;">
提现时间
</view>
<view class="" style="">
{{ Service.formatDate(item.addTime,1) }}
</view>
</view>
<view v-if="item.reply && item.status==2" class="" style="display: flex; align-items: center; gap: 30rpx; margin-top: 10rpx;">
<view class="" style="width: 120rpx;">
拒绝通知
</view>
<view class="" style="">
{{ item.reply }}
</view>
</view>
</view>
<up-loadmore :status="status" />
</view>
</template>
<script setup lang="ts">
import { onShow, onLoad, onReachBottom } from "@dcloudio/uni-app";
import { Service } from '@/Service/Service';
import { ref } from "vue";
import { CNRiderOrderService } from '@/Service/CN/CNRiderOrderService'
let isLoading = ref(true)
let withdrowList = ref<Array<any>>([])
let status = ref('nomore')
let page = ref(1)
onLoad(() => {
getData()
});
onShow(() => {
});
onReachBottom(() => {
getList()
})
const getData = () => {
status.value = 'loadmore'
page.value = 1
withdrowList.value = []
getList()
}
//获取订单
const getList = () => {
if (status.value == 'nomore' || status.value == 'loading') {
return
}
status.value == 'loadmore'
CNRiderOrderService.GetRiderWithList(page.value).then(res => {
isLoading.value = false
if (res.data) {
withdrowList.value = [...withdrowList.value, ...res.data.list]
status.value = res.data.list == 10 ? 'loadmore' : 'nomore'
page.value++
}
})
}
</script>
<style lang="scss">
.icon-placeholder {
width: 70rpx;
height: 70rpx;
background-color: #E6F7FF;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
/* 骨架屏样式 */
.skeleton-record-item {
margin-top: 20rpx;
background-color: #fff;
border-radius: 20rpx;
padding: 30rpx;
gap: 20rpx;
}
.skeleton-title {
width: 200rpx;
height: 32rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-bottom: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-amount-section {
width: 100%;
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-bottom: 20rpx;
}
.skeleton-amount-line {
width: 180rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-bottom: 10rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-amount-value {
width: 250rpx;
height: 40rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-info-row {
display: flex;
align-items: center;
gap: 30rpx;
margin-top: 10rpx;
}
.skeleton-info-label {
width: 120rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-info-value {
width: 300rpx;
height: 28rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
.skeleton-loadmore {
width: 100%;
height: 80rpx;
background: linear-gradient(90deg, #f0f0f0 25%, #e6e6e6 50%, #f0f0f0 75%);
background-size: 200% 100%;
border-radius: 4rpx;
margin-top: 20rpx;
animation: skeleton-loading 1.5s infinite ease-in-out;
}
/* 骨架屏动画 */
@keyframes skeleton-loading {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More