This commit is contained in:
shuhongfan
2023-09-04 16:40:17 +08:00
commit cf5ac25c14
8267 changed files with 1305066 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<!-- 基本信息 - 已完成详情在途详情待提货详情 -->
<template>
<view class="">
<view class="baseInfo">
<view class="addrCont">
<view class="startAddr">{{itemData.startAddress}}</view>
<view class="endAddr">{{itemData.endAddress}}</view>
</view>
<view class="carInfo">
<view class="line"> <text>任务编号</text> <text class="ritEl">{{itemData.transportTaskId}}</text> </view>
<view class="line"> <text>联系人</text> <text class="ritEl">{{itemData.startHandover}}</text> </view>
<view class="line">
<text>联系电话</text>
<view class="phoneCont">
<text class="ritEl">{{itemData.startHandoverPhone}}</text>
<image @click="callPhone(itemData.startHandoverPhone)" class="phone" src="../../../static/sj_phone.png" mode=""></image>
</view>
</view>
<view class="line"> <text>提货时间</text> <text class="ritEl">{{itemData.planDepartureTime}}</text> </view>
<view class="line"> <text>预计送达时间</text> <text class="ritEl">{{itemData.planArrivalTime}}</text> </view>
</view>
</view>
</view>
</template>
<script setup >
// 获取父组件值、方法
const props = defineProps({
itemData: {
type: Object,
default: () => {}
}
});
// 拨打电话
const callPhone = (phone) => {
uni.makePhoneCall({
phoneNumber: phone
});
}
</script>
<style src="../index.scss" lang="scss"></style>
<style lang="scss">
.phoneCont{
display: flex;
align-items: center;
.phone{
position: relative;
padding-left: 10rpx;
top: -4rpx;
width: 48rpx;
height: 48rpx;
}
}
</style>

View File

@@ -0,0 +1,190 @@
<!-- 任务首页-包含待提货在途已完成 -->
<template>
<view class="">
<scroll-view scroll-x="true" class="tabScroll" :scroll-into-view="scrollinto" :scroll-with-animation="true">
<view v-for="(item, index) in tabBars" :key="index" :id="'tab' + index" class="scroll-row-item" @click="changeTab(index)">
<view :class="tabIndex == index ? 'scroll-row-item-act' : ''">
<text class="line"></text>
{{ item }}
</view>
</view>
</scroll-view>
<!-- 已完成页面 搜索框 - start -->
<view class="searchCont" v-if="tabIndex == 2">
<SearchInput inputKey="orderId" @searchHandle="searchHandle" ></SearchInput>
<view class="timeSearch">
<uni-datetime-picker v-model="range" type="daterange" @maskClick="maskClick" rangeSeparator="至" />
<view v-show="!isSearch" class="searchBut" @click="searchHandle('time')">
<text class="button min" >筛选</text>
</view>
<view v-show="isSearch" class="searchBut">
<text class="button buttonDis1 min">筛选</text>
</view>
</view>
</view>
<!-- 已完成页面 搜索框 - end -->
<!-- 滑块内容 对应的是顶部选项卡的切换 :current="tabIndex" 设置的是y方向上可以滚动-->
<view class="container">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true" class="swiperH" :class="{finshSwiperH: tabIndex == 2}" @scrolltolower="scrolltoupperHandle" :lower-threshold="10">
<view class="marg" v-if="itemData.length > 0">
<!-- 通用卡片组件 - 待提货在途 -->
<Card v-for="(item, index) in itemData" :data="item" :src="filterUrl(item)" :key="item.id" :type="tabIndex" />
</view>
<!-- 无数据显示 -->
<view v-if="itemData.length === 0 && !loading">
<EmptyPage :emptyInfo="emptyInfo" />
</view>
<!-- end -->
<!-- 下拉加载更多Lodding -->
<view v-if="loading">
<uni-load-more :status="moreStatus" />
</view>
<!-- end -->
</scroll-view>
</view>
</view>
</template>
<script setup >
import { ref, reactive, onMounted, watchEffect, provide } from 'vue';
import { useStore } from 'vuex';
// 组件
import Card from '@/components/Card/index.vue'
// 取件信息
import EmptyPage from '@/components/EmptyPage/index.vue';
// searchInput
import SearchInput from '@/components/SearchInput/index.vue';
// 获取父组件值、方法
const props = defineProps({
itemData: {
type: Array,
default: () => []
},
moreStatus:{
type: String,
default: 'loading'
},
loading:{
type: Boolean,
default: false
}
});
// ------定义变量------
const scrollinto = ref('tab0'); //tab切换
const store = useStore();
const tabIndex = ref(store.state.taskStatus); //当前tab
const scrollH = ref(0); //滚动高度
const emptyInfo = ref('未找到相关任务');
const tabBars = reactive(['待提货', '在途', '已完成']);
const emit = defineEmits(['setTabIndex','searchSubmit']);
const page = ref();
const pageSize = ref();
const taskId = ref(''); // 任务ID
const isSearch = ref(true) // 搜索是否可点
const range = ref() // 日期选择
const orderId = ref('') // 搜索
// 将对应Id 做provide处理 方便后面使用
provide('taskId', taskId)
// ------生命周期------
onMounted(() => {
// 获取屏幕信息
uni.getSystemInfo({
success: function(res) {
// 获取元素信息
let info = uni.createSelectorQuery().select('.swiperH');
info
.boundingClientRect(function(data) {
//data - 各种参数
scrollH.value = data.height + 140;
})
.exec();
}
});
});
// 监听日期变更 调试是否可筛选状态
watchEffect(() => {
if (range.value){
isSearch.value = false
}
})
// ------定义方法------
// 卡片点击去往对应详情的url 处理
const filterUrl = (item) => {
let src = ''
if (tabIndex.value == 0){
src = `/pages/index/details?id=${item.id}`
} else {
switch (Number(item.status)){
case 1: // 待提货
src = `/pages/index/details?id=${item.id}`
break;
case 2: // 在途
src = `/pages/index/detailsRoad?id=${item.id}`
break;
case 4: // 已交付
src = `/pages/index/refister?id=${item.transportTaskId}&time=${item.actualDepartureTime}`
break;
case 6: // 已完成(已等级)- 交付之后需要回车等记
src = `/pages/index/detailsSuccess?id=${item.id}`
break;
default: // 3 改派 、5 作废
src = ``
break;
}
}
return src
}
// 搜索按钮
function searchHandle(type){
const params = type == 'time' ? range : type
emit('searchSubmit', params)
}
// 上拉刷新
function scrolltoupperHandle(){
emit('setTabIndex', tabIndex.value)
}
// tab选项卡切换轮播
const changeTab = index => {
// 点击的还是当前数据的时候直接return
if (tabIndex.value == index) {
return;
}
tabIndex.value = index;
emit('setTabIndex', index)
// 滑动
// scrollinto.value = 'tab' + index;
};
//
function maskClick(time){
console.log(time)
}
</script>
<style src="../index.scss" lang="scss"></style>
<style lang="scss">
.searchCont{
background-color: #fff;
padding: 0rpx 30rpx 30rpx 30rpx;
.searchBut{
width: 180rpx;
margin-left: 40rpx;
}
.timeSearch{
display: flex;
padding-top: 20rpx;
}
::v-deep .uniui-clear::before{
display: none;
}
::v-deep .uni-input-wrapper{
background-color: #f4f4f4;
}
}
</style>

View File

@@ -0,0 +1,55 @@
<!-- 订单列表 - 已完成待提货在途的详情页面中使用 -->
<template>
<view class="orderCont">
<view class="search">
<uni-icons class="searchIcon" @click="search()" type="search"></uni-icons>
<input type="text" class="searchInput" @input="onKeyInput" @confirm="search" confirm-type="search" placeholder="请输入运单号" />
</view>
<view class="items">
<view class="item" v-for="item in itemData">
<text class="odd">{{item.id}}</text>
<text class="piece">{{item.count}} </text>
<text class="weight">{{item.totalWeight}} kg</text>
</view>
</view>
</view>
</template>
<script setup >
import { ref } from 'vue'
// 获取父组件值、方法
const props = defineProps({
itemData: {
type: Object,
default: () => {}
},
searchHandle: {
type: Function(),
default: () => {}
}
});
const emit = defineEmits(['searchHandle']);
// 订单号
const orderId = ref('')
// 搜索按钮
const search = ()=>{
if(orderId.value == ''){
uni.showToast({
title: '请输入运单号',
duration: 1000,
icon: 'none'
});
return
}
emit('searchHandle', orderId.value)
}
// 输入值记录到orderId
const onKeyInput = (event) => {
orderId.value = event.detail.value
}
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,42 @@
<!-- 运算路线 -->
<template>
<view class="routeItem" >
<view class="routeLine">
<view class="routePoint">
<view class="tit"> <text>{{itemData.startProvince}}</text> </view>
<view class=""> <text>{{itemData.startCity}}</text> </view>
</view>
<view class="route">
<view class="line" style="">
<image class="LineImg" src="../../../static/sj_route_line.png" mode=""></image>
</view>
</view>
<view class="routePoint">
<view class="tit"> <text>{{itemData.endProvince}}</text> </view>
<view class=""> <text>{{itemData.endCity}}</text> </view>
</view>
</view>
<view class="navigat" v-if="type == 'navigation'">
<image class="naviIcon" src="../../../static/sj_navigation.png" mode=""></image>
<view class="" @click="() => openMap(itemData.startAddress,itemData.endAddress, '开始导航')">
<text>开始导航</text>
</view>
</view>
</view>
</template>
<script setup >
import { openMap } from '@/utils/index.js'
// 获取父组件值、方法
const props = defineProps({
type: {
type: String,
default: 'navigation'
},
itemData: {
type: Object,
default: () => {}
}
});
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,71 @@
<!-- 单选按钮组件 - 回车登记页面使用 -->
<template>
<view class="refisterCards">
<view class="title">
{{data.title}}
</view>
<view class="items">
<view class="item" v-for="(item, index) in data.data" :key="index" @click="activeHandel(item, index)">
<view :class="{buttonDel: true, buttonAct: actVal == item}">
<text>{{item}}</text>
</view>
</view>
<!-- 布局填充用 -->
<view v-if="data.data.length % 3 == 1 || data.data.length % 3 == 2" class="item"></view>
<view v-if="data.data.length % 3 == 1" class="item"></view>
</view>
</view>
</template>
<script setup >
import {ref} from 'vue'
// 获取父组件值、方法
const props = defineProps({
// 展示源数据
data: {
type: Object,
default: () => {}
},
// 设置默认值
value: {
type: String,
default: ''
},
// 选择数据的key
choiceKey: {
type: String,
default: ''
}
});
const emit = defineEmits(['choiceHandel'])
// 标记选择项
let actVal = ref(props.value)
// 点击选中
function activeHandel(val, index){
actVal.value = val
const param = {key: props.choiceKey, value:index+1, keyInt:val}
emit('choiceHandel', param)
}
</script>
<style lang="scss">
.refisterCards{
.title{
color: #2A2929;
margin-bottom: 30rpx;
}
.items{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item{
width: 29.5%;
padding-bottom: 30rpx;
}
}
}
</style>

View File

@@ -0,0 +1,191 @@
<!-- 延迟交货 -->
<template>
<!-- end -->
<view class="pageBox">
<!-- title -->
<DetailsNav title="延迟提货"></DetailsNav>
<!-- 取件状态列表 -->
<view class="container">
<view class="delayedCont">
<view class="lineBoder">
<text>原定时间</text>
<text class="label">{{ orgTime }}</text>
</view>
<view class="lineBoder">
<text>延迟时间</text>
<picker
mode="time"
:value="time"
start="09:01"
end="21:01"
@change="bindTimeChange"
>
<view class="uni-input timeInfo">
<text>{{ time }}</text>
<image
class="iconImg"
src="../../static/sj_open_rit.png"
mode=""
></image>
</view>
</picker>
</view>
<view class="">
<textarea
class="textInput"
v-model="formData.delayReason"
placeholder-style="color:#818181"
placeholder="请输入延迟提货原因"
/>
</view>
<view class="butCont">
<text
class="button buttonDis1"
v-show="formData.delayReason == '' || time == '不可超过两小时'"
>提交</text
>
<text
class="button"
v-show="formData.delayReason != '' && time != '不可超过两小时'"
@click="formActioin()"
>提交</text
>
</view>
</view>
</view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
// 导入接口
import { PutDelay } from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue';
// 主体部分
// ------定义变量------
const orgTime = ref(); // 原定提货时间
const time = ref('不可超过两小时');
// 提交数据
const formData = ref({
id: '',
delayTime: '',
delayReason: '',
});
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
orgTime.value = currentPage.time;
formData.value.id = currentPage.id;
});
// ------定义方法------
const bindTimeChange = (e) => {
time.value = e.detail.value;
};
// 延迟提货提交
const formActioin = async () => {
const data = formData.value;
// 延迟时间必选
if (time.value == '不可超过两小时') {
uni.showToast({
title: '请选择延迟时间!',
duration: 1000,
icon: 'none',
});
return;
}
// 请填写延迟提货原因
if (data.delayReason.trim() == '') {
uni.showToast({
title: '请填写延迟提货原因!',
duration: 1000,
icon: 'none',
});
return;
}
// 网络慢的时候添加按钮loading
let times = setTimeout(() => {
uni.showLoading({
title: 'loading',
mask:true
});
}, 500);
// 原始时间
const lastTime = orgTime.value;
// 替换处理 延迟的实际时间
data.delayTime = lastTime.replace(/(\d+){2}(:\d+){1}/, time.value);
// 延迟提货
await PutDelay(formData.value)
.then((res) => {
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times);
uni.showToast({
title: '延迟提货提交成功',
duration: 1000,
icon: 'none',
});
setTimeout(() => {
uni.navigateTo({
url: '/pages/index/index',
});
}, 500);
} else {
uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
</script>
<style lang="scss">
@import url('@/styles/theme.scss');
.delayedCont {
background-color: var(--neutral-color-white);
margin: 30rpx;
padding: 30rpx 30rpx 40rpx 30rpx;
border-radius: 20rpx;
font-size: var(--font-size-14);
.lineBoder {
display: flex;
justify-content: space-between;
padding: 40rpx 0;
border-bottom: solid 1px var(--neutral-color-cancel);
.label {
color: var(--neutral-color-font);
}
}
.textInput {
font-size: var(--font-size-14);
background-color: var(--neutral-color-cancel);
width: 100%;
border-radius: 20rpx;
margin-top: 40rpx;
padding: 30rpx;
box-sizing: border-box;
}
.butCont {
width: 60%;
margin: 40rpx auto 0 auto;
}
.timeInfo {
display: flex;
line-height: 48rpx;
.iconImg {
width: 48rpx;
height: 48rpx;
}
}
}
</style>

View File

@@ -0,0 +1,338 @@
<!-- 待提货 - 详情 -->
<template>
<!-- 详情 -->
<view class="details">
<DetailsNav title="任务详情"></DetailsNav>
<!-- 取件状态列表 -->
<view class="container">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true" class="swiperH">
<view class="cont" v-if="Object.keys(itemData).length > 0">
<!-- 通用卡片组件 - 待提货 - 带开关 -->
<CardCont title="基本信息" :open="true">
<DetailsBaseInfo :itemData="itemData"></DetailsBaseInfo>
</CardCont>
<CardCont title="车辆司机信息">
<view class="carInfo">
<view class="line">
<text class="tit">车牌号</text>
<text class="ritEl">{{ itemData.licensePlate }}</text>
</view>
<view class="line">
<text class="tit">司机姓名</text>
<text class="ritEl">{{ itemData.driverName }}</text>
</view>
</view>
</CardCont>
<CardCont title="运输路线">
<RouteCont :itemData="itemData" type="route"></RouteCont>
</CardCont>
<CardCont title="物品信息" :label="`共计: ${amount}单`">
<OrderCont
:itemData="orders"
@searchHandle="searchHandle"
></OrderCont>
</CardCont>
<CardCont title="提货信息">
<view class="upPicCont">
<view class="title">请拍照上传回单凭证</view>
<uni-file-picker
v-model="cargoPickUpPicture"
fileMediatype="image"
mode="grid"
limit="3"
@select="selectA"
/>
</view>
<view class="upPicCont">
<view class="title">请拍照上传货品照片</view>
<uni-file-picker
v-model="cargoPicture"
fileMediatype="image"
mode="grid"
limit="3"
@select="selectB"
/>
</view>
</CardCont>
</view>
<!-- 无数据显示 -->
<view v-if="Object.keys(itemData).length === 0">
<EmptyPage emptyInfo="暂无数据!" />
</view>
<!-- end -->
</scroll-view>
</view>
<!-- end -->
</view>
<!-- footer -->
<view class="footCont">
<view class="footButCan">
<text class="buttonCancel" @click="delayedHandle()">延迟提货</text>
</view>
<view class="footBut">
<text v-show="isTake" class="button" @click="takeGoods()">提货</text>
<text v-show="!isTake" class="buttonDis1">提货</text>
</view>
</view>
<!-- end -->
</template>
<script setup>
import {
ref,
reactive,
onMounted,
onUpdated,
watch,
watchEffect,
computed,
inject,
} from 'vue';
import { useStore } from 'vuex';
import { positionUploadHandle } from '@/utils/index.js';
// 导入接口
import {
GetTaskDetails,
GetTaskDetailsOrders,
TakeDelivery,
} from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue';
import EmptyPage from '@/components/EmptyPage/index.vue';
import DetailsBaseInfo from './components/DetailsBaseInfo.vue';
import OrderCont from './components/OrderCont.vue';
import RouteCont from './components/RouteCont.vue';
// 主体部分
import CardCont from '@/components/CardCont/index.vue';
// 接口调用
import { upload } from '@/pages/api/index.js';
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const itemData = ref([]);
const orders = ref([]); // 货物信息列表
const amount = ref(0); // 货物信息总数
const id = ref(''); // 任务Id
const cargoPickUpPicture = ref([]); // 提货凭证
const cargoPicture = ref([]); // 货物照片
const isTake = ref(true); // 是否可提货
const taskId = ref('');
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
id.value = currentPage.id;
getDetailsInfo();
});
// 监听是否可以提货
watchEffect(
[
cargoPickUpPicture,
() => {
isTake.value = cargoPickUpPicture.length > 0 && cargoPicture.length > 0;
},
],
[
cargoPicture,
() => {
isTake.value = cargoPickUpPicture.length > 0 && cargoPicture.length > 0;
},
]
);
// ------定义方法------
// 获取任务详情的数据
const getDetailsInfo = async () => {
// 获取任务详情的数据
await GetTaskDetails(id.value)
.then((res) => {
const { data } = res;
if (res.code === 200) {
itemData.value = data;
getOrders(data.transportTaskId);
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
// 获取物品订单信息
const getOrders = async (orderId, transportOrderId = '') => {
const params = {
transportOrderId,
taskId: orderId,
page: 1,
pageSize: 100,
};
await GetTaskDetailsOrders(params)
.then((res) => {
if (res.code === 200) {
amount.value = res.data.counts ? res.data.counts : 0;
orders.value = res.data.items;
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
// 物品信息搜索
function searchHandle(transportOrderId) {
getOrders(itemData.value.transportTaskId, transportOrderId);
}
// 延迟提货
function delayedHandle() {
const data = itemData.value;
uni.navigateTo({
url: `/pages/index/delayed?id=${data.id}&time=${data.planDepartureTime}`,
});
}
// 提货
async function takeGoods() {
const cargoPickUpPictureStr = cargoPickUpPicture.value
.map((n) => n.path)
.join();
const cargoPictureStr = cargoPicture.value.map((n) => n.path).join();
const params = {
id: id.value,
cargoPickUpPicture: cargoPickUpPictureStr,
cargoPicture: cargoPictureStr,
};
// 网络慢的时候添加按钮loading
let times = setTimeout(() => {
uni.showLoading({
title: 'loading',
mask:true
});
}, 500);
await TakeDelivery(params)
.then((res) => {
if (res.code === 200) {
// 提货之后上报位置
positionUploadHandle(true);
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times);
uni.showToast({
title: '提货完成',
duration: 1000,
icon: 'none',
});
uni.redirectTo({
url: '/pages/index/index',
});
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
}
// 文件上传
async function uploadHande(e, type) {
await upload(e)
.then((res) => {
if (res.code === 200) {
if (res.data) {
isTake.value = true;
const name = res.data.split('/')[res.data.split('/').length - 1]; // .at(-1) 新语法APP不支持
let data = {
url: res.data,
name,
extName: name.split('.')[name.split('.').length - 1],
};
if (type == 'cargoPickUpPicture') {
cargoPickUpPicture.value = [...cargoPickUpPicture.value, data];
} else {
cargoPicture.value = [...cargoPicture.value, data];
}
}
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {
uni.showToast({
title: '图片上传失败!请联系管理员',
duration: 1000,
icon: 'none',
});
});
}
// 文件选择并上传 - 回单凭证上传
async function selectA(e) {
cargoPickUpPicture.value = [];
const tempFiles = e.tempFiles[0];
if (
tempFiles.size < 1024 * 5 * 1024 &&
(tempFiles.extname == 'png' ||
tempFiles.extname == 'jpg' ||
tempFiles.extname == 'jpeg' ||
tempFiles.extname == 'gif')
) {
uploadHande(e, 'cargoPickUpPicture');
} else {
uni.showToast({
title: '上传图片大小不能超过5M格式需为jpg、png、gif',
duration: 2000,
icon: 'none',
});
}
}
// 文件选择并上传 - 货品照片上传
async function selectB(e) {
cargoPicture.value = [];
const tempFiles = e.tempFiles[0];
if (
tempFiles.size < 1024 * 5 * 1024 &&
(tempFiles.extname == 'png' ||
tempFiles.extname == 'jpg' ||
tempFiles.extname == 'jpeg' ||
tempFiles.extname == 'gif')
) {
uploadHande(e, 'cargoPicture');
} else {
uni.showToast({
title: '上传图片大小不能超过5M格式需为jpg、png、gif',
duration: 2000,
icon: 'none',
});
}
}
</script>
<style src="./index.scss" lang="scss"></style>
<style lang="scss" scoped>
.details {
height: calc(100vh - 120rpx);
.swiperH {
height: calc(100vh - 210rpx);
padding-bottom: 26rpx;
}
}
</style>

View File

@@ -0,0 +1,378 @@
<!-- 在途 - 详情 -->
<template>
<!-- 详情 -->
<view class="details">
<DetailsNav title="任务详情"></DetailsNav>
<!-- 取件状态列表 -->
<view class="container">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true" class="swiperH">
<view class="cont" v-if="Object.keys(itemData).length > 0">
<!-- 通用卡片组件 - 在途 - 带开关 -->
<CardCont title="基本信息" :open="true">
<DetailsBaseInfo :itemData="itemData"></DetailsBaseInfo>
</CardCont>
<!-- 司机信息 -->
<CardCont title="车辆司机信息">
<view class="carInfo">
<view class="line">
<text class="tit">车牌号</text>
<text class="ritEl">{{ itemData.licensePlate }}</text>
</view>
<view class="line">
<text class="tit">司机姓名</text>
<text class="ritEl">{{ itemData.driverName }}</text>
</view>
</view>
</CardCont>
<CardCont title="运输路线">
<RouteCont :itemData="itemData"></RouteCont>
</CardCont>
<!-- 物品信息 -->
<CardCont title="物品信息" :label="`共计: ${amount}单`">
<OrderCont
:itemData="orders"
@searchHandle="searchHandle"
></OrderCont>
</CardCont>
<!-- 异常信息 -->
<CardCont title="异常信息">
<view v-for="(item, index) in itemData.exception" :key="index">
<view class="delay">
<view class="info">
<view class="line">
<text>上报时间 </text
><text class="desc"> {{ item.exceptionTime }} </text>
</view>
<view class="line">
<text>异常类型 </text
><text class="desc"> {{ item.exceptionType }} </text>
</view>
<view class="line">
<text>继续运输 </text
><text class="desc"> {{ item.processResults }} </text>
</view>
</view>
<image
@click="ExceptionHandle('?id=' + item.exceptionId)"
class="goInfoIcon"
src="../../static/sj_open_rit.png"
mode=""
></image>
</view>
</view>
</CardCont>
<!-- 交货信息 -->
<CardCont title="交货信息">
<view class="upPicCont">
<view class="title">请拍照上传回单凭证</view>
<uni-file-picker
v-model="cargoPickUpPicture"
fileMediatype="image"
mode="grid"
limit="3"
@select="selectA"
/>
</view>
<view class="upPicCont">
<view class="title">请拍照上传货品照片</view>
<uni-file-picker
v-model="cargoPicture"
fileMediatype="image"
mode="grid"
limit="3"
@select="selectB"
/>
</view>
</CardCont>
</view>
<!-- 无数据显示 -->
<view v-if="Object.keys(itemData).length === 0">
<EmptyPage emptyInfo="暂无数据!" />
</view>
<!-- end -->
</scroll-view>
</view>
<!-- end -->
</view>
<!-- footer -->
<view class="footCont">
<view class="footButCan">
<text class="buttonCancel" @click="ExceptionHandle()">上报异常</text>
</view>
<view class="footBut">
<text v-show="isTake" class="button" @click="takeGoods()">交付</text>
<text v-show="!isTake" class="buttonDis1">交付</text>
</view>
</view>
<!-- end -->
</template>
<script>
export default {
mounted() {
// #ifdef APP-NVUE
const eventChannel = this.$scope.eventChannel; // 兼容APP-NVUE
// #endif
// #ifndef APP-NVUE
const eventChannel = this.getOpenerEventChannel();
// #endif
eventChannel.emit('acceptDataFromOpenedPage', {
data: 'data from test page',
});
eventChannel.emit('someEvent', {
data: 'data from test page for someEvent',
});
// 监听acceptDataFromOpenerPage事件获取上一页面通过eventChannel传送到当前页面的数据
eventChannel.on('acceptDataFromOpenerPage');
},
};
</script>
<script setup>
import { ref, reactive, onMounted, watchEffect } from 'vue';
import { useStore } from 'vuex';
import { positionUploadHandle } from '@/utils/index.js';
// 导入接口
import {
GetTaskDetails,
GetTaskDetailsOrders,
Deliver,
} from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue';
import EmptyPage from '@/components/EmptyPage/index.vue';
import DetailsBaseInfo from './components/DetailsBaseInfo.vue';
import OrderCont from './components/OrderCont.vue';
import RouteCont from './components/RouteCont.vue';
// 接口调用
import { upload } from '@/pages/api/index.js';
// 主体部分
import CardCont from '@/components/CardCont/index.vue';
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const itemData = ref([]);
const orders = ref([]); // 货物信息列表
const amount = ref(0); // 货物信息总数
const id = ref(''); // 任务Id
const cargoPickUpPicture = ref([]); // 提货凭证
const cargoPicture = ref([]); // 货物照片
const isTake = ref(false);
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
id.value = currentPage.id;
getDetailsInfo();
getOrders();
});
// 监听是否可以提货
watchEffect(
[
cargoPickUpPicture,
() => {
isTake.value = cargoPickUpPicture.length > 0 && cargoPicture.length > 0;
},
],
[
cargoPicture,
() => {
isTake.value = cargoPickUpPicture.length > 0 && cargoPicture.length > 0;
},
]
);
// ------定义方法------
// 获取任务详情的数据
const getDetailsInfo = async () => {
// 获取任务详情的数据
await GetTaskDetails(id.value)
.then((res) => {
const { data } = res;
if (res.code === 200) {
itemData.value = data;
getOrders(data.transportTaskId);
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
// 获取物品订单信息
const getOrders = async (orderId, transportOrderId = '') => {
const params = {
transportOrderId,
taskId: orderId,
page: 1,
pageSize: 100,
};
await GetTaskDetailsOrders(params)
.then((res) => {
if (res.code === 200) {
amount.value = res.data.counts ? res.data.counts : 0;
orders.value = res.data.items;
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
// 物品信息搜索
function searchHandle(transportOrderId) {
getOrders(itemData.value.transportTaskId, transportOrderId);
}
// 上报异常 、异常详情
function ExceptionHandle(src) {
uni.showToast({
title: '上报异常暂时接口还在实现中!',
duration: 1000,
icon: 'none',
});
return false;
const url = src ? `/pages/index/exception${src}` : '/pages/index/exception';
uni.navigateTo({ url });
}
// 交付
async function takeGoods() {
const cargoPickUpPictureStr = cargoPickUpPicture.value
.map((n) => n.path)
.join();
const cargoPictureStr = cargoPicture.value.map((n) => n.path).join();
const params = {
id: id.value,
transportCertificate: cargoPickUpPictureStr,
deliverPicture: cargoPictureStr,
};
// 网络慢的时候添加按钮loading
let times = setTimeout(() => {
uni.showLoading({
title: 'loading',
mask:true
});
}, 500);
await Deliver(params)
.then((res) => {
if (res.code === 200) {
// 交付之后 上报位置
positionUploadHandle(true);
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times);
uni.showToast({
title: '提货完成',
duration: 1000,
icon: 'none',
});
uni.redirectTo({
url: '/pages/index/index',
});
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
}
// 图片上传
const imageValue = ref([]);
const upImg = ref(null);
// 文件上传
async function uploadHande(e, type) {
await upload(e)
.then((res) => {
if (res.code === 200) {
if (res.data) {
isTake.value = true;
const name = res.data.split('/')[res.data.split('/').length - 1]; // .at(-1) 新语法APP不支持
let data = {
url: res.data,
name,
extName: name.split('.')[name.split('.').length - 1],
};
if (type == 'cargoPickUpPicture') {
cargoPickUpPicture.value = [...cargoPickUpPicture.value, data];
} else {
cargoPicture.value = [...cargoPicture.value, data];
}
}
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {
uni.showToast({
title: '图片上传失败!请联系管理员',
duration: 1000,
icon: 'none',
});
});
}
// 文件选择并上传 - 回单凭证上传
async function selectA(e) {
cargoPickUpPicture.value = [];
const tempFiles = e.tempFiles[0];
if (
tempFiles.size < 1024 * 5 * 1024 &&
(tempFiles.extname == 'png' ||
tempFiles.extname == 'jpg' ||
tempFiles.extname == 'jpeg' ||
tempFiles.extname == 'gif')
) {
uploadHande(e, 'cargoPickUpPicture');
} else {
uni.showToast({
title: '上传图片大小不能超过5M格式需为jpg、png、gif',
duration: 2000,
icon: 'none',
});
}
}
// 文件选择并上传 - 货品照片上传
async function selectB(e) {
cargoPicture.value = [];
const tempFiles = e.tempFiles[0];
if (
tempFiles.size < 1024 * 5 * 1024 &&
(tempFiles.extname == 'png' ||
tempFiles.extname == 'jpg' ||
tempFiles.extname == 'jpeg' ||
tempFiles.extname == 'gif')
) {
uploadHande(e, 'cargoPicture');
} else {
uni.showToast({
title: '上传图片大小不能超过5M格式需为jpg、png、gif',
duration: 2000,
icon: 'none',
});
}
}
</script>
<style src="./index.scss" lang="scss"></style>
<style lang="scss" scoped>
.details {
height: calc(100vh - 120rpx);
.swiperH {
height: calc(100vh - 240rpx);
padding-bottom: 26rpx;
}
}
</style>

View File

@@ -0,0 +1,189 @@
<!-- 已完成 - 详情 -->
<template>
<!-- 详情 -->
<view class="details">
<DetailsNav title="任务详情"></DetailsNav>
<!-- 取件状态列表 -->
<view class="container">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true" class="successSwiperH">
<view class="cont" v-if="Object.keys(itemData).length > 0">
<!-- 通用卡片组件 - 待提货 - 带开关 -->
<CardCont title="基本信息" :open="true">
<DetailsBaseInfo :itemData="itemData"></DetailsBaseInfo>
</CardCont>
<CardCont title="车辆司机信息">
<view class="carInfo">
<view class="line"> <text class="tit">车牌号</text> <text class="ritEl">{{itemData.licensePlate}}</text>
</view>
<view class="line"> <text class="tit">司机姓名</text> <text class="ritEl">{{itemData.driverName}}</text>
</view>
</view>
</CardCont>
<CardCont title="运输路线">
<RouteCont :itemData="itemData" type="route"></RouteCont>
</CardCont>
<CardCont title="物品信息" :label="`共计: ${amount}单`">
<OrderCont :itemData="orders" @searchHandle="searchHandle"></OrderCont>
</CardCont>
<CardCont title="提货凭证">
<view class="upPicCont">
<div class="tit">回单凭证</div>
<view class="upPicContImg">
<img class="image" v-for="item in itemData.cargoPickUpPicture" :src="item" alt="" srcset="">
</view>
</view>
<view class="upPicCont">
<div class="tit">提货照片</div>
<view class="upPicContImg">
<img class="image" v-for="item in itemData.cargoPicture" :src="item" alt="" srcset="">
</view>
</view>
</CardCont>
<CardCont title="交货信息">
<view class="upPicCont">
<div class="tit">回单凭证</div>
<view class="upPicContImg">
<img class="image" v-for="item in itemData.deliverPicture" :src="item" alt="" srcset="">
</view>
</view>
<view class="upPicCont">
<div class="tit">货品照片</div>
<view class="upPicContImg">
<img class="image" v-for="item in itemData.transportCertificate" :src="item" alt="" srcset="">
</view>
</view>
</CardCont>
<CardCont title="异常信息">
</CardCont>
</view>
<!-- 无数据显示 -->
<view v-if="Object.keys(itemData).length === 0">
<EmptyPage emptyInfo="暂无数据!" />
</view>
<!-- end -->
</scroll-view>
</view>
<!-- end -->
</view>
</template>
<script setup>
import {
ref,
reactive,
onMounted,
onUpdated,
watch,
watchEffect,
computed,
inject
} from 'vue';
import {
useStore
} from 'vuex';
// 导入接口
import {
GetTaskDetails,
GetTaskDetailsOrders,
TakeDelivery
} from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue'
import EmptyPage from '@/components/EmptyPage/index.vue'
import DetailsBaseInfo from './components/DetailsBaseInfo.vue'
import OrderCont from './components/OrderCont.vue'
import RouteCont from './components/RouteCont.vue'
// 主体部分
import CardCont from '@/components/CardCont/index.vue';
// 接口调用
import {
upload
} from '@/pages/api/index.js'
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const itemData = ref({});
const orders = ref([]); // 货物信息列表
const amount = ref(0); // 货物信息总数
const id = ref('') // 任务Id
const cargoPickUpPicture = ref([]) // 提货凭证
const cargoPicture = ref([]) // 货物照片
const isTake = ref(true) // 是否可提货
const taskId = ref('')
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
id.value = currentPage.id;
getDetailsInfo();
});
// ------定义方法------
// 获取任务详情的数据
const getDetailsInfo = async () => {
// 获取任务详情的数据
await GetTaskDetails(id.value)
.then(res => {
const {
data
} = res
if (res.code === 200) {
itemData.value = data
getOrders(data.transportTaskId)
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none'
});
}
})
.catch(err => {});
};
// 获取物品订单信息
const getOrders = async (orderId, transportOrderId='' ) => {
const params = {
transportOrderId,
taskId: orderId,
page: 1,
pageSize: 100
}
await GetTaskDetailsOrders(params)
.then(res => {
if (res.code === 200) {
amount.value = res.data.counts ? res.data.counts : 0
orders.value = res.data.items
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none'
});
}
})
.catch(err => {});
}
// 物品信息搜索
function searchHandle(transportOrderId) {
getOrders(itemData.value.transportTaskId, transportOrderId)
}
</script>
<style src="./index.scss" lang="scss"></style>
<style lang="scss" scoped>
.details {
.successSwiperH {
height: calc(100vh - 160rpx);
padding-bottom: 26rpx;
}
}
</style>

View File

@@ -0,0 +1,279 @@
<!-- 上报异常 -->
<template>
<!-- end -->
<view class="pageBox">
<!-- title -->
<DetailsNav :title="title"></DetailsNav>
<!-- 取件状态列表 -->
<view class="container">
<view class="delayedCont">
<view class="lineBoder">
<text>异常时间</text>
<text v-if="type == 'details'">{{ exceptionsDeta.time }}</text>
<!-- <text v-else class="label">请选择</text> -->
<uni-datetime-picker
v-else
type="datetime"
v-model="exceptionsDeta.time"
:clear-icon="false"
/>
</view>
<view class="lineBoder">
<text>上报位置</text>
<text v-if="type == 'details'">{{ exceptionsDeta.province }}</text>
<text v-else class="label">请选择</text>
</view>
<view class="lineBoder">
<text>异常类型</text>
<text v-if="type == 'details'">{{ exceptionsDeta.type }}</text>
<view v-else class="label">
<Select :options="typeOptions"></Select>
</view>
</view>
<view v-if="type == 'details'" class="lineBoder noborder">
<view class="" style="width: 100%">
<view>
<text>异常描述</text>
</view>
<view>
<text class="desc">{{ exceptionsDeta.description }}</text>
</view>
</view>
</view>
<view v-else class="lineBoder">
<view class="" style="width: 100%">
<text>异常描述</text>
<textarea
class="textInput"
v-model="formData.delayReason"
placeholder-style="color:#818181"
placeholder="请输入异常描述"
/>
</view>
</view>
</view>
<view class="delayedCont">
<view :class="{ upPicCont: true, setBottom: type == 'details' }">
<view class="title">上传图片最多6张</view>
<view v-if="type == 'details'" class="exceptionImages">
<image
class="exceptionImage"
v-for="(item, index) in exceptionsDeta.images"
:src="item"
key="index"
mode=""
></image>
</view>
<uni-file-picker
v-else
v-model="imageValue"
fileMediatype="image"
mode="grid"
@select="select"
@progress="progress"
@success="success"
@fail="fail"
/>
</view>
</view>
</view>
<!-- end -->
<view class="footCont positionBot" style="justify-content: center">
<view class="footBut" style="width: 400rpx; flex: initial">
<text class="button" @click="exceptionHandle()">提交</text>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
// 导入接口
import { Exception, ExceptionDetails } from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue';
import Select from '@/components/Select/index.vue';
// 主体部分
// ------定义变量------
const imageValue = ref([]); // 上次图片
const type = ref('add'); // 类型
const title = ref('上报异常'); // 标题
const exceptionId = ref(''); // 异常的id - 详情
const exceptionsDeta = ref({});
// 类型
const typeOptions = [
{ title: '发动机启动困难', value: '1' },
{ title: '不着车,漏油', value: '2' },
{ title: '照明失灵', value: '3' },
{ title: '有异常响动', value: '4' },
{ title: '排烟异常、温度异常', value: '5' },
{ title: '其他问题', value: '6' },
];
// 提交数据
const formData = ref({
id: '',
delayTime: '',
delayReason: '',
});
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
if (Object.keys(currentPage).length > 0) {
type.value = 'details';
title.value = '异常详情';
exceptionId.value = currentPage.exceptionId;
getDetails();
} else {
type.value = 'add';
title.value = '上报异常';
}
});
// ------定义方法------
// 上报异常
const exceptionHandle = async () => {
// 网络慢的时候添加按钮loading
let times = setTimeout(() => {
uni.showLoading({
title: 'loading',
mask:true
});
}, 500);
// 上报异常
await Exception(formData)
.then((res) => {
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times);
uni.showToast({
title: '提交成功',
duration: 1000,
icon: 'none',
});
setTimeout(() => {
uni.navigateTo({
url: '/pages/index/index',
});
}, 500);
} else {
uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
};
// 获取异常详情数据
async function getDetails() {
await ExceptionDetails({ exceptionId: exceptionId })
.then((res) => {
if (res.code === 200) {
const data = res.data;
data.images = [
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2018%2F04%2F2411191727687.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645143&t=8fed835d7336cac9b3fedb170766a678',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2017%2F03%2F26%2FB1371.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645178&t=07a6e8bcc05b75d3dd86c369c70f7274',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2017%2F03%2F26%2FB2154.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645143&t=54afc59b141abad9f7799f005e20c944',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2019%2F05%2F08211345608033.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645143&t=c8a2aa7a7bd874381fbb98bc4223e6fe',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2017%2F03%2F26%2FB1365.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645143&t=55b411cc1ad4aea58cef44fe11438f72',
'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.duoziwang.com%2F2019%2F04%2F07090912704156.jpg&refer=http%3A%2F%2Fimg.duoziwang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1658645143&t=fa6d42ffb721c66f1e42548895a64e89',
];
exceptionsDeta.value = res.data;
} else {
uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none',
});
}
})
.catch((err) => {});
}
// 获取上传状态
function select(e) {
console.log('选择文件:', e);
}
// 获取上传进度
function progress(e) {
console.log('上传进度:', e);
}
// 上传成功
function success(e) {
console.log('上传成功');
}
// 上传失败
function fail(e) {
console.log('上传失败:', e);
}
</script>
<style src="./index.scss" lang="scss"></style>
<style lang="scss">
@import url('@/styles/theme.scss');
.delayedCont {
background-color: var(--neutral-color-white);
margin: 30rpx;
padding: 30rpx 30rpx 40rpx 30rpx;
border-radius: 20rpx;
font-size: var(--font-size-14);
.lineBoder {
display: flex;
justify-content: space-between;
padding: 40rpx 0;
border-bottom: solid 1px var(--neutral-color-cancel);
.label {
color: var(--neutral-color-font);
}
}
.noborder {
border: none;
}
.desc {
display: inline-block;
padding-top: 40rpx;
}
.textInput {
font-size: var(--font-size-14);
background-color: var(--neutral-color-cancel);
width: 100%;
border-radius: 20rpx;
margin-top: 40rpx;
padding: 30rpx;
box-sizing: border-box;
}
.butCont {
width: 60%;
margin: 40rpx auto 0 auto;
}
.exceptionImages {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.exceptionImage {
width: 26vw;
height: 26vw;
margin-bottom: 3vw;
}
}
.setBottom {
margin-bottom: 0;
}
::v-deep .uni-input-placeholder {
text-align: right;
}
}
.positionBot {
position: fixed;
width: 100%;
bottom: 0;
}
</style>

View File

@@ -0,0 +1,257 @@
.container{
border-radius: 20rpx;
.swiperH{
height: calc(100vh - 270rpx);
padding-bottom: 30rpx;
box-sizing: border-box;
}
.finshSwiperH{
height: calc(100vh - 460rpx);
}
.cont{
padding:0 30rpx;
}
}
// 首页 - 通用 - 左右结构
.carInfo{
.line{
.tit{
display: inline-block;
min-width: 120rpx;
}
display: flex;
justify-content: space-between;
color: var(--neutral-color-font);
font-size: var(--font-size-14);
line-height: 60rpx;
.ritEl{
color: var(--neutral-color-main);
}
}
}
// 详情-基本信息
.baseInfo{
.addrCont{
font-weight: 400;
font-size: 28rpx;
color: var(--neutral-color-font);
letter-spacing: 0.32rpx;
padding-bottom: 40rpx;
margin-bottom: 20rpx;
border-bottom: 1px solid var(--neutral-color-segmentation);
position: relative;
&:before{
position: absolute;
left: 18rpx;
color:var(--neutral-color-white);
text-align: center;
content: '';
display: inline-block;
width: 0px;
height: 56%;
border-left: dashed 2px var(--neutral-color-border);
border-radius: 11px;
}
.startAddr{
padding-left: 66rpx;
position: relative;
margin-bottom: 20rpx;
line-height: 44rpx;
&:before{
position: absolute;
font-size: 22rpx;
left: 0;
color:var(--neutral-color-white);
text-align: center;
content: '';
display: inline-block;
width: 22px;
height: 22px;
background: var(--neutral-color-main);
border-radius: 11px;
}
}
.endAddr{
padding-left: 66rpx;
position: relative;
line-height: 44rpx;
&:before{
font-size: 22rpx;
position: absolute;
left: 0;
color: var(--neutral-color-white);
text-align: center;
content: '';
display: inline-block;
width: 22px;
height: 22px;
background: var(--essential-color-red);
border-radius: 11px;
}
}
}
}
// 详情-运算路线
.routeItem{
display: flex;
justify-content: center;
align-items: center;
.navigat{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0rpx 0rpx 0rpx 40rpx;
border-left: 2rpx solid #EEEEEE;
margin-left: 20rpx;
font-size: var(--font-size-12);
.naviIcon{
width: 64rpx;
height: 64rpx;
}
}
.routeLine{
display: flex;
justify-content: space-between;
padding:0 20rpx;
flex:1;
.route{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
font-weight: 400;
font-size: var(--font-size-12);
color: var(--neutral-color-main);
flex: 1;
.line{
width: 80%;
.LineImg{
width: 72rpx;
height: 32rpx;
}
}
}
.routePoint{
text-align: center;
font-weight: 400;
font-size: var(--font-size-14);
color: var(--neutral-color-main);
line-height: 60rpx;
.tit{
font-weight: 600;
font-size: var(--font-size-16);
color: var(--neutral-color-main);
}
}
}
}
// 详情-物品信息
.orderCont{
.search{
margin-bottom: 20rpx;
position: relative;
.searchIcon{
position: absolute;
left: 20rpx;
top: 18rpx;
}
.searchInput{
background-color: var(--neutral-color-cancel);
border-radius: 32rpx;
font-size: var(--font-size-12);
line-height: 64rpx;
height: 64rpx;
padding: 0 20rpx 0 60rpx;
}
}
.items{
.item{
display: flex;
justify-content: space-between;
font-size: 28rpx;
line-height: 60rpx;
color: var(--neutral-color-main);
.odd{
flex: 1;
}
.piece, .weight{
text-align: right;
width: 200rpx;
}
}
}
}
// 详情-异常信息
.delay{
display: flex;
font-size: var(--font-size-14);
align-items: center;
border-bottom: solid 2rpx var(--neutral-color-background);
padding: 20rpx 0;
.info{
flex: 1;
color: var(--neutral-color-main);
.line{
line-height: 60rpx;
.desc{
padding-left: 40rpx;
color: var(--neutral-color-font);
}
}
}
.goInfoIcon{
width: 48rpx;
height: 48rpx;
}
}
// 图片上传
.upPicCont{
margin-bottom: 32rpx;
.title{
font-weight: 400;
font-size: var(--font-size-14);
color: var(--neutral-color-font);
margin-bottom: 32rpx;
}
.file-picker__box-content{
border:none;
background: var(--neutral-color-cancel);
}
// 已完成的详情图片回显
.tit{
font-size: 28rpx;
margin-bottom: 20rpx;
}
.upPicContImg{
display: flex;
.image{
width: 180rpx;
height: 180rpx;
margin:0 30rpx 30rpx 0;
}
.image:nth-child(3){
margin-right: 0;
}
}
}
// 详情 - 底部的两按钮
.footCont{
display: flex;
background-color: var(--neutral-color-white);
padding: 20rpx 30rpx;
box-sizing: border-box;
.footButCan{
flex: 125;
margin-right: 30rpx;
}
.footBut{
flex: 200;
}
}

View File

@@ -0,0 +1,148 @@
<!-- 待提货-首页 -->
<template>
<!-- end -->
<view class="pageBox">
<!-- 取件状态列表 -->
<ItemList :itemData="itemData.value" @setTabIndex="setTabIndex" @searchSubmit="searchHandle" :loadding="loading" :moreStatus="moreStatus" />
<!-- end -->
</view>
<!-- footer -->
<UniFooter :pagePath="'pages/index/index'"></UniFooter>
<!-- end -->
</template>
<script>
export default {
name: 'list',
// 上拉刷新
onPullDownRefresh() {
uni.stopPullDownRefresh();
}
};
</script>
<script setup>
import { ref, reactive, onMounted, computed } from 'vue';
import { useStore } from 'vuex';
// 导入接口
import { GetTasksList, GetTaskDetails, PositionUpload } from '@/pages/api/index.js';
import { positionsUploadInit } from '@/utils/index.js';
// 导入组件
// 底部导航
import UniFooter from '@/components/Footer/index.vue';
// 主体部分
import ItemList from './components/ItemList.vue';
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const itemData = reactive([]);
const tabIndex = ref(store.state.taskStatus); //当前tab
const moreStatus = ref('loading'); //加载更多状态 loading more noMore
const loading = ref(false); // 是否展示加载更多
const date = new Date();
const year = date.getFullYear();
const month = computed(() => {
const mt = date.getMonth();
if (mt > 0 && mt < 10) {
return '0' + mt;
} else if (mt == 0) {
return 12;
} else {
return mt;
}
});
const month1 = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;
const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
const params = reactive({
status: tabIndex != 2 ? tabIndex.value + 1 : 4,
transportTaskId: '', // 运单号 非必传
page: 1,
pageSize: 10
});
const pages = ref(0); // 总页数
// ------生命周期------
onMounted(() => {
// 上报位置 进入首页立即上报
positionsUploadInit();
getNewData();
});
// ------定义方法------
const isSendRequest = ref(false); // 是否继续发送请求
// 获取任务列表
const getNewData = async type => {
if (isSendRequest.value) {
return;
}
loading.value = true;
let infoParams = {};
if (store.state.taskStatus === 2) {
infoParams = {
...params,
startTime: month.value == 12 ? `${year - 1}-${month.value}-${day}` : `${year}-${month.value}-${day}`,
endTime: `${year}-${month1}-${day}`
};
} else {
infoParams = {
...params
};
}
await GetTasksList(infoParams)
.then(res => {
if (res.code == 200) {
const { data } = res;
// 搜索数据处理
if (type && type == 'search') {
params.page = 1;
params.pageSize = 10;
itemData.value = data.items ? data.items : [];
} else {
// items == null 会报错 把他处理掉
const items = data.items == null ? [] : data.items;
// 从第一页请求 清空之前的数据
params.page == 1 ? (itemData.value = undefined) : null;
// 下拉数据合并
itemData.value = itemData.value ? [...itemData.value, ...items] : items;
// 如果 当前页面的数据已经全部数据了 那么停止拿数据
if (data.counts == 0) {
isSendRequest.value = true;
}
}
loading.value = false;
counts.value = data.pages;
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none'
});
}
})
.catch(err => {});
};
// 切换 1待提货、2在途、4已完成
const setTabIndex = index => {
const tab = store.state.taskStatus;
store.commit('setTaskStatus', index);
// 下拉加载和切换的时候处理分页参数
params.page = tab == index ? params.page + 1 : 1;
params.status = index != 2 ? index + 1 : 6;
isSendRequest.value = false;
getNewData();
};
// 已完成搜索
const searchHandle = val => {
// 已完成列表提交搜索
if (typeof val == 'string') {
params.transportTaskId = val;
} else {
params.startTime = val.value[0];
params.endTime = val.value[1];
}
getNewData('search');
};
</script>
<style src="./index.scss" lang="scss"></style>

View File

@@ -0,0 +1,222 @@
<!-- 回车登记 -->
<template>
<view>
<!-- 详情 -->
<view class="refister">
<DetailsNav title="回车登记"></DetailsNav>
<view class="container">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true" class="swiperH">
<view class="boxBg">
<view class="infoCard">
<view class="line">
<text>出车时间</text>
<text>{{startTime}}</text>
</view>
<view class="line">
<text>回车时间</text>
<uni-datetime-picker type="datetime" v-model="params.endTime" :clear-icon="false" @change="changeLog" />
</view>
</view>
<!-- 通用卡片组件 - 待提货 - 带开关 -->
<CardCont title="车辆违章" type="redio" redioKey="isBreakRules" @redioChange = "redioChange">
<refisterCards :data="breakRulesType" :value="breakRulesType.data[0]" choiceKey="breakRulesType" @choiceHandel = "choiceHandel" />
<refisterCards :data="penaltyAmount" :value="penaltyAmount.data[0]" choiceKey="penaltyAmount" @choiceHandel = "choiceHandel" />
<refisterCards :data="deductPoints" :value="deductPoints.data[0]" choiceKey="deductPoints" @choiceHandel = "choiceHandel" />
</CardCont>
<CardCont title="车辆故障" type="redio" redioKey="isFault" @redioChange = "redioChange">
<!-- 车辆是否可用 -->
<refisterCards :data="isAvailable" :value="isAvailable.data[0]" choiceKey="isAvailable" @choiceHandel = "choiceHandel" />
<!-- 故障类型 -->
<refisterCards :data="faultType" :value="faultType.data[0]" choiceKey="faultType" @choiceHandel = "choiceHandel" />
<TextArea placeholder="请简单描述故障"></TextArea>
<ImageUpload title="请拍照" @uploadImage="uploadImage" tit='faultImages'></ImageUpload>
</CardCont>
<CardCont title="车辆事故" type="redio" redioKey="isAccident" @redioChange = "redioChange">
<refisterCards :data="accidentType" :value="accidentType.data[0]" choiceKey="accidentType" @choiceHandel = "choiceHandel" />
<TextArea placeholder="请简单描述故障"></TextArea>
<ImageUpload title="请拍照" @uploadImage="uploadImage" tit='accidentImages'></ImageUpload>
</CardCont>
</view>
</scroll-view>
</view>
<!-- end -->
</view>
<!-- footer -->
<view class="footCont">
<view class="footBut">
<text class="button" v-show="params.endTime != '' && (params.endTime).length == 19 " @click="submit()">交车</text>
<text class="button buttonDis1" v-show="params.endTime == '' || (params.endTime).length < 19" >交车</text>
</view>
</view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted, onUpdated, watch, watchEffect, computed, inject } from 'vue';
// 导入接口
import { TruckRegistration } from '@/pages/api/index.js';
// 导入组件
import DetailsNav from '@/components/DetailsNav/index.vue'
import CardCont from '@/components/CardCont/index.vue';
import refisterCards from './components/refisterCards.vue'
import ImageUpload from '@/components/ImageUpload/index.vue'
import TextArea from '@/components/TextArea/index.vue'
// ------定义变量------
const startTime = ref('')
const breakRulesType = {
title: '违章类型',
data: ['闯红灯', '无证驾驶', '超载', '酒后驾驶', '超速驾驶']
}
const penaltyAmount = {
title: '罚款金额',
data: ['0元', '100元', '200元', '300元', '500元', '1000元', '2000元']
}
const deductPoints = {
title: '扣分',
data: ['0分', '1分', '2分', '3分', '6分', '12分']
}
const isAvailable = {
title: '车辆是否可用',
data: ['是', '否']
}
const faultType = {
title: '故障类型',
data: ['启动困难', '不着车', '漏油', '漏水', '照明失灵', '有异响', '排烟异常', '温度异常', '其他']
}
const accidentType = {
title: '事故类型',
data: ['直行事故', '追尾事故', '超车事故', '左转弯事故', '右转弯事故', '弯道事故', '坡道事故', '会车事故', '其他']
}
let params = reactive({
id: '',
startTime: '', //出车时间
endTime: '', //回车时间
isBreakRules: false, //违章
breakRulesType: 1, //违章类型
penaltyAmount: 1, //罚款金额
deductPoints: 1, //扣分
isFault: false, //是否有故障
isAvailable: true, //车辆是否可用
faultType: 1, //故障类型
faultImages: '', //故障图片
isAccident: false, // 是否有事故
accidentType: 1,
accidentImages: '' // 事故图片
})
// ------生命周期------
onMounted(() => {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1].$page.options;
startTime.value = currentPage.time;
params.startTime = currentPage.time;
params.id = currentPage.id
});
// ------定义方法------
// 回车登级-交车数据提交
async function submit() {
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
mask:true
});
},500)
await TruckRegistration(params)
.then(res => {
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
uni.showToast({
title: '回车登记数据提交成功!',
duration: 1000,
icon: 'none'
});
uni.navigateTo({
url: '/pages/index/index'
})
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none'
});
}
})
.catch(err => {});
}
// 是否打开相关的违章、故障、事故并赋值到params
function redioChange(item){
params[item.key] = item.value == 2 ? true : false
}
// 图片上传
const uploadImage = (item) => {
if (item.key == 'accidentImages'){
params.accidentImages = params.accidentImages == '' ? item.value : params.accidentImages + ',' + item.value
} else {
params.faultImages = params.faultImages == '' ? item.value : params.faultImages + ',' + item.value
}
}
// 回车时间
function changeLog(e){
if (e.length < 19){
uni.showToast({
title: '请选择回车时间(包含日期及时间)',
duration: 3000,
icon: 'none'
});
}
}
// 选择违章的数据
function choiceHandel(item){
if (item.key == 'isAvailable'){
params[item.key] = item.value == 1 ? true : false
} else {
params[item.key] = item.value
}
}
</script>
<style src="./index.scss" lang="scss"></style>
<style lang="scss">
.refister .container{
padding-top: 30rpx;
font-size: var(--font-size-14);
.infoCard{
background: #FFFFFF;
border-radius: 20rpx;
padding:0 30rpx;
.line{
display: flex;
justify-content: space-between;
line-height: 60rpx;
border-bottom: solid 2rpx #EEEEEE;
padding: 20rpx 0;
.desc{
color: #818181;
}
}
.line:last-child{
border: none;
}
}
::v-deep .uni-input-placeholder{
text-align: right;
}
}
</style>