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,11 @@
import {
request
} from "../../utils/request.js"
// 获取省市区
export const getProvinces = (params) =>
request({
url: '/areas/children',
method: 'get',
params
})

View File

@@ -0,0 +1,11 @@
import {
request
} from "../../utils/request.js"
// 计算运费
export const calculateFreight = (params) =>
request({
url: '/tasks/calculate',
method: 'post',
params
})

View File

@@ -0,0 +1,150 @@
import {
request
} from "../../utils/request.js"
// 获取相关消息
export const getHomeInfo = (params) =>
request({
url: '/messages/home/get',
method: 'get',
params
})
// 获取首页相关任务、取件、派件、今日已取、已签
export const getHomeData = () =>
request({
url: '/tasks/taskStatistics',
method: 'get'
})
// 获取首页取件派件列表
export const getExpressage = (params) =>
request({
url: `/tasks/${params.type}/${params.state}`,
method: 'get',
params
})
// 取件派件分页列表
export const getDeliveryList = (params) =>
request({
url: '/tasks/page',
method: 'get',
params
})
// 转单
export const transferBatch = (params) =>
request({
url: '/tasks/transfer/batch',
method: 'post',
params
})
// 删除任务
export const taskDelete = (id) =>
request({
url: `/tasks/${id}`,
method: 'delete'
})
// 批量删除
export const taskBatchDelete = (params) =>
request({
url: `/tasks/batch`,
method: 'delete',
params
})
// 取件、派件取消
export const taskCancel = (params) =>
request({
url: `/tasks/cancel`,
method: 'post',
params
})
// 获取任务详情
export const getDetail = (id) =>
request({
url: `/tasks/get/${id}`,
method: 'get'
})
// 身份证号验证
export const idCardCheck = (params) =>
request({
url: `/tasks/idCard/check`,
method: 'post',
params
})
// 去取件
export const getPickup = (params) =>
request({
url: `/tasks/pickup`,
method: 'put',
params
})
// 获取快递员列表
export const getSameAgency = (params) =>
request({
url: `/users/sameAgency`,
method: 'get',
params
})
// 拒收
export const rejection = (id) =>
request({
url: `/tasks/reject/${id}`,
method: 'put'
})
// 签收
export const tasksSign = (params) =>
request({
url: `/tasks/sign`,
method: 'put',
params
})
// 获取支付二维码
export const getQrCode = (params) =>
request({
url: `/pays/qrCode/get`,
method: 'post',
params
})
// 是否支付成功
export const paySucceed = (id) =>
request({
url: `/pays/status/${id}`,
method: 'get'
})
// 获取运单轨迹
export const getTracks = (id) =>
request({
url: `/tasks/tracks/${id}`,
method: 'get'
})
// 搜索、首页、取件、派件
export const getSearch = (params) =>
request({
url: `/tasks/search`,
method: 'post',
params
})
// 最近查找
export const getRecentSearch = () =>
request({
url: `/tasks/recentSearch`,
method: 'get'
})
// 标记为最近查找
export const setMarkRecent = (transportOrderId) =>
request({
url: `/tasks/markRecent/${transportOrderId}`,
method: 'get'
})
// 清空最近查找
export const clearRecentSearch = () =>
request({
url: `/tasks/recentSearch`,
method: 'delete'
})
// 上报位置
export const PositionUpload = (params) =>
request({
url: `/track/upload`,
method: 'put',
data:params,
params
})

View File

@@ -0,0 +1,10 @@
import {
request
} from "../../utils/request.js"
// 作业范围
export const getUserScope = (params) =>
request({
url: `/users/scope`,
method: 'get',
params
})

View File

@@ -0,0 +1,42 @@
import {
request
} from "../../utils/request.js"
// 公告/公告详情/系统通知列表公用接口
export const getNewList = (type) =>
request({
url: `/messages/list?bussinessType=3&contentType=${type}`, //3代表快递员端接口
method: 'get'
})
// 取件、派件、签收、取消列表接口
export const getMessagesList = (params) =>
request({
url: `/messages/page`,
method: 'get',
params
})
// 获取系统通知
export const getNotice = (params) =>
request({
url: '/messages/notice/new/get',
method: 'get',
params
})
// 获取公告详情
export const getDetail= (id) =>
request({
url: `messages/bulletins/get/${id}`,
method: 'get'
})
// 标记已读
export const msgRead= (id) =>
request({
url: `/messages/${id}`,
method: 'put'
})
// 全部已读
export const msgAllRead= (params) =>
request({
url: `/messages/readAll/${params}`,
method: 'put',
params
})

View File

@@ -0,0 +1,32 @@
import {
request
} from "../../utils/request.js"
// 手机号登录
export const phoneLogins = (params) =>
request({
url: `/logins/phone`,
method: 'post',
params
})
// 账号登录
export const userLogins = (params) =>
request({
url: `/login/account`,
method: 'post',
params
})
// 发送短信验证码
export const getsmsCode = (params) =>
request({
url: `/verifyCodes/smsCode`,
method: 'post',
params
})
// 获取用户信息
export const getUserInfo = (params) =>
request({
url: `/users/get`,
method: 'get',
params
})

View File

@@ -0,0 +1,55 @@
<!-- 订单取消原因选择 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="pageBox">
<view class="boxCon concelBox">
<view class="tabConList conCenter">
<view
class="item"
v-for="(item, index) in baseData"
:key="index"
@click="handleClick(item)"
>
<view>{{ item.label }}</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from "vue";
import { useStore } from "vuex";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 导入公用数据
import { cancelData } from "@/utils/commonData.js";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const title = ref("订单取消原因"); //nav标题
const baseData = reactive(cancelData);
// ------定义方法------
const handleClick = (item) => {
let isRedistribute = false;
if (item.value === 8) {
isRedistribute = true;
}
// vuex储存数据
store.commit("user/setReasonVal", item);
store.commit("user/setRedistribute", isRedistribute);
uni.redirectTo({
url: "/pages/cancel/index",
});
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/cancel/index",
});
};
</script>
<style src="../../styles/expressage.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,159 @@
<!-- 订单取消原因申请 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="pageBox">
<view class="boxCon concelBox">
<view class="tabConList">
<view class="item">
<text>寄件人</text>
<view>{{ detailsData.senderName }}</view>
</view>
<view class="item">
<text>订单号</text>
<view>{{ detailsData.orderId }}</view>
</view>
<view class="item">
<text>寄件人地址</text>
<view>{{ detailsData.senderAddress }}</view>
</view>
</view>
</view>
<view class="boxCon concelBox">
<view class="tabConList">
<view class="item" @click="handleCause">
<text>订单取消原因</text>
<view class="cause">
{{ reason }}
<icon class="nextIcon"></icon>
</view>
</view>
<view class="item">
<view>
<textarea
placeholder="订单取消原因描述:"
:placeholder-class="placeholderClass"
v-model="reasonDesc"
@input="monitorInput"
></textarea>
</view>
</view>
</view>
</view>
<view class="btnBox"
><button
class="btn-default uni-mini"
@click="handleSubmit"
:disabled="reason === '' || !reason"
:class="reason === '' || !reason ? 'disabled' : ''"
>
确定
</button></view
>
</view>
</template>
<script setup>
import { ref, onMounted, nextTick } from "vue";
import { useStore } from "vuex";
// 设置字符串的长度
import { validateTextLength } from "@/utils/index.js";
// 接口
import { getDetail, taskCancel } from "@/pages/api/index.js";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const taskId = users.taskId; //用vuex获取列表页传过来的任务id
const title = ref("订单取消原因申请"); //nav标题
let reasonDesc = ref(""); //原因描述
let reason = users.reasonVal.label !== "" ? users.reasonVal.label : ""; //取消原因
const detailsData = ref({}); //详情数据
// ------生命周期------
onMounted(() => {
// 获取订单取消原因描述
if (users.reasonDesc !== "") {
reasonDesc.value = users.reasonDesc;
}
// 获取详情
getDetails(taskId);
});
// ------定义方法------
// 获取详情
const getDetails = async (id) => {
await getDetail(id).then((res) => {
detailsData.value = res.data;
});
};
// 订单取消原因描述控制在100
const monitorInput = () => {
nextTick(() => {
let leng = validateTextLength(reasonDesc.value);
if (leng > 100) {
reasonDesc.value = reasonDesc.value.substring(0, 100);
}
});
};
// 提交原因申请
const handleSubmit = async () => {
if (reason !== "") {
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
const params = {
id: taskId,
reason: users.reasonVal.value,
reasonDesc: reasonDesc.value,
};
await taskCancel(params).then((res) => {
store.commit("user/setTabIndex", 0);
uni.redirectTo({
url: "/pages/pickup/index",
});
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
goBack();
return uni.showToast({
title: "申请成功!",
duration: 1000,
icon: "none",
});
}
});
} else {
return uni.showToast({
title: "请选择取消原因!",
duration: 1000,
icon: "none",
});
}
};
// 取消订单原因选择
const handleCause = () => {
// 由于要跳转到取消原因页面跳转后订单原因描述数据会自动销毁所以先用vuex存起来
store.commit("user/setReasonDesc", reasonDesc.value);
// 任务id
uni.navigateTo({
url: "/pages/cancel/cause",
});
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/pickup/index",
});
};
</script>
<style src="../../styles/expressage.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,260 @@
<template>
<view v-if="tabIndex === 1">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 列表内容 -->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<view class="checkbox" v-if="isAdmin">
<view class="checkRadio"><radio :value="String(index)" :class="item.selected === true ? 'active' : ''" :checked="item.selected" @click="checkbox(index)" /></view>
</view>
<view class="boxBg" :class="isAdmin ? 'adminActive' : ''">
<view class="tabList">
<view class="item" @click="handleDetails($event, item)">
<view class="titInfo">运单号{{ item.transportOrderId }}</view>
<view class="address">收件人{{ item.name }}</view>
<view class="address">派件地址{{ item.address }}</view>
<view class="address">签收时间{{ item.taskTime }}</view>
<view class="time" v-if="item.amount > 0 && item.status === 2">运费{{ item.amount }}</view>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="item.status === 2 && item.paymentStatus === 1 && item.paymentMethod === 2 && item.signStatus !== 2"
>
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
</view>
<!-- 空页面 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted, watch, nextTick } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
//接口
import { getDeliveryList, getSearch } from '@/pages/api/index.js';
// 下拉提示
import ReachBottom from '@/components/reach-bottom/index.vue';
//空页面
import EmptyPage from '@/components/uni-empty-page/index.vue';
// 获取父组件数据
const props = defineProps({
// 当前高度
// 是否触发管理按钮
tabIndex: {
type: Number,
default: 1
},
isAdmin: {
type: Boolean,
default: false
},
// // 搜索分页
searchInfo: {
type: Object,
default: () => ({})
}
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(''); //子组件向父组件事件传递
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let itemData = ref([]);//列表数据
let reload = ref(false);//数据加载
let pages = ref(0);//总页数
let pageNum = ref(1);//当前页
let selected = reactive(new Map());
const emptyData = ref('暂无数据');//空页面提示
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
orderDistance: null,
orderTime: null,
filterOverTime: null,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 5
});
let searchPage = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10
});
// 上下拉取
onReachBottom(() => {
if (pageNum.value >= pages.value) {
loadMore.value.status = 'noMore';
return false;
} else {
loadMore.value.status = 'loading';
let times = setTimeout(() => {
pageNum.value++;
if (props.searchInfo.keyword) {
getSearchList();
} else {
getList();
}
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// 计算是否全选或者单选
watch(users, (newValue, oldValue) => {
if (users.selectTaskData.size > 0) {
for (let [key, value] of users.selectTaskData) {
itemData.value.forEach(element => {
if (value === element.id) {
element.selected = true;
}
});
}
} else {
itemData.value.forEach(element => {
element.selected = false;
});
}
});
// ------生命周期------
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
// 是否触发了搜索清空
if (users.isSearchClear) {
pageNum.value = 1;
store.commit('user/setSearchClear', false);
}
page = {
...page,
page: pageNum.value,
orderDistance: users.orderDistance,
orderTime: users.orderTime,
filterOverTime: users.filterOverTime
};
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
// 触发tab切换
// 如果触发了tab切换或者触发了搜索清空
if (users.istabChange || users.isSearchClear) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
}
});
};
// 搜索数据
const getSearchList = async () => {
reload.value = true;
let valNum = 0;
if (!users.isInput) {
valNum = 1;
pageNum.value = 1;
store.commit('user/setIsFiltrate', false);
}
searchPage = {
...searchPage,
keyword: props.searchInfo.keyword,
status: props.searchInfo.status,
taskType: props.searchInfo.taskType,
page: valNum ? 1 : pageNum.value
};
// 后端接口调用
await getSearch(searchPage).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange || !users.isInput) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
const getSelected = array => {
selected.value = array;
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 已签收详情
const handleDetails = (e, item) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', item.id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit('user/setTaskType', 2);
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit('user/setTaskStatus', 5);
store.commit('user/setNewType', null);
if (item.status === 2 && item.paymentStatus === 1 && item.paymentMethod === 2 && item.signStatus !== 2) {
// 未付款进入付款二维码页面
store.commit('user/setIsDelivery', true);
store.commit('user/setPayData', {});
uni.redirectTo({
url: '/pages/pay/scanPay'
});
} else {
// 进入详情页
uni.redirectTo({
url: '/pages/details/waybill'
});
}
};
//把数据、方法暴漏给父组件
defineExpose({
getList,
getSearchList
});
</script>

View File

@@ -0,0 +1,282 @@
<template>
<view v-if="tabIndex === 0">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 列表内容 -->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<view class="checkbox" v-if="isAdmin">
<view class="checkRadio"><radio :value="String(index)" :class="item.selected === true ? 'active' : ''" :checked="item.selected" @click="checkbox(index)" /></view>
</view>
<view class="boxBg" :class="isAdmin ? 'adminActive' : ''">
<view class="tabList">
<view class="item" @click.stop="handleDetails($event, item.id)">
<view class="titInfo">
<view>
<text class="name">{{ item.name }}</text>
{{ item.phone }}
<icon class="phone" @click.stop="handlePhone($event, item.phone)"></icon>
<icon class="note" @click.stop="handleNote"></icon>
</view>
</view>
<view class="address">{{ item.address }}</view>
<view class="address">{{ item.distance }}公里</view>
<view class="time">运单号{{ item.transportOrderId }}</view>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
</view>
<!-- 空页面 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
<!-- 拨打电话弹层 -->
<Phone ref="phone" :phoneData="phoneData"></Phone>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
//接口
import { getDeliveryList, getSearch } from '@/pages/api/index.js';
// 下拉提示
import ReachBottom from '@/components/reach-bottom/index.vue';
//空页面
import EmptyPage from '@/components/uni-empty-page/index.vue';
import Phone from '@/components/uni-phone/index.vue';
// 获取父组件数据
const props = defineProps({
// 当前触发的tab值
tabIndex: {
type: Number,
default: 0
},
// 是否触发管理按钮
isAdmin: {
type: Boolean,
default: false
},
// // 搜索分页
searchInfo: {
type: Object,
default: () => ({})
}
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(''); //子组件向父组件事件传递
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
const phone = ref();
let itemData = ref([]);//列表数据
let reload = ref(false);//数据加载
let pages = ref(0); //总页数
let pageNum = users.isFiltrate ? 1 : ref(1); //存放当前页
let selected = reactive(new Map());
const emptyData = ref('暂无数据');//空页面提示
const phoneData = ref('');
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
orderDistance: null,
orderTime: null,
filterOverTime: null,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 4
});
let searchPage = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10
});
// 上下拉取
onReachBottom(() => {
store.commit('user/setIsInput', true); //是否在文本框里输入了文字
if (pageNum.value >= Number(pages.value)) {
loadMore.value.status = 'noMore';
return false;
} else {
loadMore.value.status = 'loading';
let times = setTimeout(() => {
pageNum.value++;
if (props.searchInfo.keyword) {
getSearchList();
} else {
getList();
}
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// ------生命周期------
// 计算是否全选或者单选
watch(users, (newValue, oldValue) => {
if (users.selectTaskData.size > 0) {
for (let [key, value] of users.selectTaskData) {
itemData.value.forEach(element => {
if (value === element.id) {
element.selected = true;
}
});
}
} else {
itemData.value.forEach(element => {
element.selected = false;
});
}
});
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
//判断是否进行了距离、时间、超时任务筛选如果是当前页设为第一页上拉的数值设为1便于第二次上拉
let valNum = 0;
if (users.isFiltrate || users.isSearchClear) {
valNum = 1;
pageNum.value = 1;
// 如果触发了距离、时间、超时筛选
if (users.isFiltrate) {
store.commit('user/setIsFiltrate', false);
}
// 是否触发了搜索清空
if (users.isSearchClear) {
store.commit('user/setSearchClear', false);
}
}
page = {
...page,
page: valNum ? 1 : pageNum.value,
orderDistance: users.orderDistance,
orderTime: users.orderTime,
filterOverTime: users.filterOverTime
};
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0 || users.isFiltrate) {
itemData.value = [];
}
// 触发tab切换
// 如果触发了tab切换或者触发了搜索清空
if (users.istabChange || users.isSearchClear) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
}
});
};
// 搜索数据
const getSearchList = async () => {
reload.value = true;
let valNum = 0;
if (!users.isInput) {
valNum = 1;
pageNum.value = 1;
store.commit('user/setIsFiltrate', false);
}
searchPage = {
...searchPage,
keyword: props.searchInfo.keyword,
status: props.searchInfo.status,
taskType: props.searchInfo.taskType,
page: valNum ? 1 : pageNum.value
};
// 后端接口调用
await getSearch(searchPage).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange || !users.isInput) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
// 获取多选或者单选的数据
const getSelected = array => {
selected.value = array;
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 去派件详情
const handleDetails = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit('user/setTaskType', 2);
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit('user/setTaskStatus', 4);
store.commit('user/setNewType', null);
// 进入详情页
uni.redirectTo({
url: '/pages/details/waybill'
});
};
// 拨打电话弹层
const handlePhone = (e, val) => {
e.stopPropagation();
phoneData.value = val;
phone.value.dialogOpen();
};
// 发短信
const handleNote = () => {
uni.showToast({
title: '程序员哥哥正在实现中',
duration: 1000,
icon: 'none'
});
};
//把数据、方法暴漏给父组件
defineExpose({ getList, getSearchList });
</script>

View File

@@ -0,0 +1,144 @@
<template>
<!-- 待派件 -->
<DealParcel ref="dealparcel" :tabIndex="tabIndex" :isAdmin="isAdmin" @checkbox="checkbox" @getSelected="getSelected" :searchInfo="searchInfo"></DealParcel>
<!-- end -->
<!-- 已签收 -->
<AlreadyParcel ref="already" :tabIndex="tabIndex" :isAdmin="isAdmin" @checkbox="checkbox" :searchInfo="searchInfo"></AlreadyParcel>
<!-- end -->
<!-- 提示窗示例 -->
<UniPopup ref="popup" :tipInfo="tipInfo" @handleClick="handleClick"></UniPopup>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted, watch } from 'vue';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
//接口
import { getDeliveryList, taskDelete } from '@/pages/api/index.js';
// 导入组件
// 待派件
import DealParcel from './components/dealParcel.vue';
// 已签收
import AlreadyParcel from './components/alreadyParcel.vue';
// 弹层
import UniPopup from '@/components/uni-popup/index.vue';
// 获取父组件数据
const props = defineProps({
// tab切换数据
tabBars: {
type: Object,
default: () => ({})
},
tabIndex: {
type: Number,
default: 0
},
// 是否触发管理按钮
isAdmin: {
type: Boolean,
default: false
},
// 获取当前筛选的距离升序还是降序
orderDistance: {
type: Number,
default: 0
},
// 获取当前筛选的时间升序还是降序
orderTime: {
type: Number,
default: 0
},
// 获取当前筛选超时
filterOverTime: {
type: Number,
default: 0
}
});
// ------定义变量------
const emit = defineEmits(''); //子组件向父组件事件传递
const store = useStore(); //设置、获取储存的数据
const users = store.state.user;
let popup = ref();
let dealparcel = ref();
let already = ref();
let cancel = ref();
const tipInfo = ref('确定要删除吗?');
let taskId = ref('');
let scrollH = ref(0); //滚动高度
let searchInfo = reactive({
keyword:null,
status:null,
taskType:null
});
// ------生命周期------
onMounted(() => {
// 获取屏幕信息
uni.getSystemInfo({
success: res => {
scrollH.value = res.windowHeight;
}
});
});
// ------定义方法------
// 获取初始值
const init = () => {
};
// 获取已经选的任务
const getSelected=(array)=>{
emit('getSelected',array)
}
// 获取待派件列表方法
const dealPList =()=>{
dealparcel.value.getList()
}
// 搜索待派件列表方法
const dealSearchList = () => {
dealparcel.value.getSearchList();
};
// 获取已签收列表方法
const alreadList =()=>{
already.value.getList()
}
// 搜索已签收列表方法
const alreadSearchList = () => {
already.value.getSearchList();
};
// 确认删除
const handleClick = async () => {
await taskDelete(taskId.value)
.then(res => {
if (res.code === 200) {
dealparcel.value.getList()
return uni.showToast({
title: '删除成功!',
duration: 1000,
icon: 'none'
});
}
})
};
//左右滑动tab切换
const onChangeSwiperTab = e => {
emit('onChangeSwiperTab', e);
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 删除弹层id
const handleOpen = id => {
popup.value.dialogOpen();
taskId.value = id;
};
//把数据、方法暴漏给父组件
defineExpose({
dealPList,
dealSearchList,
alreadList,
alreadSearchList,
searchInfo
});
</script>

View File

@@ -0,0 +1,7 @@
.expressage {
.tabScroll {
.scroll-row-item {
margin-right: 80rpx;
}
}
}

View File

@@ -0,0 +1,240 @@
<!-- 派件页面 -->
<template>
<!-- 搜索nav -->
<SearchPage
@handleSearch="handleSearch"
ref="search"
@clearSearchData="clearSearchData"
></SearchPage>
<!-- end -->
<view>
<!-- tab切换 -->
<UniTab
:tabBars="tabBars"
ref="tab"
@getTabIndex="getTabIndex"
class="pickupTab"
></UniTab>
<!-- end -->
<!-- 筛选 -->
<ListFiltrate
v-if="tabIndex === 0"
@getList="getList"
class="pickupFilrate"
></ListFiltrate>
<!-- end -->
<!-- 取件状态列表 -->
<view
:class="tabIndex === 0 ? 'pickupBoxTop' : 'pickupTop'"
style="padding: 0 0 200rpx 0"
>
<TabList
:tabBars="tabBars"
:tabIndex="tabIndex"
:isAdmin="isAdmin"
@onChangeSwiperTab="onChangeSwiperTab"
@checkbox="checkbox"
ref="list"
></TabList>
</view>
<!-- end -->
</view>
<!-- 底部管理 单选\转单\打印\删除 -->
<!-- 派件后期会加功能,所以这块代码先不删除 -->
<ExpressageFoot
ref="expressageFoot"
@getAdmin="getAdmin"
:isAdmin="isAdmin"
:isDelivery="isDelivery"
:selected="selected"
:tabIndex="tabIndex"
@allSelect="allSelect"
@handleClick="handleClick"
></ExpressageFoot>
<!-- end -->
<!-- footer -->
<UniFooter :pagePath="'pages/delivery/index'"></UniFooter>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useStore } from "vuex";
// 基本数据
import { PickUpData } from "@/utils/commonData.js";
// 接口api
import { taskBatchDelete } from "@/pages/api/index.js";
// 导入组件
// 导航
// 搜索组件
import SearchPage from "@/components/uni-search/index.vue";
// 底部导航
import UniFooter from "@/components/uni-footer/index.vue";
// tab切换
import UniTab from "@/components/uni-tab/index.vue";
// 筛选
import ListFiltrate from "@/components/uni-list-filtrate/index.vue";
// 底部管理全选组件
import ExpressageFoot from "@/components/uni-expressage-foot/index.vue";
// list
import TabList from "./components/list.vue";
// ------定义变量------
const store = useStore();
const users = store.state.user;
const emit = defineEmits(""); //子组件向父组件事件传递
const tab = ref();
const list = ref();
const search = ref(); //定义搜索ref
const expressageFoot = ref();
const tabBars = PickUpData;
let tabIndex = ref(0); //当前tab
let isDelivery = ref(true);
let isAdmin = ref(false); //是否触发管理按钮
// 存储已选内容, 因为这个列表是增删很频繁的所以选用map而不是数组key对应的是数据的下标。至于value存放什么完全由自己定
let selected = reactive(new Map());
// ------生命周期------
onMounted(() => {
if (users.tabIndex) {
tabIndex.value = users.tabIndex;
}
if (users.tabIndex === 0) {
list.value.dealPList();
} else {
list.value.alreadList();
}
});
// ------定义方法------
// 搜索
const handleSearch = () => {
list.value.searchInfo.taskType = 2;
list.value.searchInfo.keyword = search.value.searchVal;
store.commit("user/setIsInput", false); //是否在文本框里输入了文字默认false
store.commit("user/setDeliveryData", []);
if (tabIndex.value === 0) {
list.value.searchInfo.status = 1;
list.value.dealSearchList();
} else {
list.value.searchInfo.status = 2;
list.value.alreadSearchList();
}
};
// 批量删除
const handleClick = async () => {
const ids = [];
// 要批量删除的id
for (const [key, value] of selected) {
ids.push(value);
}
await taskBatchDelete({ idList: ids }).then((res) => {
if (res.code === 200) {
list.value.alreadList();
// 存储列表数据
store.commit("user/setDeliveryData", []);
// 总页数清空
store.commit("user/setPages", 0);
store.commit("user/setSelectTaskData", new Map());
selected.clear();
// expressageFoot.value.isAdmin = false
isAdmin.value = false;
return uni.showToast({
title: "删除成功!",
duration: 1000,
icon: "none",
});
}
});
};
// 清除搜索
const clearSearchData = () => {
store.commit("user/setIsInput", true);
store.commit("user/setDeliveryData", []); //清空列表数据
store.commit("user/setSearchText", ""); //清空搜索框内容
store.commit("user/setSearchClear", true); //是否清空搜索框
list.value.searchInfo.keyword = ""; //清空搜索框内容
// 总页数清空
store.commit("user/setPages", 0);
if (tabIndex.value === 0) {
list.value.dealPList();
} else {
list.value.alreadList();
}
};
// 获取tab切换当前的index
const getTabIndex = (index) => {
tabIndex.value = index;
// 存储列表数据
store.commit("user/setDeliveryData", []);
// 总页数清空
store.commit("user/setPages", 0);
store.commit("user/setSelectTaskData", new Map());
store.commit("user/setFilterOverTime", null);
store.commit("user/setIsFiltrate", false);
search.value.searchVal = "";
store.commit("user/setSearchText", ""); //清空搜索框内容
store.commit("user/setSearchClear", true); //是否清空搜索框
selected.clear();
// 修改底部管理按钮状态
isAdmin.value = false;
// 根据不同的tab值切更新 取件数据
if (index === 0) {
list.value.dealPList();
} else {
list.value.alreadList();
}
};
// 触发选项卡事件
const onChangeSwiperTab = (e) => {
tab.value.changeTab(e.detail.current);
};
// 获取foot底部组件的管理按钮触发值向列表页传递全选单选用
const getAdmin = (val) => {
isAdmin.value = val;
};
// 给筛选组件传递,刷新列表
const getList = () => {
list.value.dealPList();
};
// 全选与反选事件
const allSelect = () => {
// 已经全选情况下,就是反选,全选就说明长度一样
let itemData = users.deliveryData;
if (selected.size === itemData.length) {
selected.clear(); // 全部清除
itemData.forEach((element) => {
element.selected = false; // 全部不选,就行了
});
}
// 还未全选的状态下
else {
itemData.forEach((element, index) => {
// 因为可能存在部分已经选择了,所以得先判断是否已存在,不存在才需要添加
if (!selected.has(index)) {
selected.set(index, element.id); // key是对应的下标index而value是可以自定义的
element.selected = true; // 设为选中
}
});
}
emit("getSelected", selected);
store.commit("user/setSelectTaskData", selected);
};
// 选项框点击事件,参数是数据的下标
const checkbox = (index) => {
// 选中的状态下再次点击,即为取消选中
let itemData = users.deliveryData;
if (itemData[index].selected) {
itemData[index].selected = false;
selected.delete(index); // 然后删除对应key即可
}
// 未选中状态下点击
else {
itemData[index].selected = true;
selected.set(index, itemData[index].id);
}
store.commit("user/setSelectTaskData", selected);
};
</script>
<style src="../../styles/expressage.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,46 @@
<!--取派地址-->
<template>
<view class="boxBg">
<view class="addressCon">
<view class="item">
<view class="sendIcon">{{ taskType === 1 ? "取" : "派" }}</view>
<view class="addressInfo">
<view
><text class="name">{{ detailsData.senderName }}</text
>{{ detailsData.senderPhone }}</view
>
<view>{{ detailsData.senderAddress }}</view>
</view>
</view>
<view class="item">
<view class="receiveIcon">{{ taskType === 1 ? "派" : "收" }}</view>
<view class="addressInfo">
<view
><text class="name">{{ detailsData.receiverName }}</text
>{{ detailsData.receiverPhone }}</view
>
<view>{{ detailsData.receiverAddress }}</view>
</view>
</view>
<view class="line"></view>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
// 获取父组件数据
const props = defineProps({
detailsData: {
type: Object,
default: () => ({}),
},
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user; //vuex获取、储存数据
let taskType = users.taskType;
</script>
<style>
</style>

View File

@@ -0,0 +1,118 @@
<!--身份证验证-->
<template>
<view class="boxBg">
<view class="tit">
<text v-if="detailsData.idCardNoVerify === 0 && !flag">
身份验证未验证
<icon class="iconTip"></icon>
</text>
<text v-else-if="detailsData.idCardNoVerify === 1 || flag">
身份验证验证通过
<!-- TODO 先保留-->
<!-- <icon class="iconTip"></icon> -->
</text>
<text v-else>
身份验证验证未通过
<icon class="iconTip"></icon>
</text>
</view>
<view class="identityBox" v-if="detailsData.idCardNoVerify !== 1 && !flag">
<view>
<uni-forms ref="customForm">
<uni-forms-item name="name"
><uni-easyinput
class="item"
v-model="name"
placeholder="请输入真实姓名"
/></uni-forms-item>
<uni-forms-item name="idCard"
><uni-easyinput
class="item"
v-model="idCard"
placeholder="请输入身份证号码"
@blur="handleIdcard"
/></uni-forms-item>
</uni-forms>
<button class="uni-btn concelBtn" @click="handleCheck">验证</button>
</view>
</view>
<view class="identitySuccee" v-else>
<view class="text" v-if="name !== ''">{{ name }}</view>
<view class="text">{{
idCard !== "" ? idCard : detailsData.idCardNo
}}</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useStore } from "vuex";
// 验证
import { validateIdentityCard } from "@/utils/validate";
// 接口
import { idCardCheck } from "@/pages/api/index.js";
// 获取父组件数据
const props = defineProps({
detailsData: {
type: Object,
default: () => ({}),
},
});
// ------定义变量------
const customForm = ref();
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
// 表单数据
let idCard = ref(""); //身份证号
let name = ref(""); //身份证号
let isValidate = ref(false); //输入身份证是否验证成功
let flag = ref(null); //是否校验成功
// ------定义方法------
onMounted(() => {
if (users.cardData) {
name.value = users.cardData.name;
name.idCard = users.cardData.idCard;
flag.value = true;
}
});
// 身份校验
const handleIdcard = () => {
const validate = validateIdentityCard(idCard.value);
if (validate) {
isValidate.value = true;
} else {
return uni.showToast({
title: validate,
duration: 1000,
icon: "none",
});
}
};
// 验证身份号
const handleCheck = async () => {
const params = {
name: name.value,
idCard: idCard.value,
};
store.commit("user/setCardData", params);
await idCardCheck(params)
.then((res) => {
if (res.code === 200) {
flag.value = res.data.flag;
return uni.showToast({
title: "验证成功",
duration: 1000,
icon: "none",
});
}
})
};
// 暴漏给父组件
defineExpose({
customForm,
idCard,
name,
isValidate,
});
</script>

View File

@@ -0,0 +1,221 @@
<template>
<view class="uniPopup detailPopup">
<uni-popup ref="popup" type="bottom">
<view class="tit">
<text>{{
type === 1
? "物品名称"
: type === 2
? "付款方式"
: type === 3
? "备注"
: "签收人"
}}</text>
<icon @click="dialogClose">关闭</icon>
</view>
<view class="popupContent">
<!-- 物品名称 -->
<view v-if="type === 1">
<view class="goodBox">
<view
v-for="(item, index) in GoodsData"
:key="index"
class="item"
:class="index === isActive ? 'active' : ''"
@click="handleActive(index, item)"
>
<text>{{ item.label }}</text>
</view>
</view>
<view v-if="isShowGoodInfo" class="other">
<textarea
v-model="otherData"
placeholder="请输入物品信息"
@input="monitorInput"
:maxlength="goodMaxLength"
></textarea>
<text class="numText" :class="goodNumVal === 0 ? 'tip' : ''"
>{{ goodNumVal }}/10</text
>
</view>
</view>
<!-- end -->
<!-- 付款方式 -->
<view v-else-if="type === 2">
<view
class="typeItem"
v-for="(item, index) in PayMethodData"
:key="index"
@click="checkbox(index)"
>
<text>{{ item.label }}</text>
<view class="checkRadio"
><radio
:value="String(index)"
:class="index === current ? 'active' : ''"
:checked="index === current"
/></view>
</view>
</view>
<!-- end -->
<!-- 备注 -->
<view v-else-if="type === 3" class="remark">
<textarea
v-model="remark"
placeholder="补充说明"
@input="textInput"
:maxlength="remarkMaxLength"
></textarea>
<text class="numText" :class="remarkNumVal === 0 ? 'tip' : ''"
>{{ remarkNumVal }}/30</text
>
</view>
<!-- end -->
<!-- 签收人 -->
<view v-else>
<view
class="typeItem"
v-for="(item, index) in SignData"
:key="index"
@click="checkbox(index)"
>
<text>{{ item.label }}</text>
<view class="checkRadio"
><radio
:value="String(index)"
:class="index === current ? 'active' : ''"
:checked="index === current"
/></view>
</view>
</view>
<!-- end -->
</view>
<view class="btnBox"
><button class="btn-default uni-mini" @click="handleSubmit">
确定
</button></view
>
</uni-popup>
</view>
</template>
<script setup>
import { ref, nextTick, watch } from "vue";
import { validateTextLength } from "@/utils/index.js";
// 基本数据
import { PayMethodData, GoodsData, SignData } from "@/utils/commonData.js";
// 获取父组件数据
const props = defineProps({
detailsData: {
type: Object,
default: () => ({}),
},
type: {
type: Number,
default: 0,
},
});
watch(props, (newValue, olcValue) => {
if (newValue !== undefined) {
remark.value = newValue.detailsData.remark;
if (newValue.type === 2) {
if (newValue.detailsData.paymentMethod === 1) {
current.value = 0;
} else {
current.value = 1;
}
}
}
});
// ------定义变量------
// 定义ref 获取子组件方法或者值
const popup = ref();
const emit = defineEmits(); //子组件向父组件事件传递
let current = ref(0); //当前触发付款方式的值
let isActive = ref(0); //当前触发物品名称的值
let otherData = ref(""); //自定义其他物品信息
let goodNumVal = ref(0); //其他自定义的字节数控制值
let remarkNumVal = ref(0); //备注字节数控制值
let remark = ref(""); //备注
let goodMaxLength = ref(10);
let remarkMaxLength = ref(30);
let isShowGoodInfo = ref(false); //控制其他文本域的显示/隐藏
// ------定义方法------
// 确定
const handleSubmit = () => {
// type=1 物品名称 type=2 付款方式 type=3 备注
if (props.type === 1) {
let val = null;
if (isShowGoodInfo.value) {
if (otherData.value === "") {
return uni.showToast({
title: "请输入物品信息",
duration: 1000,
icon: "none",
});
}
val = otherData.value;
} else {
val = GoodsData[isActive.value].label;
}
emit("getGoodType", val);
} else if (props.type === 2) {
emit("getPayMethod", PayMethodData[current.value].label);
} else if (props.type === 3) {
emit("getRemark", remark.value);
} else {
emit("getSignType", SignData[current.value].value);
}
dialogClose();
};
// 选项框点击事件,参数是数据的下标
const checkbox = (index) => {
current.value = index;
};
// 选择物品
const handleActive = (index, item) => {
if (item.label === "其他") {
isShowGoodInfo.value = true;
} else {
isShowGoodInfo.value = false;
}
isActive.value = index;
};
// 打开弹层
const dialogOpen = () => {
popup.value.open();
};
// 关闭弹层
const dialogClose = () => {
popup.value.close();
};
// 其他自定义的名称控制10个字符
const monitorInput = () => {
nextTick(() => {
let leng = validateTextLength(otherData.value);
if (leng >= 10) {
goodMaxLength.value = leng;
} else {
goodMaxLength.value = 20;
}
goodNumVal.value = Math.floor(leng);
});
};
// 备注控制50个字符
const textInput = () => {
nextTick(() => {
let leng = validateTextLength(remark.value);
if (leng >= 30) {
remarkMaxLength.value = leng;
} else {
remarkMaxLength.value = 60;
}
remarkNumVal.value = Math.floor(leng);
});
};
// 向父组件暴露方法
defineExpose({
dialogOpen,
current,
});
</script>

View File

@@ -0,0 +1,667 @@
body,
uni-page-body,
uni-page-head,
.uni-page-head {
background-color: var(--neutral-color-background) !important;
}
.detailBox {
padding-bottom: 186rpx;
.boxBg {
margin-top: 32rpx;
padding: 28rpx 26rpx;
}
::v-deep .tit {
display: flex;
align-items: center;
& > text {
flex: 1;
color: var(--neutral-color-font);
span {
display: flex;
align-items: center;
}
}
.copy {
background: url(@/static/icon20.png) no-repeat;
background-size: contain;
width: 24rpx;
height: 30rpx;
margin-left: 14rpx;
}
.goodsSelect {
text-align: right;
display: flex;
align-items: center;
}
.iconTip {
background: var(--essential-color-red);
width: 12rpx;
height: 12rpx;
border-radius: 50%;
vertical-align: middle;
}
.textInfo {
color: var(--neutral-color-main);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
width: 400rpx;
// padding-right: 20rpx;
vertical-align: middle;
text-align: right;
}
}
.freight {
display: flex;
border-top: 1px solid var(--neutral-color-background);
padding: 36rpx 0 12rpx;
margin-top: 16rpx;
view {
&:first-child {
flex: 1;
text {
font-size: var(--font-size-12);
color: var(--neutral-color-font);
padding-left: 10rpx;
text {
color: var(--essential-color-red);
}
}
}
&:last-child {
text-align: right;
display: flex;
align-items: center;
}
::v-deep uni-input {
width: 120rpx;
font-size: var(--font-size-14);
color: var(--neutral-color-main);
height: 30rpx;
line-height: 30rpx;
min-height: 30rpx;
}
}
}
.btnBox {
position: fixed;
left: 0;
right: 0;
bottom: 10rpx;
.btn-default {
box-shadow: 0 7px 12px 0 rgba(239, 79, 63, 0.41);
}
}
::v-deep .identityBox {
padding: 8rpx 0 0;
margin-top: 28rpx;
border-top: 1px solid var(--neutral-color-background);
display: flex;
align-items: center;
& > view {
&:first-child {
flex: 1;
display: flex;
align-items: center;
}
}
.concelBtn {
line-height: 60rpx;
height: 60rpx;
border-radius: 30rpx;
margin-top: 40rpx !important;
}
.uni-forms {
flex: 1;
.is-input-border {
border: 0 none !important;
}
.uni-easyinput__content-input {
padding: 0 !important;
}
.uni-easyinput__placeholder-class {
font-size: var(--font-size-14);
}
.uni-forms-item__inner {
padding: 0;
}
}
.text {
padding-top: 10rpx;
}
}
::v-deep .identitySuccee {
padding: 20rpx 0;
line-height: 60rpx;
}
:deep(.pickupBox){
padding-bottom: 0;
.addressCon{
.item{
padding: 10rpx 0 !important;
}
}
}
}
::v-deep .addressCon {
.item {
display: flex;
padding: 16rpx 0;
.name {
padding-right: 20rpx;
}
}
}
::v-deep .goodsCon {
.item {
&:first-child {
padding-top: 12rpx;
}
}
}
// 弹层
::v-deep .detailPopup {
.uni-popup__wrapper {
background: var(--neutral-color-white) !important;
border-radius: 32rpx 32rpx 0 0 !important;
.btn-default {
height: 90rpx;
line-height: 90rpx;
}
.tit {
height: 120rpx;
line-height: 120rpx;
display: flex;
padding: 0 44rpx;
align-items: center;
border-bottom: 1px solid var(--neutral-color-background);
text {
flex: 1;
}
icon {
text-align: right;
background: url(@/static/icon21.png) no-repeat;
background-size: contain;
width: 26rpx;
height: 26rpx;
text-align: right;
}
}
.typeItem {
border-bottom: 1px solid var(--neutral-color-background);
display: flex;
padding: 0 44rpx;
align-items: center;
height: 116rpx;
line-height: 116rpx;
text {
flex: 1;
}
}
.goodBox {
padding: 0 32rpx 0 4rpx;
display: flex;
flex-flow: row wrap;
align-content: flex-start;
.item {
box-sizing: border-box;
flex: 0 0 29.3%;
margin-top: 40rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: var(--neutral-color-background);
border-radius: 20rpx;
margin-left: 28rpx;
}
.active {
border: 1px solid #ef4f3f;
height: 76rpx;
line-height: 76rpx;
background: var(--neutral-color-white);
}
}
}
.other {
height: 40rpx;
line-height: 40rpx;
padding: 28rpx 30rpx;
margin: 40rpx 30rpx 0;
background: var(--neutral-color-background);
border-radius: 10px;
position: relative;
.uni-textarea-textarea {
height: 40rpx;
}
.numText {
position: absolute;
top: 28rpx;
right: 24rpx;
color: var(--neutral-color-font);
.tip {
color: #bdbdbd;
}
}
}
.remark {
// height: 40rpx;
line-height: 40rpx;
padding: 28rpx 30rpx;
margin: 40rpx 30rpx 0;
background: var(--neutral-color-background);
border-radius: 10px;
position: relative;
// .uni-textarea-textarea{
// height: 40rpx;
// }
.numText {
position: absolute;
bottom: 28rpx;
right: 24rpx;
color: var(--neutral-color-font);
.tip {
color: #bdbdbd;
}
}
}
}
// 运单详情
.wayCon {
.item {
display: flex;
align-items: center;
line-height: 80rpx;
height: 80rpx;
color: var(--neutral-color-font);
text {
flex: 1;
text-align: right;
color: var(--neutral-color-main);
}
}
}
.remark {
border-top: 1px solid var(--neutral-color-background);
border-bottom: 1px solid var(--neutral-color-background);
padding: 30rpx 0;
margin: 20rpx 0;
.item {
text-align: left;
display: inherit;
height: auto;
line-height: 40rpx;
&:last-child {
padding: 20rpx 0 0;
}
}
}
::v-deep .content {
uni-cover-view {
overflow: initial;
}
}
.scroll-Y {
height: 300rpx;
}
.bottmBox {
background: var(--neutral-color-white);
border-radius: 10px;
position: fixed;
top: 190rpx;
bottom: 0;
left: 0;
right: 0;
z-index: 999;
padding-top: 40rpx;
.orderList {
padding: 0 32rpx 40rpx;
margin: 0 30rpx;
height: calc(82vh - 20rpx);
// height: 400px;
overflow-y: scroll;
.fontPostion {
position: absolute;
top: 8rpx;
}
& > .uni-cover-view {
uni-cover-view {
// &:nth-child(1) {
// .logistics-orderInfo-right {
// margin-left: 12rpx;
// }
// }
// &:nth-child(2) {
// .logistics-orderInfo-right {
// margin-left: 10rpx;
// }
// }
}
}
.item {
position: relative;
padding-bottom: 60rpx;
border-left: 1px dotted #ccc;
&:last-child {
border: 0 none;
padding: 0;
}
.iconBg {
width: 52rpx;
height: 52rpx;
background: var(--neutral-color-font);
line-height: 52rpx;
color: var(--neutral-color-white);
text-align: center;
padding: 10rpx 0 0 0rpx;
box-sizing: border-box;
border-radius: 50%;
position: absolute;
left: -26rpx;
icon {
width: 30rpx;
height: 32rpx;
}
&.harvest {
background: var(--essential-color-red);
}
.pickUp {
background: url(@/static/icon24.png) no-repeat;
background-size: contain;
}
.transport {
background: url(@/static/icon25.png) no-repeat;
background-size: contain;
width: 37rpx;
height: 28rpx;
}
}
.rtext {
padding-left: 48rpx;
line-height: 34rpx;
font-size: var(--font-size-12);
.tit {
font-weight: 600;
font-size: var(--font-size-16);
line-height: 44rpx;
}
.time {
color: var(--neutral-color-font);
padding: 2rpx 0 6rpx;
}
}
}
}
}
// ::v-deep .logistics-orderInfo {
// &.logistics-orderInfo-item.active {
// .logistics-orderInfo-left {
// .iconBg {
// background-color: #e63e32 !important;
// }
// }
// }
// &.logistics-orderInfo-item {
// & > .uni-cover-view {
// // display: flex;
// // padding-bottom: 60rpx;
// }
// .logistics-orderInfo-left {
// float: left;
// // text-align: center;
// // .gray.circle {
// // background-color: #818181;
// // }
// .logistics-orderInfo-item.active {
// .logistics-orderInfo-left {
// .circle {
// background-color: #e63e32 !important;
// }
// }
// }
// .circle {
// position: relative;
// // background-color: #818181;
// right: 20rpx;
// width: 52rpx;
// height: 52rpx;
// line-height: 52rpx;
// font-size: 24rpx;
// display: flex;
// align-items: center;
// justify-content: center;
// color: #818181;
// image {
// width: 40rpx;
// height: 40rpx;
// }
// .uni-cover-view {
// width: 52rpx;
// // height: 52rpx;
// // line-height: 52rpx;
// display: flex;
// align-items: center;
// justify-content: center;
// text-align: center;
// }
// }
// .point {
// width: 10rpx;
// height: 10rpx;
// border-radius: 5rpx;
// background-color: #818181;
// margin-right: 40rpx;
// }
// .line {
// min-height:11vh;
// width: 2rpx;
// background: #dfdfdf;
// border-left: 0.5rpx dashed #dfdfdf;
// position: relative;
// left: 4rpx;
// }
// .line.short {
// height: 120rpx;
// }
// .iconBg {
// width: 52rpx;
// height: 52rpx;
// background: var(--neutral-color-font);
// line-height: 52rpx;
// color: var(--neutral-color-white);
// text-align: center;
// box-sizing: border-box;
// border-radius: 50%;
// // display: flex;
// // align-items: center;
// // justify-content: center;
// uni-cover-image {
// width: 26rpx;
// height: 26rpx;
// align-items: center;
// justify-content: center;
// display: flex;
// padding-left: 12rpx;
// margin-left: 6rpx;
// img{
// width: 30rpx;
// height: 20rpx;
// }
// }
// &.harvest {
// background: var(--essential-color-red);
// }
// .pickUp {
// background: url(@/static/icon24.png) no-repeat;
// background-size: contain;
// }
// .transport {
// background: url(@/static/icon25.png) no-repeat;
// background-size: contain;
// width: 37rpx;
// height: 28rpx;
// }
// .delivery {
// background: url(@/static/paisong.png) no-repeat;
// background-size: contain;
// width: 37rpx;
// height: 28rpx;
// }
// }
// }
// .logistics-orderInfo-right {
// .status {
// font-size: 32rpx;
// color: #2a2929;
// font-weight: bold;
// margin-bottom: 6rpx;
// }
// .time {
// margin-bottom: 6rpx;
// }
// .time,
// .desc {
// font-size: 24rpx;
// color: #818181;
// }
// .desc {
// float: left;
// white-space: normal;
// line-height: normal;
// line-height: 34rpx;
// min-height: 120rpx;
// }
// .desc.active {
// // font-weight: bold;
// color: #2a2929;
// min-height: 160rpx;
// }
// }
// }
// }
// .up {
// animation: upAmimat 500ms infinite;
// }
// .down {
// animation: downAmimat 500ms infinite;
// }
// @keyframes upAmimat {
// from {
// top: 200px;
// }
// to {
// top: 200px;
// }
// }
// @keyframes downAmimat {
// from {
// bottom: 0px;
// }
// to {
// bottom: 0px;
// }
// }
::v-deep .logistics-orderInfo{
&.logistics-orderInfo-item.active{
.red{
// font-weight: bold;
color:#E63E32!important;
}
.logistics-orderInfo-left{
.circle{
background-color:#E63E32!important;
}
}
}
&.logistics-orderInfo-item{
display: flex;
.logistics-orderInfo-left{
// text-align: center;
.circle{
background-color:#818181 ;
}
.circle{
position: relative;
right: 20rpx;
width: 52rpx;
height: 52rpx;
text-align: center;
line-height: 52rpx;
border-radius: 50%;
font-size: 24rpx;
display: flex;
align-items: center;
justify-content: center;
color: white;
image{
// width:30rpx ;
// height:20rpx ;
}
.ys{
background: url(@/static/yunshuzhong.png) no-repeat;
background-size: contain;
width:36rpx ;
height:28rpx ;
margin-left: 4rpx;
}
.ps{
background: url(@/static/paisong.png) no-repeat;
background-size: contain;
width:34rpx ;
height:32rpx ;
margin-left:2rpx;
}
}
.point{
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background-color:#818181 ;
margin-right: 40rpx;
}
.line{
height: 154rpx;
width: 2rpx;
border-left: 2rpx dashed #DFDFDF;
position: relative;
left: 4rpx;
}
.line.short{
height:120rpx ;
}
}
.logistics-orderInfo-right{
.status{
font-size: 32rpx;
color:#2A2929 ;
font-weight: bold;
margin-bottom: 6rpx;
}
.time{
margin-bottom: 6rpx;
}
.time,.desc{
font-size: 24rpx;
color:#818181 ;
}
// .desc .red{
// // font-weight: bold;
// color:#E63E32!important;
// }
}
}
}

View File

@@ -0,0 +1,385 @@
<!-- 去取件详情页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="detailBox">
<!-- 订单号 -->
<view class="boxBg">
<view class="tit">
<text>
<text>订单号SD{{ detailsData.orderId }}</text>
<icon @click="handleCopy" class="copy"></icon>
</text>
</view>
</view>
<!-- end -->
<!-- 取件信息 -->
<Address :detailsData="detailsData" class="pickupBox"></Address>
<!-- end -->
<!-- 物品信息 -->
<view class="boxBg">
<view class="tit">
<text>物品名称</text>
<view class="goodsSelect" @click="handleGoods" v-if="!isPickUp || (users.paymentMethod === 2 && !isCollect)">
<text class="textInfo">{{ detailsData.goodsType }}</text>
<icon class="nextIcon"></icon>
</view>
<view class="goodsSelect" v-else>
<text class="textInfo">{{ detailsData.goodsType }}</text>
</view>
</view>
</view>
<!-- end -->
<!-- 计算物品快递费 -->
<view class="boxBg">
<GoodsInfo ref="goods" :detailsData="detailsData" @getFreight="getFreight"></GoodsInfo>
<view class="freight">
<view>
总计金额
<text>
<text>*</text>
基础运费+增值服务费
</text>
</view>
<view>
<view v-if="!isPickUp || (users.paymentMethod === 2 && !isCollect)">
<input v-if="isFreigthEdit" type="number" v-model="freight" @blur="handleAmount" />
<text @click="handleFreight" v-else>{{ detailsData.freight }}</text>
<text></text>
</view>
<view v-else>
<text>{{ users.payData.tradingAmount }}</text>
<text></text>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 付款方式 -->
<view class="boxBg">
<view class="tit">
<text>付款方式</text>
<view class="goodsSelect" @click="handlePayMethod" v-if="!isPickUp || (users.paymentMethod === 2 && !isCollect)">
<text class="textInfo">{{ detailsData.paymentMethod === 1 ? '寄付' : '到付' }}</text>
<icon class="nextIcon"></icon>
</view>
<view class="goodsSelect" v-else>
<text class="textInfo">{{ detailsData.paymentMethod === 1 ? '寄付' : '到付' }}</text>
</view>
</view>
</view>
<!-- end -->
<!-- 备注 -->
<view class="boxBg">
<view class="tit">
<text>备注</text>
<view class="goodsSelect" @click="handleRemark" v-if="!isPickUp || (users.paymentMethod === 2 && !isCollect)">
<text class="textInfo">{{ detailsData.remark }}</text>
<icon class="nextIcon"></icon>
</view>
<view class="goodsSelect" v-else>
<text class="textInfo">{{ detailsData.remark }}</text>
</view>
</view>
</view>
<!-- end -->
<!-- 身份验证未验证 -->
<Authentication ref="card" :detailsData="detailsData"></Authentication>
<!-- end -->
<view class="btnBox">
<button v-if="isPickUp && users.paymentMethod === 1" class="btn-default uni-mini" @click="handleReceipt">去收款</button>
<button v-if="isCollect && isPickUp && users.paymentMethod === 2" class="btn-default uni-mini btn-forbid">已取件</button>
<button v-if="!isPickUp || (users.paymentMethod === 2 && !isCollect)" class="btn-default uni-mini" @click="handleSubmit">去取件</button>
</view>
<!-- 物品名称付款选择备注弹层 -->
<Uppop ref="method" @getGoodType="getGoodType" @getPayMethod="getPayMethod" @getRemark="getRemark" :detailsData="detailsData" :type="type"></Uppop>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted, watch, nextTick } from 'vue';
import { useStore } from 'vuex';
// 接口
import { getDetail, getPickup } from '@/pages/api/index.js';
// 导入组件
// 导航组件
import UniNav from '@/components/uni-nav/index.vue';
// 地址
import Address from './components/address.vue';
// 物品信息
import GoodsInfo from '@/components/uni-goods/index.vue';
// 身份认证
import Authentication from './components/authentication.vue';
// 付款方式先择、物品名称弹层
import Uppop from './components/uppop.vue';
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
// 定义ref 获取子组件方法或者值
const goods = ref(); //
const card = ref(); //
const method = ref(); //
const emit = defineEmits(''); //子组件向父组件事件传递
// 获取列表页传过来的id 有两种方法
// 第一种
// const pages = getCurrentPages(); //获取加载的页面获取当前页面路由信息uniapp 做安卓不支持 vue-router
// const currentPage = pages[pages.length - 1]; //获取当前页面的对象
// 第二种 用vuexs
const taskId = store.state.user.taskId; //用vuex获取列表页传过来的任务id
const title = ref('去取件'); //nav标题
let type = ref(1); //物品名称和付款方式公用一个弹层根据不同type值来做判断 物品:1,付款方式:2,备注:3
const detailsData = ref({}); //详情数据
let isFreigthEdit = ref(false);
let freight = ref(0); //金额
let isPickUp = ref(false); //是否去取件
let isCollect = ref(false); //到付的情况下,是否触发去取件后到,显示按钮为已取件
const stopClick = ref(true); //防止连续提交
// 监听修改金额数值,小数点后保留一位
watch(freight, (newValue, oldValue) => {
const val = Number(newValue);
// 最大输入99999最小输入1
nextTick(() => {
if (val < 99999 && val > 1) {
freight.value = parseInt(val * 100) / 100;
}
if (val > 99999) {
freight.value = 99999;
}
});
});
// ------生命周期------
onMounted(() => {
getDetails(taskId);
//
if (users.isPickUp) {
isPickUp.value = true;
} else {
isPickUp.value = false;
}
//
if (users.isCollect) {
isCollect.value = true;
} else {
isCollect.value = false;
}
});
// ------定义方法------
// 获取详情
const getDetails = async id => {
await getDetail(id).then(res => {
detailsData.value = res.data;
freight.value = detailsData.value.freight;
if (users.paymentMethod) {
if (users.paymentMethod === 1) {
detailsData.value.paymentMethod = 1;
} else {
detailsData.value.paymentMethod = 2;
}
}
goods.value.weight = Number(detailsData.value.weight);
goods.value.volume = Number(detailsData.value.volume);
// 设置当前是到付还是寄付
store.commit('user/setPaymentMethod', detailsData.value.paymentMethod);
store.commit('user/setDetailsData', res.data);
});
};
// 去取件
const handleSubmit = async () => {
if (stopClick.value) {
stopClick.value = false;
// 表单校验
const cards = card.value;
const good = goods.value;
// 未验证的身份证需要做校验
if (!cards.isValidate && detailsData.value.idCardNoVerify !== 1) {
stopClick.value = true;
return uni.showToast({
title: '请输入正确的身份证',
duration: 1000,
icon: 'none'
});
return false;
} else {
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
const details = detailsData.value;
// 要提交给后端的参数
if (freight.value !== 0) {
details.freight = freight.value;
}
const params = {
amount: good.freightData ? good.freightData : Number(details.freight), //总额
id: taskId, //任务id
goodName: details.goodsType, //物品名称
idCard: details.idCardNoVerify === 1 ? null : cards.idCard, //身份证号
name: details.idCardNoVerify === 1 ? null : cards.name, //真实姓名
payMethod: details.paymentMethod, //付款方式
remark: details.remark, //备注
volume: Number(good.volume), //体积
weight: good.weight //重量
};
// 存储信息,二维码支付页面要用
const payData = {
memo: details.remark,
productOrderNo: details.orderId,
tradingAmount: params.amount
};
store.commit('user/setPayData', payData);
await getPickup(params)
.then(res => {
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
// TODO先保留次代码后期需求可能有变更
// // const type = details.paymentMethod;
// // 跳转到取件成功页
// uni.redirectTo({
// url: '/pages/pay/index?type=' + type
// });
// store.commit('user/setIsPickUp', true);
}
setTimeout(()=>{
stopClick.value = true;
},3000)
})
.catch(err => {
return uni.showToast({
title: err.msg,
duration: 1000,
icon: 'none'
});
});
const type = details.paymentMethod;
// // 跳转到取件成功页
uni.redirectTo({
url: '/pages/pay/index?type=' + type
});
store.commit('user/setIsPickUp', true);
}
}
};
// 复制订单号
const handleCopy = () => {
uni.setClipboardData({
data: detailsData.value.orderId, // 要保存的内容
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none'
});
}
});
};
// 是否修改总计金额
const handleFreight = () => {
isFreigthEdit.value = true;
};
// 获取运费金额
const getFreight = val => {
detailsData.value.freight = val;
freight.value = detailsData.value.freight;
};
// 输入金额是否小于1
const handleAmount = () => {
nextTick(() => {
if (freight.value < 1) {
freight.value = 1;
}
});
};
// 物品
// 获取物品名称,获取子组件传的值
const getGoodType = val => {
detailsData.value.goodsType = val;
};
// 物品名称
const handleGoods = () => {
type.value = 1;
handleOpen();
};
// 付款方式
// 获取付款方式,获取子组件传的值
const getPayMethod = val => {
if (val === '寄付') {
detailsData.value.paymentMethod = 1;
} else {
detailsData.value.paymentMethod = 2;
}
store.commit('user/setPaymentMethod', detailsData.value.paymentMethod);
};
// 付款方式选择
const handlePayMethod = () => {
type.value = 2;
handleOpen();
};
// 备注
// 获取备注内容,获取子组件传的值
const getRemark = val => {
detailsData.value.remark = val;
};
// 打开弹层写备注
const handleRemark = () => {
if (users.isBack !== 'collect') {
type.value = 3;
handleOpen();
}
};
// 打开弹层
const handleOpen = () => {
method.value.dialogOpen();
};
// 返回上一页
const goBack = () => {
store.commit('user/setPaymentMethod', null);
store.commit('user/setCardData', null);
store.commit('user/setIsPickUp', false);
store.commit('user/setIsCollect', false);
if (users.newType === 301) {
uni.redirectTo({
url: '/pages/news/system?title=取件相关&type=301'
});
} else if (users.detailType === 1) {
// 如果是从历史取派的取件列表进入的,返回的时候进入到历史取派列表
store.commit('user/setTabIndex', 0);
uni.redirectTo({
url: '/pages/history/index'
});
} else if (users.isSearch) {
store.commit('user/setIsSearch', false);
uni.redirectTo({
url: '/pages/search/index'
});
} else {
store.commit('user/setTabIndex', 0);
uni.redirectTo({
url: '/pages/pickup/index'
});
}
};
// 去收款
const handleReceipt = () => {
store.commit('user/setPayData', {});
uni.redirectTo({
url: '/pages/pay/scanPay'
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,183 @@
<!-- 订单跟踪详情页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="content">
<view class="bottmBox">
<view class="orderList">
<view
class="logistics-orderInfo logistics-orderInfo-item"
:class="[index === 0 ? 'active' : '']"
:key="index"
v-for="(item, index) in markers.value"
>
<view class="logistics-orderInfo-left">
<view
class="circle gray"
v-if="['已拒收', '已签收', '已取件'].includes(item.status)"
>
{{
item.status === "已拒收"
? "拒"
: item.status === "已签收"
? "签"
: "取"
}}
</view>
<view
class="circle gray"
v-else-if="
(index === 0 && ['运送中', '派送中'].includes(item.status)) ||
(index > 0 && markers.value[index - 1].status !== '运送中')
"
>
<image :class="item.status === '派送中' ? 'ys' : 'ps'"></image>
</view>
<view
class="point"
v-else-if="
index > 0 && markers.value[index - 1].status === '运送中'
"
></view>
<view
class="line"
v-if="!(index === markers.value.length - 1)"
:class="item.status === '运送中' ? 'short' : ''"
></view>
</view>
<view class="logistics-orderInfo-right">
<view
class="status"
v-if="
!(
index > 0 &&
markers.value[index - 1].status === '运送中' &&
item.status === '运送中'
)
"
>{{ item.status }}</view
>
<view class="time">{{ item.created }}</view>
<view class="desc" v-html="strInit(item.info)"></view>
</view>
</view>
</view>
</view>
<!-- TODO此处代码保留 -->
<!-- <map class="mapBox" :latitude="latitude" :longitude="longitude" scale="6">
<cover-view class="bottmBox" v-if="markers.value">
<cover-view class="orderList" scroll-top='0'>
<cover-view class="logistics-orderInfo logistics-orderInfo-item" :class="[index===0?'active':'']" :key="index"
v-for='(item,index) in markers.value'>
<cover-view class="logistics-orderInfo-left">
<cover-view class="circle" v-if="['已拒收','已签收','已取件'].includes(item.status)">
<cover-view class="iconBg" ><cover-view class="fontPostion">{{item.status==='已拒收'?'拒':item.status==='已签收'?'签':'取'}}</cover-view></cover-view>
</cover-view>
<cover-view class="circle"
v-else-if="index ===0 &&['运送中','派送中'].includes(item.status) || index>0 && markers.value[index-1].status !=='运送中'">
<cover-view class="iconBg" ><cover-image :src="item.status==='派送中'?'../../static/yunshuzhong.png':'../../static/paisong.png'"></cover-image></cover-view>
</cover-view>
<cover-view class="point" v-else-if="index>0 && markers.value[index-1].status==='运送中'"></cover-view>
<cover-view class="line" v-if='!(index === markers.value.length - 1)'
:class="item.status==='运送中'?'short':''"></cover-view>
</cover-view>
<cover-view class="logistics-orderInfo-right">
<cover-view class="status"
v-if='!(index>0 && markers.value[index-1].status==="运送中" &&item.status==="运送中")'>
{{item.status}}
</cover-view>
<cover-view class="time">{{item.created}}</cover-view>
<cover-view class="desc"
:class="index === 0|| item.status === 23010?'active':''"
>{{item.info}}
</cover-view>
</cover-view>
</cover-view>
</cover-view>
</cover-view>
</map> -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useStore } from "vuex";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 导入接口
import { getTracks } from "@/pages/api/index.js";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user; //vuex获取、储存数据
const title = ref("订单跟踪"); //nav标题
const latitude = ref(39.91667); //维度
const longitude = ref(116.41667); //经度
// 起始位置
const markers = reactive([]);
// 路线点
const polyline = reactive([
// 第一条线
{
// 每个点的经纬度
points: [
{ longitude: 116.41667, latitude: 39.91667 },
{ longitude: 118.78333, latitude: 32.05 },
],
// 路线颜色
color: "#EF4F3F",
// 线条宽度
width: 12,
},
]);
// ------生命周期------
// ------定义方法------
onMounted(() => {
getTrack();
});
//将后端传来的字符串中的数字变为红色
const strInit = (value) => {
let strText = value;
let replaceText = [];
for (let i = 0; i <= 10; i++) {
replaceText.push("" + i);
}
// 后面
const str = value.split("【")[1];
// 转换成html形式解析
for (let i = 0; i < replaceText.length; i++) {
var replaceString = `<span class='red'>` + replaceText[i] + `</span>`;
strText = strText.replace(RegExp(replaceText[i], "g"), replaceString);
}
// 这里再把这个红色替换成你想要的颜色
// 由于在循环体里面数字会被替换所以用了一个单词red来当成初始色
strText = strText.replace(RegExp("red", "g"), "red");
return strText;
};
// 获取运单轨迹
const getTrack = async () => {
await getTracks(users.detailsData.transportOrderId).then((res) => {
if (res.code === 200) {
markers.value = res.data.reverse();
polyline[0].points = res.data.data;
}
});
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/details/waybill",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>
<style></style>

View File

@@ -0,0 +1,450 @@
<!-- 已取件已签收已取消详情页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="detailBox">
<!-- 运单号 -->
<view class="boxBg">
<view class="tit">
<text>
<!-- 当状态是去派送4\签收5的时候显示运单号 -->
<text v-if="taskStatus === 4 || taskStatus === 5">运单号{{ detailsData.transportOrderId }}</text>
<text v-else>订单号SD{{ detailsData.orderId }}</text>
<!-- end -->
<icon @click="handleCopy" class="copy"></icon>
</text>
</view>
</view>
<!-- end -->
<!-- 取件信息 -->
<Address :detailsData="detailsData" class="pickupBox"></Address>
<!-- end -->
<!-- 物品信息 -->
<view class="boxBg">
<view class="wayCon">
<view class="item">
物品名称
<text>{{ detailsData.goodsType }}</text>
</view>
<view class="item">
物品重量
<text>{{ detailsData.weight }}kg</text>
</view>
<view class="item">
物品体积
<text>{{ detailsData.volume }}</text>
</view>
<view class="item">
总计金额
<text>{{ detailsData.freight }}</text>
</view>
</view>
<view class="wayCon remark">
<view class="item">备注</view>
<view class="item">
<text>{{ detailsData.remark ? detailsData.remark : '暂无' }}</text>
</view>
</view>
<view class="wayCon">
<view class="item">
付款方式
<text>{{ detailsData.paymentMethod === 1 ? '寄付' : '到付' }}</text>
</view>
<!-- 当状态是已签收5,显示签收人 -->
<view class="item" v-if="taskStatus === 5">
签收人
<text>{{ detailsData.paymentMethod === 1 ? '本人' : '代收' }}</text>
</view>
<!-- end -->
</view>
</view>
<!-- end -->
<!-- 当状态是去派件4的时候显示签收人选择拒收签收按钮 -->
<view class="boxBg" v-if="detailsData.taskType === 2 && detailsData.status === 1">
<view class="tit">
<text>签收人</text>
<view class="goodsSelect" v-if="(isSign && detailsData.paymentMethod == 1) || (isPickUp && detailsData.paymentMethod === 2)">
<text class="textInfo">{{ detailsData.signRecipient === 1 ? '本人' : '代收' }}</text>
</view>
<view class="goodsSelect" @click="handleSignOpen" v-else>
<text class="textInfo">{{ detailsData.signRecipient === 1 ? '本人' : '代收' }}</text>
<icon class="nextIcon"></icon>
</view>
</view>
</view>
<!-- {{detailsData.status}}--{{users.isNew}}--{{taskStatus}} -->
<!-- <view
class="btnBox subBtnBox"
v-if="(detailsData.status===1&&!users.isNew)||(taskStatus === 4&&!users.isNew) || (taskStatus === 0 && users.taskType === 2) || (users.detailType === 2 && taskStatus === 4) || (detailsData.status===1&&users.detailType === 2 && taskStatus === 6)"
>
<button v-if="(!isSign && !isPickUp) || (isSign && !isPickUp && detailsData.paymentMethod === 2)" class="btn-default uni-sub-btn" @click="handleRejection(detailsData.id)">
拒收
</button>
<button v-if="!isPickUp" class="btn-default" @click="handleSign(detailsData.id)">签收</button>
<button v-if="isPickUp && detailsData.paymentMethod === 2" class="btn-default uni-mini" @click="handleReceipt">去收款</button>
<button v-if="isSign && detailsData.paymentMethod == 1" class="btn-default uni-mini btn-forbid">已签收</button>
</view> -->
<!-- end -->
<!-- 当状态是已取件2或者已签收5显示跟踪按钮 ||(taskStatus === 6&&users.taskType===2)-->
<!-- <view class="btnBox" v-if="(detailsData.status===2&&users.isNew)||(taskStatus === 2&&users.isNew) || taskStatus === 5 || (users.detailType === 2 && taskStatus === 6 &&users.isNew) || (users.detailType === 1 && taskStatus === 6)">
<button class="btn-default uni-mini" @click="handleOrder">订单跟踪</button>
</view> -->
<!-- end -->
<!-- 付款方式paymentMethod1寄付2到付 -->
<!-- 付款状态paymentStatus1未付2已付 -->
<!-- 签收状态signStatus1为已签收2为拒收 -->
<!-- 任务类型taskType1为取件任务2为派件任务 -->
<!-- 任务状态status1未取派2完成3取消 未派件的情况下显示的按钮 -->
<!-- 派件 -->
<view v-if="detailsData.taskType === 2">
<!-- 未派件未签收-->
<view class="btnBox subBtnBox" v-if="detailsData.status === 1">
<button class="btn-default uni-sub-btn" v-if="detailsData.signStatus !== 1" @click="handleRejection(detailsData.id)">拒收</button>
<button class="btn-default" v-if="detailsData.signStatus !== 1" @click="handleSign(detailsData.id)">签收</button>
</view>
<!-- end -->
<!-- 已经派件未付款或者已经签收 -->
<view class="btnBox subBtnBox" v-else>
<!-- 签收后未付款isPickUp代表未收款进入收款页返回时候的显示去收款按钮 -->
<!-- 已签收到的订单付但是未付款 应该显示去收款-->
<button
v-if="isPickUp && detailsData.paymentStatus === 1 && detailsData.paymentMethod === 2 && detailsData.signStatus === 1"
class="btn-default uni-mini"
@click="handleReceipt"
>
去收款
</button>
<!-- 签收状态是已签收显示已签收按钮 -->
<!-- isSign代表已经点击了签收进入到了派件成功页返回的时候要显示已经签收 -->
<button v-if="isSign && detailsData.signStatus === 1" class="btn-default uni-mini btn-forbid">已签收</button>
<!-- 当状态是已签收显示跟踪按钮-->
<!-- 已派件 -->
<view v-if="detailsData.status === 2" class="btnBox">
<!-- 未付款从消息签收提醒 -->
<button
v-if="
(!isPickUp && !isSign && detailsData.paymentMethod === 1) ||
users.isNew ||
(!isPickUp && !isSign && detailsData.paymentMethod === 2 && detailsData.paymentStatus === 1) ||
(!isPickUp && !isSign && detailsData.paymentMethod === 2 && detailsData.paymentStatus === 2 && detailsData.signStatus == 1)
"
class="btn-default uni-mini"
@click="handleOrder"
>
订单跟踪
</button>
</view>
</view>
<!-- end -->
<!-- end -->
</view>
<!-- 取件 -->
<view v-else>
<!-- 当状态是已取件显示跟踪按钮-->
<view class="btnBox" v-if="detailsData.status === 2"><button class="btn-default uni-mini" @click="handleOrder">订单跟踪</button></view>
<!-- end -->
</view>
<!-- 物品名称、付款选择、备注弹层 -->
<Uppop ref="sign" @getSignType="getSignType" :type="type"></Uppop>
<!-- end -->
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
// 接口
import { getDetail, rejection, tasksSign, PositionUpload } from '@/pages/api/index.js';
import { positionUploadHandle } from '@/utils/index.js';
// 导入组件
// 导航组件
import UniNav from '@/components/uni-nav/index.vue';
// 地址
import Address from './components/address.vue';
// 付款方式先择、物品名称弹层
import Uppop from './components/uppop.vue';
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user; //vuex获取、储存数据
const taskStatus = users.taskStatus; //获取列表页传过来的取件类型 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
const emit = defineEmits(''); //子组件向父组件事件传递
const sign = ref(); // 定义ref 获取子组件方法或者值
const taskId = users.taskId;
const title = users.taskStatus === 4 || (taskStatus === 0 && users.taskType === 2) ? '去派件' : '运单详情'; //nav标题
let detailsData = ref({}); //详情数据
let type = ref(0); //物品名称、付款方式、签收人公用一个弹层根据不同type值来做判断 物品:1,付款方式:2,备注:3,签收人:4
let isPickUp = ref(false); //是否去签收
let isCollect = ref(false); //到付的情况下,是否触发去取件后到,显示按钮为已取件
let isSign = ref(false); //是否已签收
const stopClick = ref(false); //防止连续提交
// ------生命周期------
onMounted(() => {
getDetails(taskId);
if (users.isPickUp) {
isPickUp.value = true;
} else {
isPickUp.value = false;
}
//
if (users.isSign) {
isSign.value = true;
} else {
isSign.value = false;
}
});
// ------定义方法------
// 获取详情
const getDetails = async id => {
await getDetail(id).then(res => {
detailsData.value = res.data;
// 设置当前是到付还是寄付
store.commit('user/setPaymentMethod', detailsData.value.paymentMethod);
store.commit('user/setDetailsData', res.data);
});
};
// 拒收
const handleRejection = async id => {
if (stopClick.value) {
return;
}
stopClick.value = true;
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
await rejection(id)
.then(res => {
if (res.code === 200) {
// 拒收之后上报位置
positionUploadHandle(true);
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
let timeId = setTimeout(() => {
// 如果是从全部派送进入到详情,拒收后要跳转到全部取派的 全部派件tab值为1
if (taskStatus === 6 && users.detailType === 2) {
store.commit('user/setTabIndex', 1);
uni.redirectTo({
url: '/pages/history/index'
});
} else {
// 如果是派件列表tab是0
store.commit('user/setTabIndex', 0);
uni.redirectTo({
url: '/pages/delivery/index'
});
}
}, 1000);
uni.showToast({
title: '用户拒收',
icon: 'none',
duration: '1000'
});
}
stopClick.value = false;
})
.catch(err => {
uni.showToast({
title: err.msg,
icon: 'none',
duration: '1000'
});
});
};
// 签收
const handleSign = async id => {
if (stopClick.value) {
return;
}
stopClick.value = true;
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
const params = {
id: id,
signRecipient: detailsData.value.signRecipient
};
// 跳转到签收成功页
await tasksSign(params).then(res => {
if (res.code === 200) {
// 签收之后上报位置
positionUploadHandle(true);
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
const type = detailsData.value.paymentMethod; //获取付款类型,根据付款类型来判断是否要进入付款页面
// 跳转到签收成功页
uni.redirectTo({
url: '/pages/pay/index?type=' + type
});
store.commit('user/setIsPickUp', true);
store.commit('user/setIsDelivery', true); //因为取件和派件用的是一个公用页面所以用isDelivery来判断是不是从派件进到签收成功页面的
}
stopClick.value = false;
});
};
// 复制订单号
const handleCopy = () => {
uni.setClipboardData({
data: detailsData.value.orderId, // 要保存的内容
success: function() {
uni.showToast({
title: '复制成功',
icon: 'none'
});
}
});
};
// 获取签收人名称,获取子组件传的值
const getSignType = val => {
detailsData.value.signRecipient = val;
};
// 签收人
const handleSignOpen = () => {
type.value = 4;
sign.value.dialogOpen();
};
// 订单追踪
const handleOrder = () => {
uni.redirectTo({
url: '/pages/details/orderMap'
});
};
// 返回上一页
const goBack = () => {
store.commit('user/setIsPickUp', false);
store.commit('user/setIsSign', false);
store.commit('user/setIsDelivery', false);
// 根据不同的状态跳转到不同的页面
// 待取件、已取件、已取消
if (taskStatus === 1 || taskStatus === 2 || taskStatus === 3) {
if (taskStatus === 1) {
// 待取件
store.commit('user/setTabIndex', 0);
} else if (taskStatus === 2) {
// 已取件
store.commit('user/setTabIndex', 1);
} else {
// 已取消
store.commit('user/setTabIndex', 2);
}
// 如果是从搜索列表进来的,返回的时候要返回到搜索列表
if (users.isSearch) {
store.commit('user/setIsSearch', false);
uni.redirectTo({
url: '/pages/search/index'
});
} else {
// 如果是从待取件、已取件、已取消列表进来的返回的时候要返回相对应的tab页
uni.redirectTo({
url: '/pages/pickup/index'
});
}
} else if ((taskStatus === 5 && users.newType !== 302 && !users.isNew) || (taskStatus === 4 && users.detailType !== 2 && users.newType !== 304)) {
if (taskStatus === 4) {
store.commit('user/setTabIndex', 0);
} else {
store.commit('user/setTabIndex', 1);
}
if (users.isSearch) {
store.commit('user/setIsSearch', false);
uni.redirectTo({
url: '/pages/search/index'
});
} else {
uni.redirectTo({
url: '/pages/delivery/index'
});
}
} else if (
((taskStatus === 6 || taskStatus === 4) && users.detailType === 2) ||
(users.detailType === 1 && users.newType !== 302) ||
(taskStatus === 6 && users.detailType === 1)
) {
// 从历史记录派件进入到详情,返回的时候返回历史记录
if (taskStatus === 6 && users.detailType === 1) {
store.commit('user/setTabIndex', 0);
}
if (taskStatus === 6 && users.detailType === 2) {
store.commit('user/setTabIndex', 1);
}
uni.redirectTo({
url: '/pages/history/index'
});
// 从派件列表进入详情页
if (taskStatus === 4 && users.detailType === 2) {
store.commit('user/setTabIndex', 1);
if (users.isSearch) {
store.commit('user/setIsSearch', false);
uni.redirectTo({
url: '/pages/search/index'
});
} else {
uni.redirectTo({
url: '/pages/history/index'
});
}
}
if (taskStatus === 4 && users.detailType === 1) {
store.commit('user/setTabIndex', 0);
uni.redirectTo({
url: '/pages/delivery/index'
});
}
} else if (users.newType === 301) {
// 跳转到消息寄件相关
uni.redirectTo({
url: '/pages/news/system?title=取件相关&type=301'
});
} else if (users.newType === 302) {
// 跳转到消息寄件相关
uni.redirectTo({
url: '/pages/news/system?title=签收提醒&type=302'
});
} else if (users.newType === 303) {
// 跳转到消息快件取消
uni.redirectTo({
url: '/pages/news/system?title=快件取消&type=303'
});
} else if (users.newType === 304) {
// 跳转到消息派件相关
uni.redirectTo({
url: '/pages/news/system?title=派件相关&type=304'
});
} else {
store.commit('user/setTabIndex', 0);
uni.redirectTo({
url: '/pages/delivery/index'
});
}
store.commit('user/setIsNew', false);
};
// 去收款
const handleReceipt = () => {
store.commit('user/setPayData', {});
uni.redirectTo({
url: '/pages/pay/scanPay'
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,76 @@
<!--收寄地址-->
<template>
<view class="boxBg">
<view class="addressCon">
<view class="item" @click="handleDate(1)">
<view class="sendIcon"></view>
<view class="address">
<view :class="mailCity.province ? 'active' : ''">
<view v-if="!mailCity.province">请选择寄件城市</view>
<view v-else>
<text>{{ mailCity.province }}</text>
<text>{{ mailCity.city }}</text>
<text>{{ mailCity.area }}</text>
</view>
</view>
<icon class="nextIcon"></icon>
</view>
<view class="line"></view>
</view>
<view class="item" @click="handleDate(2)">
<view class="receiveIcon"></view>
<view class="address">
<view :class="consigneeCity.province ? 'active' : ''">
<view v-if="!consigneeCity.province">请选择收件城市</view>
<view v-else>
<text>{{ consigneeCity.province }}</text>
<text>{{ consigneeCity.city }}</text>
<text>{{ consigneeCity.area }}</text>
</view>
</view>
<icon class="nextIcon"></icon>
</view>
</view>
</view>
<CityPopup ref="city" :type="type" @getCity="getCity"></CityPopup>
</view>
</template>
<script setup>
import { ref } from "vue";
// 接口pai
// 导入组件
// 城市弹层
import CityPopup from "@/components/uni-address/index.vue";
// ------定义变量------
const city = ref(); //定义子组件ref获取子组件方法
const emit = defineEmits();
let type = ref(null); //触发的寄件还是收件type=1 寄件;type=2 收件
let mailCity = ref({}); //寄件数据
let consigneeCity = ref({}); //收件数据
// ------定义方法------
// 地址弹层弹层
const handleDate = (val) => {
// type代表触发的寄件还是收件type=1 寄件;type=2 收件
type.value = val;
city.value.handleOpen();
};
// 获取区县
const getCity = (obj) => {
if (type.value === 1) {
mailCity.value = obj;
} else {
consigneeCity.value = obj;
}
if (mailCity.value.areaId && consigneeCity.value.areaId) {
emit("handleCity", true);
}
};
//把数据、方法暴漏给父组件
defineExpose({
mailCity,
consigneeCity,
});
</script>
<style></style>

View File

@@ -0,0 +1,290 @@
<!--重量体积计算-->
<template>
<view class="boxBg">
<view class="goodsCon">
<view class="item">
<text>预估重量</text>
<view class="bg goodInfo">
<view
class="symbol"
:class="isLessThan ? 'active' : ''"
@click="handleMinus"
>-</view
>
<view class="num">
<input
class="uni-input"
type="number"
maxlength="6"
v-model="weight"
@blur="handleSymbol"
/>
<text>kg</text>
</view>
<view
class="symbol"
:class="isExceed ? 'active' : ''"
@click="handleAdd"
>+</view
>
</view>
</view>
<view class="item">
<text>总体积</text>
<view class="bg goodInfo">
<!-- 暂时去除 :class="isLessThanVolume ? 'active' : ''" -->
<view class="symbol" @click="handleVolumeMinus">-</view>
<view class="num">
<input
class="uni-input"
type="number"
maxlength="6"
v-model="volume"
@blur="handleVolume"
/>
<text></text>
</view>
<!-- 暂时去除 :class="isExceedVolume ? 'active' : ''" -->
<view class="symbol" @click="handleVolumeAdd">+</view>
</view>
</view>
<view class="item calculate">
<view class="bg">
<input
class="uni-input"
type="number"
maxlength="3"
v-model="measureLong"
placeholder="长"
@input="handleCalculate"
/>
<text :class="measureLong ? 'active' : ''">cm</text>
</view>
<text>*</text>
<view class="bg">
<input
class="uni-input"
type="number"
maxlength="3"
v-model="measureWidth"
placeholder="宽"
@input="handleCalculate"
/>
<text :class="measureWidth ? 'active' : ''">cm</text>
</view>
<text>*</text>
<view class="bg">
<input
class="uni-input"
type="number"
maxlength="3"
v-model="measureHigh"
placeholder="高"
@input="handleCalculate"
/>
<text :class="measureHigh ? 'active' : ''">cm</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, watch, nextTick } from "vue";
// ------定义变量------
let weight = ref(1); //重量
let volume = ref(0); //体积
let measureLong = ref(null); //长
let measureWidth = ref(null); //宽
let measureHigh = ref(null); //高
let isLessThan = ref(true); //判断重量是否小于0.1
let isExceed = ref(false); //判断重量是否大于9999
let isLessThanVolume = ref(true); //判断体积是否小于0.0001m³
let isExceedVolume = ref(false); //判断体积是否大于99m³
// 暴漏给父组件
defineExpose({
weight,
volume,
measureLong,
measureWidth,
measureHigh,
});
// ------生命周期------
// 监听重量数值,小数点后保留一位
watch(weight, (newValue, oldValue) => {
const val = Number(newValue);
nextTick(() => {
// 数值小于0.1并且大于0 数值默认为1
if (val < 0.1 && val > 0) {
weight.value = 1;
}
// 处理小数点小数点保留1位
if (val > 0.1) {
weight.value = parseInt(val * 10) / 10;
}
// 数值小于等于1 左侧按钮置灰
if (val <= 1) {
isLessThan.value = true; //左侧减号置灰
} else {
isLessThan.value = false; //左侧减号去除置灰
if (val >= 9999) {
weight.value = 9999;
isExceed.value = true; //右侧加号置灰
} else {
isExceed.value = false; //右侧加号去除置灰
}
}
});
});
// 监听长,取整
watch(measureLong, (newValue, oldValue) => {
const val = Number(newValue);
nextTick(() => {
measureLong.value = Math.floor(val);
if (newValue <= 0) {
measureLong.value = null;
}
});
});
// 监听宽,取整
watch(measureWidth, (newValue, oldValue) => {
const val = Number(newValue);
nextTick(() => {
measureWidth.value = Math.floor(val);
if (newValue <= 0) {
measureWidth.value = null;
}
});
});
// 监听高,取整
watch(measureHigh, (newValue, oldValue) => {
const val = Number(newValue);
nextTick(() => {
measureHigh.value = Math.floor(val);
if (newValue <= 0) {
measureHigh.value = null;
}
});
});
// ------定义方法------
//触发重量输入如果输入0自动判断为1kg,最小可输入值为0.1kg,最大值为9999kg
const handleSymbol = (e) => {
const value = e.detail.value;
if (value < 0.1) {
weight.value = 1;
isLessThan.value = true; //左侧减号置灰
} else {
if (value > 0.1 && value <= 1) {
isLessThan.value = true;
} else {
isLessThan.value = false; //左侧减号去除置灰
}
if (value >= 9999) {
isExceed.value = true; //右侧加号置灰
weight.value = 9999;
} else {
isExceed.value = false; //右侧加号去除置灰
}
}
};
// 减重量
const handleMinus = () => {
// 重量减去1
if (weight.value > 1) {
weight.value--;
isExceed.value = false; //右侧加号去除置灰
weight.value = weight.value.toFixed(1);
}
if (weight.value <= 0) {
weight.value = 1;
isLessThan.value = true; //左侧减号置灰
}
};
// 加重量
const handleAdd = () => {
// 重量加1
if (weight.value < 9999) {
++weight.value;
isLessThan.value = false; //左侧减号去除置灰
}
if (weight.value === 9999) {
isExceed.value = true; //右侧加号置灰
}
};
// 体积
const handleVolume = (e) => {
const value = Number(e.detail.value);
if (value < 0.0001) {
volume.value = 0;
} else {
if (value > 99) {
volume.value = 99;
return uni.showToast({
title: "体积最大可不能超过99m³",
duration: 1000,
icon: "none",
});
}
}
};
// 减体积
const handleVolumeMinus = () => {
// 体积减去1
if (volume.value > 1) {
volume.value--;
volume.value = parseInt(volume.value * 10000) / 10000;
}
// 体积
if (volume.value <= 0 || volume.value === 1) {
volume.value = 0;
}
};
// 加体积
const handleVolumeAdd = () => {
// 体积加1
if (volume.value < 99) {
++volume.value;
isLessThanVolume.value = false; //左侧减号去除置灰
}
if (volume.value === 99) {
isExceedVolume.value = true; //右侧加号置灰
}
};
// 计算立方米
const handleCalculate = () => {
const long = measureLong.value; //长
const wide = measureWidth.value; //宽
const height = measureHigh.value; //高
// 长宽高都大于1才可以计算
if (long >= 1 && wide >= 1 && height >= 1) {
nextTick(() => {
// 计算立方米:长/100*宽/100*高/100
let val = (long / 100) * (wide / 100) * (height / 100);
// 立方米必须大于0.0001
if (val < 0.0001) {
volume.value = 0;
} else if (val > 99) {
// 小数点后保留四位小数
isExceedVolume.value = true; //右侧加号置灰
volume.value = 99;
return uni.showToast({
title: "体积最大可不能超过99m³",
duration: 1000,
icon: "none",
});
} else {
volume.value = parseInt(val * 10000) / 10000;
// 如果体积大于1左侧减号按钮去除置灰
if (val > 1) {
isLessThanVolume.value = false; //左侧减号去除置灰
} else {
isLessThanVolume.value = true; //左侧减号置灰
}
isExceedVolume.value = false; //右侧加号去除置灰
}
});
}
};
</script>

View File

@@ -0,0 +1,23 @@
<!--重量体积查询结果-->
<template>
<view class="boxBg result">
<view>计费重量{{ baseData.weight }} kg</view>
<view>计费体积{{ baseData.volumeValue }} </view>
<view
>首重1.0kg{{ baseData.firstWeight }}续重{{
baseData.continuousWeight
}}/kg</view
>
<view class="price"><text></text>{{ baseData.freight }}</view>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
baseData: {
type: Object,
default: () => ({}),
},
});
</script>

View File

@@ -0,0 +1,88 @@
body,
uni-page-body {
background: var(--neutral-color-background) !important;
}
.freightBox {
.boxBg {
box-shadow: none;
margin-top: 36rpx;
padding: 0 32rpx;
}
.btnBox{
.btn-default{
width: 400rpx;
}
}
::v-deep .result{
padding: 36rpx 26rpx 16rpx;
line-height: 42rpx;
margin-top: 0;
position: relative;
view{
padding-bottom: 14rpx;
&:nth-child(3){
color: var(--neutral-color-font);
font-weight: normal;
font-size: var(--font-size-12);
}
}
.price{
position: absolute;
right: 40rpx;
top: 50%;
transform: translate(0, -50%);
font-size: 40rpx;
color: var(--essential-color-red) !important;
line-height: 42rpx;
display: flex;
align-items: center;
text{
font-size: var(--font-size-12);
font-weight: normal;
}
}
}
}
::v-deep .addressCon {
.item {
height: 138rpx;
line-height: 138rpx;
display: flex;
align-items: center;
&:first-child {
.address {
border-bottom: 1px solid var(--neutral-color-background);
}
}
.address {
flex: 1;
display: flex;
align-items: center;
&>view {
flex: 1;
color: var(--neutral-color-font);
&.active {
color: var(--neutral-color-main);
font-weight: 600;
font-size: var(--font-size-16);
text{
padding-right: 20rpx;
}
}
}
}
}
}
::v-deep .goodsCon {
padding: 8rpx 0 32rpx;
font-size: var(--font-size-13);
font-weight: 600;
.item{
& > text {
width: 104rpx;
}
}
}

View File

@@ -0,0 +1,114 @@
<!-- 运费查询页 -->
<template>
<!-- 自定义头部 -->
<view class="navHead"><UniNav :title="title" @goBack="goBack"></UniNav></view>
<!-- end -->
<!-- 列表 -->
<view class="pageBox freightBox">
<!-- 地址 -->
<UniAddress ref="address" @handleCity="handleCity"></UniAddress>
<!-- end -->
<!-- 重量体积计算 -->
<view class="boxBg">
<GoodsInfo ref="goods"></GoodsInfo>
</view>
<!-- end -->
<!-- 查询按钮 -->
<view class="btnBox"
><button
class="btn-default"
:class="isCityId ? '' : 'btn-forbid'"
@click="handleSubmit"
>
立即查询
</button></view
>
<!-- end -->
<!-- 查询结果 -->
<Result :baseData="baseData.value" v-if="isShow"></Result>
<!-- end -->
</view>
<!-- end -->
</template>
<script setup>
import { ref, reactive } from "vue";
// 接口 api
import { calculateFreight } from "@/pages/api/freight.js";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 地址选择组件
import UniAddress from "./components/address.vue";
// 物品信息组件
import GoodsInfo from "@/components/uni-goods/index.vue";
// 查询结果
import Result from "./components/result.vue";
// ------定义变量------
const title = ref("运费查询");
const isShow = ref(false);//是否显示查询结果
let baseData = reactive({});//储存数据
const goods = ref(); //定义子组件的ref,可以调取子组件的值
const address = ref(); //定义子组件的ref,可以调取子组件的值
const isCityId = ref(false);
// ------定义方法------
// 立即查询
const handleSubmit = async () => {
// 显示查询结果
const senderCountyId = address.value.mailCity.areaId; // 寄件id
const receiverCountyId = address.value.consigneeCity.areaId; // 收件id
const goodData = goods.value;
if (!senderCountyId) {
return uni.showToast({
title: "请选择寄件城市",
duration: 1000,
icon: "none",
});
}
if (!receiverCountyId) {
return uni.showToast({
title: "请选择收件城市",
duration: 1000,
icon: "none",
});
}
isShow.value = true;
let data = {
senderCountyId: senderCountyId,
receiverCountyId: receiverCountyId,
volume: goodData.volume === 0 ? 1 : goodData.volume * 1000000,
weight: goodData.weight,
measureLong: goodData.measureLong,
measureWidth: goodData.measureWidth,
measureHigh: goodData.measureHigh,
};
const res = await calculateFreight(data);
if (res.code === 200) {
baseData.value = {
volumeValue: goodData.volume,
...res.data,
};
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: "none",
});
}
};
// 是否选择了寄件地址和收件地址
const handleCity = (val) => {
isCityId.value = val;
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/index/index",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,181 @@
<template>
<view class="dateBox uniPopup">
<view class="item">
<!-- 由于需求改动这块日期展示效果先不用 -->
<!-- <view class="date" @change="handleDate">
<icon class="dateIcon"></icon>
<text>{{ getNow(dates) }}</text>
<icon class="next"></icon>
</view> -->
<uni-datetime-picker type="date" :clear-icon="false" @change="handleDate">
<view class="date">
<icon class="dateIcon"></icon>
<text>{{ dates }}</text>
<icon class="next"></icon>
</view>
</uni-datetime-picker>
</view>
<view class="item" :class="isTmClick ? 'red' : ''" @click="hanleDay(0)">
<text :class="!isToday ? 'gray' : ''">明天</text>
</view>
<view class="item" :class="isAtClick ? 'red' : ''" @click="hanleDay(1)">
<text :class="!isToday ? 'gray' : ''">后天</text>
</view>
<!-- 由于需求改动这块日期展示效果先不用 -->
<!-- <view class="datePopupBox">
<uni-popup ref="popup" type="bottom" background-color="#fff">
<view class="popup-content">
<view class="tit">
<view @click="handleCancel('bottom')">取消</view>
<view>选择开始日期</view>
<view @click="handleComplete">完成</view>
</view>
<view class="date-select">
<picker-view class="picker-view" :value="defaultValue" :indicator-style="indicatorStyle" @change="bindChange">
<picker-view-column>
<view class="item" v-for="(item, index) in monthData.value" :key="index">
<text @click="handleGetNow(index)">{{ getNow(item) }}</text>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</uni-popup>
</view> -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted, watch } from 'vue';
import { useStore } from 'vuex';
// 获取封装好的方法
import { getTate, getNow, getDay, afterTomorrowDay, tomorrowDay, getMonthDay } from '@/utils/index.js';
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits();
const popup = ref(); //定义弹层ref
let dates = ref(); //当前日期
let isPreExceed = ref(false); //触发前一天是否超过30天
let isNextExceed = ref(false); //触发后一天是否超过30天
const monthData = reactive([]); //时间数组
const indicatorStyle = ref(`height: 100rpx;`);
let defaultValue = ref([0]); //时间默认选择为数组第一个值
let times = ref(null); //触发的事件选择
let dateTime = ref(null); //时间选择筛选
// let isTomorrow = ref(false); //是否触发了明天、后台按钮,用来判断日期弹层是否要显示当前日期,如果触发了今天、后天,那么弹层显示的时候应该显示当前的日期
// 监听触发前一天、后一天按钮向前后者向后超过当前日期30天的日期按钮置灰
let isToday = ref(true); //如果是当天,明天后天可以触发
// let dayArr = reactive(['明天', '后天']);
let isActive = ref(null); //定义明后天的当前样式
const isTmClick = ref(false); //触发明天
const isAtClick = ref(false);//触发后天
watch(dates, (newValue, oldValue) => {
isToday.value = getNow(newValue) === getNow(new Date());
const obj = getMonthDay(newValue);
// 前一天按钮置灰
if (obj.timeNow === obj.timeStar) {
isPreExceed.value = true;
}
// 后一天按钮置灰
if (obj.timeNow === obj.timeEnd) {
isNextExceed.value = true;
}
// 弹层选择的时间和当前时间显示一致
monthData.value.map((val, index) => {
if (getTate(obj.timeNow) === val) {
defaultValue.value = [index];
}
});
// 传递当前筛选的时间
if (users.timeData) {
emit('getDateTime', getTate(users.timeData));
dates.value = users.timeData;
} else {
emit('getDateTime', getTate(obj.timeNow));
}
});
// ------生命周期------
onMounted(() => {
dates.value = getTate(new Date()); //获取当前日期
monthData.value = getDay(); //获取当月的所有日期
});
// ------定义方法------
// 触发今天、明天
const hanleDay = index => {
if (isToday.value) {
if (index === 0) {
isAtClick.value = false
isTmClick.value =!isTmClick.value
store.commit('user/setTimeData', tomorrowDay());
emit('getDateTime', getTate(tomorrowDay()));
if (!isTmClick.value) {
store.commit('user/setTimeData', getTate(new Date()));
emit('getDateTime', getTate(getTate(new Date())));
}
// isTomorrow.value = true;
} else {
isTmClick.value = false
isAtClick.value =!isAtClick.value
store.commit('user/setTimeData', afterTomorrowDay());
emit('getDateTime', getTate(afterTomorrowDay()));
if (!isAtClick.value) {
store.commit('user/setTimeData', getTate(new Date()));
emit('getDateTime', getTate(getTate(new Date())));
}
// isTomorrow.value = true;
}
}
};
// 时间弹层
const handleDate = type => {
times.value = type;
handleComplete();
// // 由于需求改动,以日历形式展现,以下先不用
// if (isTomorrow.value) {
// monthData.value.map((val, index) => {
// if (getTate(new Date()) === val) {
// defaultValue.value = [index];
// }
// });
// }
// isTomorrow.value = false;
// popup.value.open(type);
};
// // 由于需求改动,以日历形式展现,以下先不用
// // 选择时间
// const bindChange = e => {
// // 时间筛选
// times.value = monthData.value[e.detail.value[0]];
// };
// 完成
const handleComplete = () => {
// 清除明后天当前样式
isActive.value = null;
if (times.value === dates.value) {
isToday.value = true;
} else {
isToday.value = false;
}
if (times.value !== null) {
dates.value = times.value;
store.commit('user/setTimeData', times.value);
handleCancel();
} else {
uni.showToast({
title: '请选择日期',
icon: 'none'
});
}
};
// // 由于需求改动,以日历形式展现,以下先不用
// const handleGetNow = num => {
// times.value = monthData.value[num];
// };
// 关闭弹层
const handleCancel = () => {
popup.value.close();
};
</script>

View File

@@ -0,0 +1,131 @@
<template>
<!-- 列表内容 -->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<view class="boxBg">
<view class="tabList">
<view class="item" @click.stop="handleDetails($event, item)">
<view v-if="item.status !== 1" class="history">
<view class="titInfo" v-if="item.transportOrderId !== null"
>运单号{{ item.transportOrderId }}</view
>
<view class="address">收件人{{ item.name }}</view>
<view class="address">派件地址{{ item.address }}</view>
<view class="address">签收时间{{ item.actualEndTime }}</view>
<view class="time" v-if="item.status === 2"
>运费{{ item.amount }}</view
>
</view>
<view v-else class="history">
<view class="titInfo">
<view>
<text class="name">{{ item.name }}</text>
{{ item.phone }}
<!-- TODO拨打电话和发信息小图标暂时保留 -->
<!-- <icon class="phone" @click.stop="handlePhone($event, item.phone)"></icon>
<icon class="note" @click.stop="handleNote"></icon> -->
</view>
</view>
<view class="address">{{ item.address }}</view>
<view class="address">{{ item.distance }}公里</view>
<view class="time" v-if="item.transportOrderId !== null"
>运单号{{ item.transportOrderId }}</view
>
</view>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="item.status === 1"
><button class="uni-btn btn-default">去派件</button></text
>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-else-if="
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 2 &&
item.signStatus !== 2
"
><button class="uni-btn btn-default">去收款</button></text
>
<text @click.stop="handleOpen($event, item.id)" class="delete" v-else
><button class="uni-btn concelBtn">删除</button></text
>
</view>
</view>
</view>
</view>
<!-- end -->
</template>
<script setup>
import { useStore } from "vuex";
// 获取父组件数据
const props = defineProps({
// 数据
itemData: {
type: Array,
default: () => [],
},
// 当前触发的tab值
tabIndex: {
type: Number,
default: 0,
},
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(""); //子组件向父组件事件传递
// ------定义方法------
// 删除弹层
const handleOpen = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
emit("handleOpen", id);
};
// 去派件详情
const handleDetails = (e, item) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit("user/setTaskId", item.id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit("user/setTaskType", 2);
store.commit("user/setDetailType", 2); //从历史订单他跳入订单详情
// 进入详情页
if (item.status === 1) {
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit("user/setTaskStatus", 6);
uni.redirectTo({
url: "/pages/details/waybill",
});
} else if (
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 2 &&
item.signStatus !== 2
) {
store.commit("user/setIsDelivery", true);
store.commit("user/setTaskStatus", 6);
// 未付款进入付款二维码页面
store.commit("user/setPayData", {});
uni.redirectTo({
url: "/pages/pay/scanPay",
});
} else {
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit("user/setTaskStatus", 6);
// 已经完成的进入订单详情页
uni.redirectTo({
url: "/pages/details/waybill",
});
}
};
</script>

View File

@@ -0,0 +1,208 @@
<template>
<view class="pageBox">
<!-- tab切换 -->
<UniTab
:tabBars="tabBars"
ref="tab"
@getTabIndex="getTabIndex"
class="historyTab"
></UniTab>
<!-- end -->
<view class="homeSwiper historyboxTop">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 取件 -->
<view v-if="tabIndex === 0"
><Pickup :itemData="itemData" @handleOpen="handleOpen"></Pickup
></view>
<!-- end -->
<!-- 派件 -->
<view v-else
><Delivery :itemData="itemData" @handleOpen="handleOpen"></Delivery
></view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
<!-- 空页面 -->
</view>
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
</view>
<!-- end -->
</view>
<!-- 提示窗 -->
<UniPopup
ref="popup"
:tipInfo="tipInfo"
@handleClick="handleClick"
></UniPopup>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted, watch } from "vue";
import { onReachBottom } from "@dcloudio/uni-app";
import { useStore } from "vuex";
import { getTimeDate} from '@/utils/index.js';
// 基本数据
import { HistoryTabData } from "@/utils/commonData.js";
//接口
import { getDeliveryList, taskDelete } from "@/pages/api/index.js";
// tab切换
import UniTab from "@/components/uni-tab/index.vue";
// 弹层
import UniPopup from "@/components/uni-popup/index.vue";
// 下拉提示
import ReachBottom from "@/components/reach-bottom/index.vue";
//空页面
import EmptyPage from "@/components/uni-empty-page/index.vue";
// 取件
import Pickup from "./pickup.vue";
// 派件
import Delivery from "./delivery.vue";
// 获取父组件数据
const props = defineProps({
// 筛选时间
dateTime: {
type: String,
default: "",
},
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(""); //子组件向父组件事件传递
let popup = ref();
const tipInfo = ref("确认删除该订单吗?");
const tabBars = HistoryTabData;//tab标签数据
let taskId = ref(""); //任务id
let tabIndex = ref(0); //当前tab
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let itemData = ref([]);//列表数据
let reload = ref(false); //是否加载
let pages = ref(0); //总页数
let pageNum = users.isFiltrate ? 1 : ref(1); //存放当前页
const emptyData = ref("暂无数据");
let isPullDown = ref(false); //是否下拉了
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
taskType: 1,
});
watch(props, (newValue, oldValue) => {
// 存储清空列表数据
store.commit("user/setDeliveryData", []);
getList(newValue.dateTime);
});
// 监听tab切换
watch(tabIndex, (newValue, oldValue) => {
if (newValue === 0) {
page.taskType = 1;
} else {
page.taskType = 2;
}
// 存储清空列表数据
store.commit("user/setDeliveryData", []);
// 根据不同的tab值切更新 取件数据
getList(page.dateTime);
});
// ------生命周期------
onMounted(() => {
if (users.tabIndex) {
tabIndex.value = users.tabIndex;
}
});
// 上下拉取
onReachBottom(() => {
if (pageNum.value >= Number(pages.value)) {
loadMore.value.status = "noMore";
return false;
} else {
loadMore.value.status = "loading";
let times = setTimeout(() => {
pageNum.value++;
getList(page.dateTime);
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// ------定义方法------
// 获取数据
const getList = async (time) => {
reload.value = true;
//判断是否进行了距离、时间、超时任务筛选如果是当前页设为第一页上拉的数值设为1便于第二次上拉
page = {
...page,
dateTime: (getTimeDate(time)).veryDayDate,
page: pageNum.value,
};
await getDeliveryList(page).then((res) => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange) {
itemData.value = res.data.items;
store.commit("user/setIstabChange", false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit("user/setDeliveryData", itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = "noMore";
}
} else {
itemData.value = [];
}
}
});
};
// 获取tab切换当前的index
const getTabIndex = (index) => {
store.commit("user/setTabIndex", 0);
store.commit("user/setIstabChange", true);
pageNum.value = 1;
pages.value = 1;
tabIndex.value = index;
itemData.value = [];
store.commit("user/setDeliveryData", []);
};
// 确认删除
const handleClick = async () => {
await taskDelete(taskId.value).then((res) => {
if (res.code === 200) {
store.commit("user/setDeliveryData", []);
getList(page.dateTime);
isPullDown.value = true;
return uni.showToast({
title: "删除成功!",
duration: 1000,
icon: "none",
});
}
});
};
// 删除弹层
const handleOpen = (id) => {
popup.value.dialogOpen();
taskId.value = id;
};
// 触发选项卡事件
const onChangeSwiperTab = (e) => {
changeTab(e.detail.current);
};
//把数据、方法暴漏给父组件
defineExpose({
getList,
});
</script>

View File

@@ -0,0 +1,125 @@
<template>
<!-- 列表内容 -->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<view class="boxBg">
<view class="tabList">
<view class="item" @click.stop="handleDetails($event, item)">
<view v-if="item.status !== 1" class="history">
<view class="titInfo">订单号SD{{ item.orderId }}</view>
<view class="address">寄件人{{ item.name }}</view>
<view class="address">取件地址{{ item.address }}</view>
<view class="address">取件时间{{ item.actualEndTime }}</view>
<view class="time" v-if="item.status === 2"
>运费{{ item.amount }}</view
>
</view>
<view v-else class="history">
<view class="titInfo">
<view>
<text class="name">{{ item.name }}</text>
{{ item.phone }}
<!-- TODO拨打电话和发信息小图标暂时保留 -->
<!-- <icon class="phone" @click.stop="handlePhone($event, item.phone)"></icon>
<icon class="note" @click.stop="handleNote"></icon> -->
</view>
</view>
<view class="address">{{ item.address }}</view>
<view class="address">{{ item.distance }}公里</view>
<view class="time"
>预约取件时间{{ taskTimeFormat(item.estimatedStartTime) }}
{{ overTimeFormat(item.estimatedEndTime) }}</view
>
</view>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="item.status === 1"
><button class="uni-btn btn-default">去取件</button></text
>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-else-if="
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 1
"
><button class="uni-btn btn-default">去收款</button></text
>
<text @click.stop="handleOpen($event, item.id)" class="delete" v-else
><button class="uni-btn concelBtn">删除</button></text
>
</view>
</view>
</view>
</view>
<!-- end -->
</template>
<script setup>
import { onMounted } from "vue";
import { taskTimeFormat, overTimeFormat } from "@/utils/index.js";
import { useStore } from "vuex";
// 获取父组件数据
const props = defineProps({
// 数据
itemData: {
type: Array,
default: () => [],
},
// 当前触发的tab值
tabIndex: {
type: Number,
default: 0,
},
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(""); //子组件向父组件事件传递
// ------生命周期------
onMounted(() => {});
// ------定义方法------
// 删除弹层
const handleOpen = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
emit("handleOpen", id);
};
// 去取件详情
const handleDetails = (e, item) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit("user/setTaskId", item.id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit("user/setTaskType", 1);
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit("user/setTaskStatus", 6);
store.commit("user/setDetailType", 1); //从历史订单他跳入订单详情
// 如果时带取件进入到去取件页面
// 进入详情页
if (item.status === 1) {
uni.redirectTo({
url: "/pages/details/index",
});
} else if (
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 1
) {
// 未付款进入付款二维码页面
store.commit("user/setPayData", {});
uni.redirectTo({
url: "/pages/pay/scanPay",
});
} else {
// 已经完成的进入订单详情页
uni.redirectTo({
url: "/pages/details/waybill",
});
}
};
</script>

View File

@@ -0,0 +1,101 @@
body,
uni-page-body {
background: var(--neutral-color-background) !important;
}
.dateBox {
padding: 14rpx 60rpx 24rpx 40rpx;
line-height: 40rpx;
background: var(--neutral-color-white);
border-bottom: 1px solid var(--neutral-color-background);
display: flex;
width: 100%;
box-sizing: border-box;
align-items: center;
position: fixed;
z-index: 2;
top: 264rpx;
::v-deep & > view {
&:nth-child(2) {
text-align: center;
flex: 1;
}
&:last-child {
text-align: right;
}
.date {
width: 300rpx;
height: 60rpx;
line-height: 60rpx;
margin: 0 auto;
background: var(--neutral-color-background);
border-radius: 8rpx;
display: flex;
align-items: center;
padding: 0 16rpx;
.dateIcon {
width: 34rpx;
height: 36rpx;
background: url(@/static/date.png) no-repeat;
background-size: contain;
margin-right: 22rpx;
}
text {
flex: 1;
}
.next {
width: 30rpx;
height: 23rpx;
background: url(@/static/icon15.png) no-repeat 50% 50%;
background-size: contain;
transform: rotate(90deg);
}
}
.noDate {
.dateIcon {
background: url(@/static/dateno.png) no-repeat;
background-size: contain;
}
text {
color: #bfbfbf;
}
.next {
background: url(@/static/icon16.png) no-repeat 50% 50%;
background-size: contain;
transform: rotate(-90deg);
}
}
}
:deep(.red){
color: var(--essential-color-red);
}
:deep(.gray){
color: var(--neutral-color-placeholder);
}
}
.history{
background: var(--neutral-color-white);
padding: 30rpx;
::v-deep .navBox {
top: 148rpx !important;
.uni-navbar {
padding: 40rpx !important;
}
}
}
::v-deep .historyTab{
width: 100% !important;
border-top: 1px solid var(--neutral-color-background);
position: fixed;
top: 361rpx;
z-index: 1;
.scroll-row-item{
margin-right: 128rpx;
}
}
::v-deep .uni-datetime-picker--btn,::v-deep .uni-calendar-item__weeks-box .uni-calendar-item--checked{
background-color: var(--essential-color-red);
}
::v-deep .uni-calendar-item__weeks-box-text{
color: var(--neutral-color-main);
}

View File

@@ -0,0 +1,94 @@
<!-- 历史取派页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<!-- 搜索nav -->
<view class="history">
<view class="navBox">
<view class="search">
<view class="uni-navbar">
<view class="input-view">
<view class="input-view">
<uni-icons
class="input-uni-icon"
type="search"
size="18"
color="#999"
/>
<input
confirm-type="search"
class="nav-bar-input"
type="text"
v-model="searchVal"
placeholder="输入四位或完整运单号/手机号、姓名"
@confirm="handleSearch"
@tap="handleSearch"
/>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- end -->
<view>
<!-- 日期选择 -->
<TateSelete @getDateTime="getDateTime"></TateSelete>
<!-- end -->
<!-- 取件派件列表 -->
<TabList :dateTime="dateTime" ref="list"></TabList>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive } from "vue";
import { useStore } from "vuex";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 日期选择
import TateSelete from "./commponents/date.vue";
// 列表
import TabList from "./commponents/index.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
let list = ref();
let itemData = reactive([]); //列表数据
const title = ref("全部取派"); //nav标题
let dateTime = ref(""); //存放日期
const searchVal = ref(""); //搜索内容
// ------生命周期------
// ------定义方法------
// 获取筛选的时间
const getDateTime = (val) => {
dateTime.value = val;
};
// 跳转到搜索页面
const handleSearch = (e) => {
// 跳转到搜索页面
uni.redirectTo({
url: "/pages/search/index",
});
};
// 返回上一页
const goBack = () => {
if (users.taskStatus === -1) {
uni.redirectTo({
url: "/pages/my/index",
});
} else {
uni.redirectTo({
url: "/pages/index/index",
});
}
store.commit("user/setTabIndex", 0);
store.commit("user/setTaskStatus", 0);
store.commit("user/setTimeData", null);
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,83 @@
<template>
<view class="commonUse">
<view class="hometit">常用功能</view>
<view class="commonList">
<view @click="handleChild">
<icon class="icon delivery"></icon>
<text>派件扫描</text>
</view>
<view @click="handleChild">
<icon class="icon sign"></icon>
<text>签收扫描</text>
</view>
<view>
<view @click="handleHistory">
<icon class="icon history"></icon>
<text>全部取派</text>
</view>
</view>
<view>
<view @click="handleNew">
<icon class="icon new"></icon>
<text>消息通知</text>
</view>
</view>
<view>
<navigator url="/pages/freight/index" open-type="redirect">
<icon class="icon freight"></icon>
<text>运费查询</text>
</navigator>
</view>
<view>
<view @click="handleTip">
<icon class="icon tip"></icon>
<text>签收提醒</text>
</view>
</view>
<view @click="handleChild">
<icon class="icon exclusive"></icon>
<text>专属取寄</text>
</view>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
// 获取父组件值、方法
const props = defineProps({});
const store = useStore(); //vuex获取储存数据
// 定义方法
const handleChild = () => {
uni.showToast({
title: "程序员哥哥正在实现中",
duration: 1000,
icon: "none",
});
};
// 历史派件
const handleHistory = () => {
store.commit("user/setTabIndex", 0);
store.commit("user/setNewType", null);
uni.navigateTo({
url: "/pages/history/index",
});
};
// 签收提醒
const handleTip = () => {
store.commit("user/setTaskStatus", -1);
uni.navigateTo({
url: "/pages/news/system?title=签收提醒&type=302",
});
};
// 消息通知
const handleNew = () => {
store.commit("user/setTabIndex", 1);
uni.navigateTo({
url: "/pages/news/index",
});
};
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,48 @@
<template>
<view class="commonData">
<view class="hometit">数据展示</view>
<view class="dataList">
<view class="todayGet" @click="handlTodayAcquired">
<view>今日已取</view>
<view class="num">{{ baseData.completePickUpNum }}</view>
<view class="rightIcon"></view>
</view>
<view class="todaySign" @click="handlTodaySign">
<view>今日已签</view>
<view class="num">{{ baseData.signedNum }}</view>
<view class="rightIcon"></view>
</view>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
const store = useStore(); //设置、获取数据
// 获取父组件值、方法
const props = defineProps({
baseData: {
type: Object,
default: () => ({}),
},
});
// ------定义方法------
// 进入 已取件列表页
const handlTodayAcquired = () => {
store.commit("user/setTabIndex", 1);
store.commit("setFootStatus", 1);
uni.redirectTo({
url: "/pages/pickup/index",
});
};
// 进入 已签收列表页
const handlTodaySign = () => {
store.commit("user/setTabIndex", 1);
store.commit("setFootStatus", 3);
uni.redirectTo({
url: "/pages/delivery/index",
});
};
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,198 @@
<template>
<view>
<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.name }}
<text class="num">{{ item.num }}</text>
</view>
</view>
</scroll-view>
<view class="homeSwiper">
<view v-if="tabIndex === 0 || tabIndex === 1">
<view v-if="itemData.length > 0">
<view
class="boxBg"
v-for="(item, index) in itemData.slice(0, 3)"
:key="index"
v-if="tabIndex === 0"
>
<view class="tabList">
<view class="item" @click.stop="handleDetail(item.id)">
<view class="titInfo">
{{ item.name }}
<text>{{ item.phone }}</text>
<icon
class="phone"
@click.stop="handlePhone($event, item.phone)"
></icon>
<icon class="note" @click.stop="handleNote"></icon>
</view>
<view class="address">{{ item.address }}</view>
<view class="distance">{{ item.distance }}公里</view>
<view class="time"
>预约取件时间{{
taskTimeFormat(item.estimatedStartTime)
}}
{{ overTimeFormat(item.estimatedEndTime) }}</view
>
</view>
</view>
</view>
<!-- 待派件 -->
<view
class="boxBg"
v-for="(item, index) in itemData.slice(0, 3)"
:key="index"
v-if="tabIndex === 1"
>
<view class="tabList">
<view class="item" @click.stop="handleDetail(item.id)">
<view class="titInfo">
{{ item.name }}
<text>{{ item.phone }}</text>
<icon
class="phone"
@click.stop="handlePhone($event, item.phone)"
></icon>
<icon class="note" @click.stop="handleNote"></icon>
</view>
<view class="address">{{ item.address }}</view>
<view class="address">{{ item.distance }}公里</view>
<view class="time">运单号{{ item.transportOrderId }}</view>
</view>
</view>
</view>
</view>
<view class="moreInfo" v-if="itemData.length > 3" @click="handleMore">
查看更多
<icon></icon>
</view>
</view>
<!-- 无数据显示 -->
<view v-if="itemData.length === 0"
><EmptyPage :emptyInfo="emptyInfo"></EmptyPage
></view>
<!-- end -->
</view>
<!-- 拨打手机弹层 -->
<Phone ref="phone" :phoneData="phoneData"></Phone>
<!-- end -->
</view>
</template>
<script setup>
import { ref } from "vue";
import { useStore } from "vuex";
// 处理事件封装的方法
import { taskTimeFormat, overTimeFormat } from "@/utils/index.js";
//组件
// 无数据
import EmptyPage from "@/components/uni-empty-page/index.vue";
import Phone from "@/components/uni-phone/index.vue";
// 获取父组件值、方法
const props = defineProps({
itemData: {
type: Array,
default: () => [],
},
tabBars: {
type: Array,
default: () => [],
},
});
// ------定义变量------
const store = useStore(); //设置、获取数据
let scrollinto = ref("tab0"); //tab切换
let tabIndex = ref(0); //当前tab
const phone = ref();
const emptyInfo = ref("- 空空如也,无运单记录 -");
const emit = defineEmits("getTabIndex");
const phoneData = ref("");
// ------定义方法------
// tab选项卡切换轮播
const changeTab = (index) => {
// 点击的还是当前数据的时候直接return
if (tabIndex.value == index) {
return;
}
tabIndex.value = index;
emit("getTabIndex", index);
// 滑动
scrollinto.value = "tab" + index;
};
// 触发选项卡事件
const onChangeSwiperTab = (e) => {
changeTab(e.detail.current);
};
// 进入详情
const handleDetail = (id) => {
// 把任务id用vuex的方法存储方便其他页面调用
store.commit("user/setTaskId", id);
store.commit("user/setNewType", null);
// 进入取件详情页
if (tabIndex.value === 0) {
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit("user/setTaskType", 1);
store.commit("user/setDetailType", 0);
store.commit("setFootStatus", 1); //修改底部tab当前标签
uni.redirectTo({
url: "/pages/details/index",
});
} else {
// 进入派件详情页
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit("user/setTaskType", 2);
store.commit("user/setDetailType", 0);
store.commit("setFootStatus", 3); //修改底部tab当前标签
uni.redirectTo({
url: "/pages/details/waybill",
});
}
};
// 进入待取件待派件页面
const handleMore = () => {
if (tabIndex.value === 0) {
uni.redirectTo({
url: "/pages/pickup/index",
});
} else {
uni.redirectTo({
url: "/pages/delivery/index",
});
}
};
// 拨打电话弹层
const handlePhone = (e, val) => {
// 阻止事件冒泡
e.stopPropagation();
phoneData.value = val;
phone.value.dialogOpen();
};
// 发短信
const handleNote = () => {
uni.showToast({
title: "程序员哥哥正在实现中",
duration: 1000,
icon: "none",
});
};
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,59 @@
<template>
<view class="infoBox">
<view class="boxBg">
<view @click="handlePicup">
<view class="num">{{ baseData.newPickUpNum }}</view>
<view>取件任务</view>
</view>
<view @click="handleDelivery">
<view class="num">{{ baseData.newDispatchNum }}</view>
<view>派件任务</view>
</view>
<view @click="handleOvertime">
<view class="num">{{ baseData.overTimeNum }}</view>
<view>超时任务</view>
</view>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
const store = useStore(); //设置、获取数据
// 获取父组件值、方法
const props = defineProps({
baseData: {
type: Object,
default: () => ({}),
},
});
// ------定义方法------
// 进入 待取件列表页
const handlePicup = () => {
store.commit("user/setTabIndex", 0);
store.commit("setFootStatus", 1);
uni.redirectTo({
url: "/pages/pickup/index",
});
};
// 进入 待派件列表页
const handleDelivery = () => {
store.commit("user/setTabIndex", 0);
store.commit("setFootStatus", 3);
store.commit("user/setDetailType", 0);
uni.redirectTo({
url: "/pages/delivery/index",
});
};
// 进入 选中超时任务
const handleOvertime = () => {
store.commit("user/setTabIndex", 0);
store.commit("setFootStatus", 1);
store.commit("user/setFilterOverTime", true);
uni.redirectTo({
url: "/pages/pickup/index",
});
};
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,48 @@
<template>
<view class="orderTip" v-if="orderData" @click="handleLink">
<view class="btn">消息通知</view>
<view>{{ orderData.recentMsg }}</view>
<view>
{{ orderData.minutes === 0 ? 1 : orderData.minutes }}分钟前
<icon class="iconNext"></icon>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
const store = useStore(); //vuex获取、储存数据
// 获取父组件值、方法
const props = defineProps({
orderData: Object,
default: () => ({}),
});
// ------定义方法------
const handleLink = () => {
const type = props.orderData.contentType;
store.commit("user/setTaskStatus", -1);
if (type === 300) {
uni.redirectTo({
url: "/pages/news/index",
});
} else if (type === 301) {
uni.redirectTo({
url: "/pages/news/system?title=取件相关&type=301",
});
} else if (type === 302) {
uni.redirectTo({
url: "/pages/news/system?title=签收提醒&type=302",
});
} else if (type === 303) {
uni.redirectTo({
url: "/pages/news/system?title=快件取消&type=303",
});
} else {
uni.redirectTo({
url: "/pages/news/system?title=派件相关&type=304",
});
}
};
</script>
<style src="../index.scss" lang="scss"></style>

View File

@@ -0,0 +1,237 @@
body,
uni-page-body,
uni-page-head,
.uni-page-head {
background-color: var(--neutral-color-white) !important;
}
.homePageBox{
// height: calc(100vh);
position: relative;
padding-top: 430rpx;
overflow: hidden;
.boxBg {
box-shadow: 0 0 22rpx 22rpx rgba(162, 162, 162, 0.06);}
}
.noOrder{
padding-top: 460rpx;
}
.infoBox {
margin-top: 216rpx;
position: relative;
z-index: 9;
// position: fixed;
// z-index: 10;
// left: 0;
// right: 0;
.boxBg {
padding: 38rpx 0;
// height: 200rpx;
display: flex;
box-shadow: 0 0 22rpx 22rpx rgba(162, 162, 162, 0.06);
& > view {
flex: 1;
text-align: center;
border-left: 1px solid var(--neutral-color-background);
font-size: var(--font-size-14);
line-height: 40rpx;
color: var(--neutral-color-font);
&:first-child {
border: 0 none;
}
.num {
font-weight: 600;
font-size: 64rpx;
line-height: 80rpx;
color: var(--neutral-color-main);
padding-bottom: 6rpx;
}
& > view {
padding-bottom: 10rpx;
}
}
}
}
.orderTip {
height: 68rpx;
line-height: 68rpx;
background: #fff1f0;
border-radius: 20rpx;
margin-top: 40rpx;
margin-bottom: 30rpx;
padding: 0 22rpx 0 14rpx;
display: flex;
font-size: 20rpx;
color: var(--essential-color-red);
align-items: center;
& > view {
&:nth-child(2) {
flex: 1;
}
&:nth-child(3) {
display: flex;
align-items: center;
}
}
.btn {
display: inline-block;
width: 106rpx;
// height: 36rpx;
line-height: 34rpx;
// padding: 0 0 0 10rpx;
background: url(@/static/new.png) no-repeat;
background-size: contain;
color: var(--neutral-color-white);
font-size: 20rpx;
margin-right: 12rpx;
text-emphasis: none;
text-align: center;
}
}
// 常用功能
.hometit {
line-height: 46rpx;
font-weight: 600;
font-family: PingFangSC-Medium;
font-size: 32rpx;
color: var(--neutral-color-main);
letter-spacing: 0.36rpx;
}
.commonList {
display: flex;
flex-flow: row wrap;
align-content: flex-start;
& > view {
box-sizing: border-box;
flex: 0 0 25%;
font-size: var(--font-size-12);
color: var(--neutral-color-font);
letter-spacing: 0.28px;
line-height: 34rpx;
padding: 30rpx 0;
text-align: center;
text {
display: block;
}
icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 16rpx;
&.delivery {
background: url(@/static/icon03.png);
background-size: contain;
}
&.sign {
background: url(@/static/icon04.png);
background-size: contain;
}
&.history {
background: url(@/static/icon05.png);
background-size: contain;
}
&.new {
background: url(@/static/icon06.png);
background-size: contain;
}
&.freight {
background: url(@/static/icon07.png);
background-size: contain;
}
&.tip {
background: url(@/static/icon08.png);
background-size: contain;
}
&.exclusive {
background: url(@/static/icon09.png);
background-size: contain;
}
}
}
}
.commonData {
padding-top: 18rpx;
.dataList {
display: flex;
margin-left: -20rpx;
padding: 32rpx 0 6rpx;
& > view {
height: 112rpx;
border-radius: 16rpx;
padding: 34rpx 24rpx;
line-height: 34rpx;
flex: 1;
margin-left: 20rpx;
position: relative;
font-size: var(--font-size-12);
}
.rightIcon {
position: absolute;
right: 18rpx;
bottom: 6rpx;
}
}
.todayGet {
background-image: linear-gradient(178deg, #ffebeb 4%, #fbf3f3 100%);
color: #ce6864;
.rightIcon {
background: url(@/static/fetch.png) no-repeat;
background-size: contain;
width: 136rpx;
height: 146rpx;
}
}
.todaySign {
background-image: linear-gradient(178deg, #ffe9dd 5%, #faf3ee 100%);
color: #c97f59;
.rightIcon {
background: url(@/static/sign.png) no-repeat;
background-size: contain;
width: 162rpx;
height: 148rpx;
}
}
.rigIcon {
position: absolute;
right: 20rpx;
}
.num {
line-height: 60rpx;
font-size: 44rpx;
color: #c24340;
letter-spacing: 0.5px;
font-weight: 600;
padding-top: 10rpx;
}
}
// 首页查看更多
.moreInfo {
font-size: var(--font-size-12);
padding: 62rpx 0 80rpx;
text-align: center;
line-height: 34rpx;
icon {
background: url(@/static/icon16.png) no-repeat;
background-size: contain;
width: 16rpx;
height: 22rpx;
margin-left: 6rpx;
flex: 1;
vertical-align: middle;
margin-top: -4rpx;
}
}
.homeSwiper{
padding-bottom: 136rpx;
::v-deep .uni-swiper-wrapper{
overflow: unset !important;
position: static !important;
// width: auto !important;
height:auto !important;
uni-swiper-item{
overflow: unset !important;
}
}
}

View File

@@ -0,0 +1,147 @@
<!-- 首页 -->
<template>
<view class="navFrame">
<!-- nav -->
<UniNav :newVal="newVal" @goBack="goBack"></UniNav>
<!-- 订单信息 -->
<OrderInfo :baseData="baseData"></OrderInfo>
<!-- end -->
</view>
<!-- end -->
<view class="homePageBox" :class="!orderData.value ? 'noOrder' : ''">
<view class="boxPad">
<!-- 订单提示 -->
<OrderTip :orderData="orderData.value"></OrderTip>
<!-- end -->
<!-- 常用功能 -->
<CommonUse></CommonUse>
<!-- end -->
<!-- 数据展示 -->
<DataPresentation :baseData="baseData"></DataPresentation>
<!-- end -->
</view>
<!-- 取件状态列表 -->
<ExpressageInfo :itemData="itemData.value" :tabBars="tabBars" @getTabIndex="getTabIndex"></ExpressageInfo>
<!-- end -->
</view>
<!-- footer -->
<UniFooter :pagePath="'pages/index/index'"></UniFooter>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useStore } from 'vuex';
import { getTimeDate, positionsUploadInit } from '@/utils/index.js';
// 静态数据
import { tabBars } from '@/utils/commonData.js'
// 导入接口
import { getHomeInfo, getHomeData, getDeliveryList} from '@/pages/api/index.js';
// 导入组件
// 导航
import UniNav from '@/components/uni-home-nav/index.vue';
// 底部导航
import UniFooter from '@/components/uni-footer/index.vue';
// 订单信息
import OrderInfo from './components/orderInfo.vue';
// 订单消息提示
import OrderTip from './components/orderTip.vue';
// 常用功能
import CommonUse from './components/commonUse.vue';
// 数据展示
import DataPresentation from './components/dataPresent.vue';
// 取件信息
import ExpressageInfo from './components/expressageInfo.vue';
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const users = store.state.user;
const newVal = ref(Number(null)); //消息
const orderData = reactive({}); //首页相关默认信息
const itemData = reactive([]);
const noPickupTaskList = reactive([]); //取件列表数据
const noDispatchTaskList = reactive([]); //派件列表数据
const locationData = ref({});
let baseData = ref({});
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 1
});
// ------生命周期------
onMounted(() => {
// 上报位置 进入首页立即上报
positionsUploadInit()
init();
});
// ------定义方法------
// 获取初始值
const init = () => {
getNewData();
getHomeBase();
getList();
};
// 获取相关消息
const getNewData = async () => {
await getHomeInfo().then(res => {
if (res.code === 200) {
orderData.value = res.data;
newVal.value = res.data.newsNum;
}
});
};
// 获取相关任务、取件、派件、今日已取、已签
const getList = async () => {
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
itemData.value = res.data.items;
}
}
});
};
// 获取相关任务、取件、派件、今日已取、已签
const getHomeBase = async () => {
const locition = {
longitude: 116.344015,
latitude: 40.060607
};
await getHomeData(locition).then(res => {
if (res.code === 200) {
baseData.value = res.data;
}
});
};
// 列表tab当前切换的index
const getTabIndex = val => {
itemData.value = [];
if (val === 0) {
page.taskStatus = 1;
} else {
page.taskStatus = 4;
}
getList();
};
// 获取当前位置
const getLocation = () => {
uni.getLocation({
type: 'gcj02',
success: res => {}
});
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: '/pages/index/index'
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>
<style lang="scss">
body {
background: #fff;
}
</style>

View File

@@ -0,0 +1,165 @@
body,
uni-page-body,
uni-page-head,
.uni-page-head {
background-color: var(--neutral-color-white) !important;
}
.navBox{
position: static;
}
.loginBox {
padding: 100rpx 34rpx 0 66rpx;
.tit {
line-height: 66rpx;
font-weight: 600;
font-size: 48rpx;
color: var(--neutral-color-main);
letter-spacing: 0;
display: flex;
padding-bottom: 52rpx;
text {
flex: 1;
}
.text {
font-weight: 500;
font-size: 32rpx;
color: var(--essential-color-red);
text-align: right;
line-height: 50rpx;
padding-top: 14rpx;
icon {
background: url(../../static/icon01.png) no-repeat;
background-size: contain;
width: 20rpx;
height: 20rpx;
margin-left: 14rpx;
vertical-align: unset;
}
}
}
.loginMain {
::v-deep .uni-input-input{
color: var(--neutral-color-main);
}
padding-right: 32rpx;
.item {
::v-deep .is-input-border {
padding: 36rpx 0;
font-weight: 400;
font-size: 32rpx;
line-height: 40rpx;
color: #797979;
letter-spacing: 0.36rpx;
border: 0 none;
border-bottom: 1px solid var(--neutral-color-background);
position: relative;
border-radius: 0;
.uni-easyinput__placeholder-class {
font-size: 32rpx;
color: var(--neutral-color-font);
letter-spacing: 0.36rpx;
}
.uni-easyinput__content-input {
padding-left: 0 !important;
font-size: 32rpx;
}
}
::v-deep .is-input-error-border {
border-bottom: 1px solid var(--essential-color-red);
margin-bottom: 44rpx;
}
::v-deep .content-clear-icon{
padding: 0;
font-size: 28rpx !important;
line-height: 46rpx;
}
::v-deep .uniui-eye-filled{
background: url(@/static/icon-close.png) 50% 50% no-repeat;
background-size: contain;
&:before{
color:transparent;
}
}
::v-deep .uniui-eye-slash-filled{
background: url(@/static/icon-open.png) 50% 50% no-repeat;
background-size: contain;
&:before{
color:transparent;
}
}
}
::v-deep .uni-forms-item__inner {
padding-bottom: 0;
}
::v-deep .uni-error-message {
bottom: -10rpx;
}
::v-deep .content-clear-icon {
margin-right: 30rpx;
}
.inputW{
::v-deep .is-input-border{
padding-right: 200rpx;
}
}
.codeBox {
position: absolute;
right: 40rpx;
top:40rpx;
font-size: 28rpx;
}
.setUrl{
font-size: 24rpx;
color: #ccc;
line-height: 48rpx;
text-align: center;
margin-top: 40rpx;
}
}
.btn-default{
height: 100rpx;
line-height: 100rpx;
border-radius: 50rpx;
&.disabled{
background-color: var(--assist-color-logbtn) !important;
}
}
.btnBox{
padding: 100rpx 0 0;
}
}
.pwdBox {
position: relative;
text {
// &.pwdIcon {
// background: url(@/static/icon-close.png);
// background-size: contain;
// }
// &.showPwdIcon {
// background: url(@/static/icon-open.png);
// background-size: contain;
// }
&.clearIcon {
position: absolute;
width: 32rpx;
height: 32rpx;
right: 60rpx;
top: 40rpx;
font-size: 0.875rem !important;
line-height: 1.4375rem;
color: rgb(192, 196, 204);
font-family: uniicons;
text-decoration: none;
text-align: center;
&:before{
content: "\e66d";
}
}
}
}
.fontCol {
color: var(--neutral-color-font);
}

View File

@@ -0,0 +1,168 @@
<!-- 手机短信登录页 -->
<template>
<view class="loginBox">
<view class="tit">
<text>手机号登录</text>
<text class="text" @click="goLogin">
账号登录
<icon></icon>
</text>
</view>
<!-- 登录表单 手机号验证码 -->
<view class="loginMain">
<uni-forms ref="customForm" :rules="customRules" :modelValue="fromInfo">
<uni-forms-item name="phone"
><uni-easyinput
class="item"
v-model="fromInfo.phone"
placeholder="请输入手机号"
/></uni-forms-item>
<uni-forms-item name="verifyCode">
<uni-easyinput
class="item inputW"
v-model="fromInfo.verifyCode"
placeholder="请输入验证码"
/>
<view class="codeBox">
<text class="code" v-show="codeInfo.show" @click="getCode"
>获取验证码</text
>
<text class="code fontCol" v-show="!codeInfo.show"
>{{ codeInfo.times }}s后重新获取</text
>
</view>
</uni-forms-item>
</uni-forms>
<!-- 按钮 -->
<view class="btnBox">
<button
class="btn-default"
:disabled="
fromInfo.phone.length === 0 || fromInfo.verifyCode.length === 0
"
:class="
fromInfo.phone.length === 0 || fromInfo.verifyCode.length === 0
? 'disabled'
: ''
"
type="primary"
@click="handleSubmit"
>
登录
</button>
</view>
<!-- end -->
</view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useStore } from "vuex";
// 接口
import { phoneLogins, getsmsCode } from "../api/user.js";
// 验证
import { validatePhone, timeCountdown, isPhone } from "@/utils/validate";
// ------定义变量------
const store = useStore(); //vuex获取储存数据
const customForm = ref();
let isVerifySuccess = ref(false);
// 表单数据
let fromInfo = reactive({
phone: "", //手机号
verifyCode: "", // 验证码
});
// 验证码倒计时数据
let codeInfo = reactive({
show: true,
timer: null,
times: 60,
});
// 表单校验
const customRules = reactive({
phone: {
rules: [
{
required: true,
validateFunction: validatePhone,
errorMessage: "请输入手机号",
},
],
},
verifyCode: {
rules: [
{
required: true,
errorMessage: "请输入验证码",
},
],
},
});
// ------生命周期------
onMounted(() => {});
// ------定义方法------
// 获取验证码
const getCode = async () => {
let p = fromInfo.phone;
isVerifySuccess.value = isPhone(p);
if (isVerifySuccess.value) {
timeCountdown(codeInfo);
const parent = {
phone: phone.value,
};
// TODO暂时保留
// 获取手机验证码
// const {data} = await getsmsCode(parent)
// if(data.code===0){
// }else{
// return uni.showToast({
// title: data.msg,
// duration: 1000,
// icon: 'none'
// });
// }
} else {
return uni.showToast({
title: "手机号输入错误!请重新输入",
duration: 1000,
icon: "none",
});
}
};
// 登录
const handleSubmit = async () => {
// 表单校验
const valid = await customForm.value.validate();
if (valid) {
// 登录接口
await phoneLogins(fromInfo).then((res) => {
if (res.code === 0) {
store.commit("user/setToken", res.token);
// 通过vuex获取用户信息
store.dispatch("user/GetUsersInfo");
// 跳转到首页
uni.redirectTo({
url: "/pages/index/index",
});
} else {
return uni.showToast({
title: res.msg,
duration: 1000,
icon: "none",
});
}
});
}
};
// 去手机登录页面
const goLogin = () => {
uni.redirectTo({
url: "/pages/login/user",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,165 @@
<!-- 账号登录页 -->
<template>
<view class="logo">神领快递员</view>
<view class="loginBox">
<view class="tit">
<text>账号登录</text>
<!-- 一期不做,暂时隐藏 -->
<!-- <text class="text" @click="goLogin">
手机号登录
<icon></icon>
</text> -->
</view>
<!-- 登录表单 -->
<view class="loginMain">
<uni-forms ref="customForm" :rules="customRules" :modelValue="fromInfo">
<uni-forms-item name="account"><uni-easyinput class="item" v-model="fromInfo.account" placeholder="请输入账号" /></uni-forms-item>
<uni-forms-item name="password" class="pwdBox">
<uni-easyinput class="item" type="password" v-model="fromInfo.password" placeholder="请输入密码" />
<text class="clearIcon" v-if="fromInfo.password.length > 0" @click="handlePwd"></text>
</uni-forms-item>
</uni-forms>
<!-- 按钮 -->
<view class="btnBox">
<button
class="btn-default"
:disabled="fromInfo.account.length === 0 || fromInfo.password.length === 0"
:class="fromInfo.account.length === 0 || fromInfo.password.length === 0 ? 'disabled' : ''"
type="primary"
@click="handleSubmit"
>
登录
</button>
</view>
<!-- end -->
<!-- 更新请求Url - 教学需求 -->
<view class="setUrl" @click="inputDialogToggle">配置请求url</view>
<uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog ref="inputClose" mode="input" title="配置URL" :value="baseURL" placeholder="请输入baseURL" @confirm="dialogInputConfirm"></uni-popup-dialog>
</uni-popup>
<!-- 更新请求Url - 教学需求 -->
</view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useStore } from 'vuex';
import storage from '@/utils/storage.js';
// 接口
import { userLogins } from '../api/user.js';
// 导入接口
import { getHomeInfo } from '@/pages/api/index.js';
// ------定义变量------
const store = useStore(); //vuex获取储存数据
let showPassword = ref(false); //控制密码右侧图标显示隐藏
const customForm = ref(); //表单校验
// 表单数据
let fromInfo = reactive({
account: 'blkdy001', //账号
password: '123456' // 密码
});
// 表单校验
const customRules = reactive({
account: {
rules: [
{
required: true,
errorMessage: '请输入手机号'
}
]
},
password: {
rules: [
{
required: true,
errorMessage: '请输入验证码'
}
]
}
});
// ------声明周期------
onMounted(() => {
// 进入登录页面配置默认的请求url
// uni.setStorageSync('baseUrl', 'http://slwl-geteway-t.itheima.net/courier');
// 处理定时上报位置的定时器
clearInterval(uni.getStorageSync('positions').timer);
uni.setStorageSync('positions', null);
});
// ------定义方法------
// 登录
const handleSubmit = async () => {
// // 表单校验
const valid = await customForm.value.validate();
if (valid) {
// 登录接口
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
await userLogins(fromInfo)
.then(async res => {
if (res.code === 200) {
// 操作成功后清除loading
setTimeout(function () {
uni.hideLoading();
}, 500);
clearTimeout(times)
// 存储token
uni.setStorageSync('token', res.data.token);
store.commit('user/setToken', res.data.token);
// // 通过vuex获取用户信息
store.dispatch('user/GetUsersInfo');
await getHomeInfo().then(res => {
if (res.code === 200) {
// 跳转到首页
uni.redirectTo({
url: '/pages/index/index'
});
}
});
} else {
uni.showToast({
title: res.msg,
duration: 1000,
icon: 'none'
});
}
})
.catch(err => {
// uni.showToast({
// title: err.msg,
// duration: 15000,
// icon: 'none'
// });
});
}
};
// 去手机登录页面
const goLogin = () => {
uni.redirectTo({
url: '/pages/login/index'
});
};
// 设置密码
const handlePwd = () => {
fromInfo.password = '';
};
// 打开设置Url窗口
const baseURL = ref(uni.getStorageSync('baseUrl'));
const inputDialog = ref(null);
const inputDialogToggle = () => {
inputDialog.value.open();
};
// 报错配置的Url
const dialogInputConfirm = val => {
baseURL.value = val;
uni.setStorageSync('baseUrl', val);
};
</script>
<style src="./index.scss" lang="scss"></style>

View File

@@ -0,0 +1,47 @@
<template>
<view class="nav-bg mnav-bg">
<view class="headBg"></view>
<view class="header">
<view class="headBox">
<view class="head"
><icon v-if="baseData.avatar === ''"></icon
><image :src="baseData.avatar" v-else></image
></view>
<view class="info">
<view class="tit">{{ baseData.name }}</view>
<view>{{ baseData.account }}</view>
<view class="address">
<icon></icon>
{{ baseData.agencyName ? baseData.agencyName : "暂无" }}
</view>
</view>
</view>
<view class="QR" @click="handleQr"
><image src="../../../static/qr.png"></image
></view>
</view>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
baseData: {
type: Object,
default: () => ({}),
},
});
// ------定义方法------
// 二维码暂时不做
const handleQr = () => {
// TODO暂时保留
// uni.navigateTo({
// url:'/pages/my/qrCode'
// })
uni.showToast({
title: "程序员哥哥正在实现中",
duration: 1000,
icon: "none",
});
};
</script>
<style src="./../index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,46 @@
<template>
<view class="boxBg headTop">
<view class="headItem">
<view class="item">
<text>我的评价</text>
<view class="star">
<uni-rate
:readonly="true"
allow-half
:value="4.5"
active-color="#EF4F3F"
color="#F4F4F4"
/>
<text>{{ 4.5 }}</text>
</view>
</view>
<view class="item">
<text>排班时间</text>
<text
v-if="
(!baseData.startTime || baseData.startTime === '') &&
(!baseData.endTime || baseData.endTime === '')
"
>无</text
>
<text v-else
>{{ overTimeFormat(baseData.startTime) }}-{{
overTimeFormat(baseData.endTime)
}}</text
>
</view>
</view>
</view>
</template>
<script setup>
// 时间处理
import { overTimeFormat } from "@/utils/index.js";
// 获取父组件数据
const props = defineProps({
baseData: {
type: Object,
default: () => ({}),
},
});
</script>
<style src="./../index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,30 @@
<template>
<view class="boxBg">
<view class="headItem">
<view class="item" @click="handleHistory">
<text>全部取派</text>
<text><icon class="nextIcon"></icon></text>
</view>
<navigator url="/pages/my/map" open-type="redirect">
<view class="item">
<text>作业范围</text>
<text><icon class="nextIcon"></icon></text>
</view>
</navigator>
</view>
</view>
</template>
<script setup>
import { useStore } from "vuex";
const store = useStore(); //vuex获取储存数据
// ------定义方法------
// 历史派件
const handleHistory = () => {
store.commit("user/setTabIndex", 0);
store.commit("user/setTaskStatus", -1);
uni.navigateTo({
url: "/pages/history/index",
});
};
</script>
<style src="./../index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,143 @@
.mnav-bg{
height: 370rpx;
}
.header {
padding: 122rpx 86rpx 102rpx 64rpx;
color: var(--neutral-color-white);
display: flex;
align-items: center;
line-height: 40rpx;
font-size: var(--font-size-16);
.headBox {
display: flex;
flex: 1;
align-items: center;
.head {
icon{
background: url(@/static/head.png) no-repeat;
background-size: contain;
width: 116rpx;
height: 116rpx;
}
image{
width: 114rpx;
height: 114rpx;
}
padding-right: 26rpx;
}
.tit {
padding-bottom: 2rpx;
}
.address {
display: flex;
align-items: center;
font-size: var(--font-size-14);
padding-top: 8rpx;
icon {
background: url(@/static/address.png) no-repeat;
background-size: contain;
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
}
.info {
flex: 1;
font-size: var(--font-size-16);
}
}
.QR {
image {
width: 48rpx;
height: 48rpx;
margin-top: 16rpx;
}
}
}
.headTop {
margin-top: -80rpx;
}
.headItem {
padding: 0 34rpx;
font-size: var(--font-size-16);
.item {
height: 100rpx;
line-height: 100rpx;
border-bottom: 1px solid var(--neutral-color-background);
display: flex;
align-items: center;
// &:last-child {
// border: 0 none;
// }
text {
&:first-child {
flex: 1;
}
}
}
}
.star {
display: flex;
align-items: center;
::v-deep .uni-icons {
font-size: 48rpx !important;
}
::v-deep .uni-rate__icon {
margin-right: 6rpx !important;
}
// icon {
// background: url(@/static/star.png) no-repeat;
// background-size: contain;
// width: 32rpx;
// height: 32rpx;
// margin-left: 16rpx;
// }
}
.boxBg {
margin-bottom: 32rpx;
}
.qrCode {
.boxBg {
margin-top: 32rpx;
text-align: center;
padding: 52rpx 60rpx 68rpx;
}
.img {
padding: 30rpx 0;
image {
width: 416rpx;
height: 410rpx;
}
}
}
.qrHead {
display: flex;
align-items: center;
text-align: center;
width: 416rpx;
margin: 0 auto;
.head {
background: url(@/static/head.png) no-repeat;
background-size: contain;
width: 86rpx;
height: 86rpx;
margin-right: 20rpx;
margin-left:20rpx;
}
.star {
font-size: 20rpx;
::v-deep .uni-icons {
font-size: 28rpx !important;
}
::v-deep .uni-rate__icon {
margin-right: 4rpx !important;
}
}
.tit{
font-size: var(--font-size-13);
font-weight: 600;
padding-bottom: 4rpx;
}
}

View File

@@ -0,0 +1,55 @@
<!-- 我的页面 -->
<template>
<view class="navFrame">
<!-- 我的基本信息 -->
<BaseInfo :baseData="baseData"></BaseInfo>
<!-- end -->
<!-- 我的评价排班时间 -->
<Evaluate :baseData="baseData"></Evaluate>
<!-- end -->
<!-- 历史取派作业范围 -->
<HistoryScope></HistoryScope>
<!-- end -->
</view>
<!-- 退出按钮 -->
<view class="footBtn">
<view class="btnBox"
><button class="btn-default" @click="handleOut">退出登录</button></view
>
</view>
<!-- end -->
<!-- footer -->
<UniFooter :pagePath="'pages/my/index'"></UniFooter>
<!-- end -->
</template>
<script setup>
import { useStore } from "vuex";
// 时间处理
import { overTimeFormat } from "@/utils/index.js";
// 导入组件
// 底部导航
import UniFooter from "@/components/uni-footer/index.vue";
// 基本信息
import BaseInfo from "./commponents/BaseInfo.vue"
// 我的评价、排班时间
import Evaluate from "./commponents/Evaluate.vue"
// 历史取派、作业范围
import HistoryScope from "./commponents/HistoryScope.vue"
// ------定义变量------
const store = useStore(); //vuex获取储存数据
let baseData = uni.getStorageSync("userInfo"); //获取登录时保存的用户信息
// 退出
const handleOut = () => {
// 移除指定 token
uni.removeStorageSync("token");
// 清理本地数据缓存
// uni.clearStorage();
// 移出当前的tab触发事件index值再次登录后底部菜单会默认选中首页
store.commit("setFootStatus", 0);
uni.redirectTo({
url: "/pages/login/user",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,87 @@
<!-- 作业范围页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="content"
><map
class="mapBox"
:latitude="latitude"
:longitude="longitude"
:polygons="polygons"
scale="16"
></map
></view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 导入接口
import { getUserScope } from "@/pages/api/my.js";
// ------定义变量------
const title = ref("作业范围"); //nav标题
const latitude = ref(39.997534); //维度
const longitude = ref(116.475334); //经度
const polygons = reactive([
{
//多边形的坐标数组
points: [
{
longitude: 116.475334,
latitude: 39.997534,
},
{
longitude: 116.476627,
latitude: 39.998315,
},
{
longitude: 116.478603,
latitude: 39.99879,
},
{
longitude: 116.478529,
latitude: 40.000296,
},
{
longitude: 116.475082,
latitude: 40.000151,
},
{
longitude: 116.473421,
latitude: 39.998717,
},
],
fillColor: "#EF4F3F20", //填充颜色后两个数值是透明度
strokeColor: "#EF4F3F", //描边颜色
strokeWidth: 2, //描边宽度
zIndex: 1, //层级
dottedLine: true,
},
]);
// ------生命周期------
onMounted(() => {
getUserPolygon();
});
// ------定义方法------
// 获取作业范围
const getUserPolygon = async () => {
await getUserScope().then((res) => {
if (res.code === 200) {
// TODO暂时保留
// polygons[0].points=res.data.polygon
// latitude.value=polygons[0].points[0].latitude
// longitude.value=polygons[0].points[0].longitude
}
});
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/my/index",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,51 @@
<!-- 专属二维码页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="pageBox qrCode">
<view class="boxBg">
<view class="qrHead">
<view class="head"></view>
<view class="info">
<view class="tit">神领快递员-张全蛋</view>
<view class="star">
<uni-rate
:readonly="true"
allow-half
:value="4.5"
active-color="#EF4F3F"
color="#F4F4F4"
/>
<text>4.7</text>
</view>
</view>
</view>
<view class="img"><image src="../../static/scanPay.png"></image></view>
<view>扫码快速下单</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 导入接口
import { getUserScope } from "@/pages/api/my.js";
// ------定义变量------
const title = ref("专属二维码"); //nav标题
// ------生命周期------
onMounted(() => {});
// ------定义方法------
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/my/index",
});
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,92 @@
<template>
<view v-if="tabIndex === 0">
<!-- 垂直滚动区域 scroll和swiper的高度都要给且是一样的高度-->
<scroll-view scroll-y="true">
<view v-if="newItemData.length > 0">
<view class="boxCon">
<view class="tabConList">
<view
class="item"
v-for="(item, index) in newItemData"
:key="index"
>
<view @click="handleClick(item)">
<text class="text active">
{{ item.title }}
</text>
<text class="time">{{ taskTimeFormat(item.created) }}</text>
</view>
</view>
</view>
</view>
<!-- 暂时先不做后期做 -->
<!-- <ReachBottom v-if="loading" :loadingText="loadingText"></ReachBottom> -->
</view>
<!-- 无数据显示 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { taskTimeFormat } from "@/utils/index.js";
// 公告数据
import { newItemData } from "@/utils/commonData.js";
// 接口api
import { getNewList } from "@/pages/api/news.js";
// 导入组件
//空页面
import EmptyPage from "@/components/uni-empty-page/index.vue";
// 下拉提示
import ReachBottom from "@/components/reach-bottom/index.vue";
// ------定义变量------
let loadingText = ref("");
let loading = ref(false);
// 获取父组件数据
const props = defineProps({
// 当前触发的tab值
tabIndex: {
type: Number,
default: 0,
},
});
// ------定义变量------
const emit = defineEmits("handleSearch"); //子组件向父组件事件传递
// ------生命周期------
onMounted(() => {
// init();
});
// ------定义方法------
const init = () => {
// TODO 暂时不做此功能数据先写死
// getList();
};
// 获取公告列表
const getList = async () => {
await getNewList("300")
.then((res) => {
if (res.code === 200) {
itemData.value = res.data;
}
})
.catch((err) => {
return uni.showToast({
title: err.msg,
duration: 1000,
icon: "none",
});
});
};
const handleClick = async (item) => {
uni.navigateTo({
url: "/pages/news/detail?obj=" + JSON.stringify(item),
});
};
//把数据、方法暴漏给父组件
defineExpose({
getList,
});
</script>

View File

@@ -0,0 +1,149 @@
<template>
<view v-if="tabIndex === 1">
<scroll-view scroll-y="true">
<view>
<view class="boxCon">
<view class="newConBox">
<view class="item">
<navigator
:url="'/pages/news/system?title=取件相关&type=' + 301"
open-type="redirect"
>
<view
class="icon send"
:class="objData.haveNewSendNotice ? 'active' : ''"
><icon></icon
></view>
<view class="text">
<view>取件相关</view>
<view>{{
objData.newSendNoticeTime
? "您有一个新的取件订单"
: "暂无消息"
}}</view>
</view>
<text class="time" v-if="objData.newSendNoticeTime">{{
taskTimeFormat(objData.newSendNoticeTime)
}}</text>
</navigator>
</view>
<view class="item">
<navigator
:url="'/pages/news/system?title=派件相关&type=' + 304"
open-type="redirect"
>
<view
class="icon delivery"
:class="objData.haveNewDispatchNotice ? 'active' : ''"
><icon></icon
></view>
<view class="text">
<view>派件相关</view>
<view>{{
objData.newDispatchNoticeTime
? "您有一个新的派件订单"
: "暂无消息"
}}</view>
</view>
<text class="time" v-if="objData.newDispatchNoticeTime">{{
taskTimeFormat(objData.newDispatchNoticeTime)
}}</text>
</navigator>
</view>
<view class="item">
<navigator
:url="'/pages/news/system?title=签收提醒&type=' + 302"
open-type="redirect"
>
<view
class="icon income"
:class="objData.haveNewReceiveNotice ? 'active' : ''"
><icon></icon
></view>
<view class="text">
<view>签收提醒</view>
<view>{{
objData.newReceiveNoticeTime
? "您有一个派件已签收"
: "暂无消息"
}}</view>
</view>
<text class="time" v-if="objData.newReceiveNoticeTime">{{
taskTimeFormat(objData.newReceiveNoticeTime)
}}</text>
</navigator>
</view>
<view class="item">
<navigator
:url="'/pages/news/system?title=快件取消&type=' + 303"
open-type="redirect"
>
<view
class="icon cancel"
:class="objData.haveNewCancelNotice ? 'active' : ''"
><icon></icon
></view>
<view class="text">
<view>快件取消</view>
<view>{{
objData.newCancelNoticeTime
? "您有一个快件已取消"
: "暂无消息"
}}</view>
</view>
<text class="time" v-if="objData.newCancelNoticeTime">{{
taskTimeFormat(objData.newCancelNoticeTime)
}}</text>
</navigator>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { taskTimeFormat } from "@/utils/index.js";
// 接口api
import { getNotice } from "@/pages/api/news.js";
// 导入组件
// ------定义变量------
let objData = ref({}); //列表数据
// 获取父组件数据
const props = defineProps({
// 当前触发的tab值
tabIndex: {
type: Number,
default: 1,
},
});
// ------定义变量------
const emit = defineEmits("getTabIndex"); //子组件向父组件事件传递
// ------生命周期------
onMounted(() => {
getOjb();
});
// ------定义方法------
// 获取系统通知
const getOjb = async () => {
await getNotice()
.then((res) => {
if (res.code === 200) {
objData.value = res.data;
}
})
.catch((err) => {
return uni.showToast({
title: err.msg,
duration: 1000,
icon: "none",
});
});
};
//把数据、方法暴漏给父组件
defineExpose({
getOjb,
});
</script>

View File

@@ -0,0 +1,68 @@
<!-- 公告详情页 -->
<template>
<!-- 自定义头部 -->
<view class="navHead"><UniNav :title="title" @goBack="goBack"></UniNav></view>
<!-- end -->
<!-- 列表 -->
<view class="pageBox newDetail">
<view class="tit">{{ objData.title }}</view>
<view class="time">{{ taskTimeFormat(objData.created) }}</view>
<view v-if="objData.firstContent" class="first">{{
objData.firstContent
}}</view>
<view>{{ objData.content }}</view>
</view>
<!-- end -->
</template>
<script setup>
import { ref } from "vue";
import { taskTimeFormat } from "@/utils/index.js";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// ------定义变量------
const title = ref("详情"); //nav标题
const pages = getCurrentPages(); //获取加载的页面获取当前页面路由信息uniapp 做安卓不支持 vue-router
const currentPage = pages[pages.length - 1]; //获取当前页面的对象
let objData = ref(JSON.parse(currentPage.$page.options.obj)); //基本数据 获取列表页传过来的详情页,此页没有详情接口
// ------定义方法------
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/news/index",
});
};
</script>
<style lang="scss" scoped>
body,
uni-page-body,
uni-page-head,
.uni-page-head {
background-color: #fff !important;
}
.pageBox {
box-shadow: inset 0 22rpx 22rpx 0 rgba(162, 162, 162, 0.06);
}
.newDetail {
padding: 60rpx 32rpx;
color: var(--neutral-color-font);
line-height: 48rpx;
font-size: var(--font-size-13);
.tit {
line-height: 60rpx;
font-size: var(--font-size-16);
color: var(--neutral-color-main);
font-weight: 600;
}
.time {
font-size: var(--font-size-12);
padding: 4rpx 0 28rpx;
}
.first {
padding: 15rpx 0 40rpx;
}
}
</style>

View File

@@ -0,0 +1,124 @@
body,
uni-page-body {
background: var(--neutral-color-background) !important;
}
.newBox {
.tabScroll {
margin-bottom: 32rpx;
background: var(--neutral-color-white);
::v-deep .uni-scroll-view-content {
font-size: var(--font-size-14) !important;
.scroll-row-item {
flex: 1;
text-align: center;
margin-right: 0;
}
}
}
}
::v-deep .newConBox {
.item {
line-height: 40rpx;
padding: 0 28rpx;
color: var(--neutral-color-font);
position: relative;
font-size: var(--font-size-12);
.navigator-wrap{
uni-navigator{
display: flex;
align-items: center;
}
}
.text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
padding: 32rpx 0 28rpx;
border-bottom: 1px solid var(--neutral-color-background);
view{
&:first-child{
color: var(--neutral-color-main);
font-size: var(--font-size-14);
padding-bottom: 4rpx;
}
}
}
.icon {
width: 64rpx;
height: 64rpx;
margin-right: 36rpx;
position: relative;
&.send {
background: url(@/static/icon17.png);
background-size: contain;
}
&.delivery {
background: url(@/static/icon27.png);
background-size: contain;
}
&.income {
background: url(@/static/icon18.png);
background-size: contain;
}
&.cancel {
background: url(@/static/icon19.png);
background-size: contain;
}
&.active {
color: var(--neutral-color-main);
icon {
position: absolute;
right: -14rpx;
top: 2rpx;
width: 14rpx;
height: 14rpx;
border-radius: 50%;
margin-right: 12rpx;
background: var(--essential-color-red);
}
}
}
.time{
position: absolute;
top: 32rpx;
right: 44rpx;
}
}
}
// 详情
// 系统列表
.systemList{
.item{
padding:30rpx 32rpx;
color: var(--neutral-color-font);
line-height: 52rpx;
margin-top: 40rpx;
.tit{
line-height: 40rpx;
padding-bottom: 30rpx;
color: var(--neutral-color-main);
border-bottom: 1px solid var(--neutral-color-background);
margin-bottom: 22rpx;
font-weight: 600;
}
.time{
display: flex;
align-items: center;
text{
flex:1
}
.redBtn{
display: inline-block;
}
}
}
}

View File

@@ -0,0 +1,105 @@
<!-- 消息列表页 -->
<template>
<!-- 自定义头部 -->
<view class="navHead">
<UniNav :title="title" @goBack="goBack"></UniNav>
</view>
<!-- end -->
<!-- 列表 -->
<view class="pageBox newBox">
<!-- 搜索列表 -->
<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>
<view class="homeSwiper">
<!-- 公告 -->
<Announcement ref="announcement" :tabIndex="tabIndex"></Announcement>
<!-- end -->
<!-- 系统通知 -->
<Notification
ref="notificat"
@getTabIndex="getTabIndex"
:tabIndex="tabIndex"
></Notification>
<!-- end -->
</view>
<!-- end -->
</view>
<!-- end -->
</template>
<script setup>
import { ref, reactive } from "vue";
import { useStore } from "vuex";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// 公告列表
import Announcement from "./components/announcement.vue";
// 系统通知
import Notification from "./components/notification.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const announcement = ref(); //定义ref
const notificat = ref();
const title = ref("消息"); //nav标题
const tabBars = reactive(["公告", "系统通知"]);
let scrollinto = ref("tab0"); //tab切换
let tabIndex = users.tabIndex === 1 ? ref(1) : ref(0); //当前tab
// ------定义方法------
// tab选项卡切换轮播
const changeTab = (index) => {
// 点击的还是当前数据的时候直接return
if (tabIndex.value == index) {
return;
}
// 触发tab切换接口
if (index === 0) {
// 当前tab值为0刷新公告列表
// announcement.value.getList()
} else {
// 当前tab值为1刷新系统通知
notificat.value.getOjb();
}
tabIndex.value = index;
store.commit("user/setTabIndex", index);
// 滑动
scrollinto.value = "tab" + index;
};
// 触发选项卡事件
const onChangeSwiperTab = (e) => {
changeTab(e.detail.current);
};
// 获取子组件传来的tabindex
const getTabIndex = (val) => {
tabIndex.value = val;
};
// 返回上一页
const goBack = () => {
uni.redirectTo({
url: "/pages/index/index",
});
store.commit("user/setNewType", null);
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,231 @@
<!-- 系统通知列表页 取件相关签收提醒快件取消 -->
<template>
<!-- 自定义头部 -->
<view class="navHead">
<UniNav
:title="title"
@goBack="goBack"
@handleAll="handleAll"
:rithtText="rithtText"
></UniNav>
</view>
<!-- end -->
<!-- 列表 -->
<view class="pageBox newBox">
<scroll-view
scroll-y="true"
:style="{ height: scrollH + 'px' }"
v-if="itemData.length > 0"
>
<view class="systemList">
<view class="boxBg item" v-for="(item, index) in itemData" :key="index">
<view class="tit" :class="item.isRead === 0 ? 'active' : ''">
<icon></icon>
<text v-if="title === '取件相关'">您有一个新的取件订单</text>
<text v-else-if="title === '派件相关'">您有一个新的派件订单</text>
<text v-else-if="title === '签收提醒'">您有一个派件已签收</text>
<text v-else>您有一个快件已取消</text>
</view>
<view class="address">{{ item.content }}</view>
<view class="time">
<text>{{ taskTimeFormat(item.created) }}</text>
<button class="uni-btn redBtn" @click="handleDetail(item)">
查看详情
</button>
</view>
</view>
</view>
<ReachBottom ref="loadMore"></ReachBottom>
</scroll-view>
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
</view>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { onReachBottom } from "@dcloudio/uni-app";
import { taskTimeFormat } from "@/utils/index.js";
import { useStore } from "vuex";
// 接口 api
import { getMessagesList, msgRead, msgAllRead } from "@/pages/api/news.js";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
//空页面
import EmptyPage from "@/components/uni-empty-page/index.vue";
// 下拉提示
import ReachBottom from "@/components/reach-bottom/index.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const pages = getCurrentPages(); //获取加载的页面获取当前页面路由信息uniapp 做安卓不支持 vue-router
const currentPage = pages[pages.length - 1].$page.options; //获取当前页面的对象
const title = currentPage.title; //nav标题
const type = currentPage.type; //当前派件类型
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
const emptyData = ref("暂无消息");
const rithtText = ref("全部已读");
let pageNumber = ref(1);
let totals = ref(0); //总页数
let pageNum = ref(1); //存放当前页
let page = reactive({
contentType: type,
page: 1,
pageSize: 10,
});
let reload = ref(false);
let scrollH = ref(null); //滚动高度
let isReadAll = ref(false); //是否已全读
let itemData = ref([]);
let ids = ref([]);
// 上下拉取
onReachBottom(() => {
if (pageNum.value >= Number(totals.value)) {
loadMore.value.status = "noMore";
return false;
} else {
loadMore.value.status = "loading";
let times = setTimeout(() => {
pageNum.value++;
getList();
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// ------生命周期------
onMounted(() => {
// // 调用接口
getList();
// 获取屏幕信息
uni.getSystemInfo({
success: (res) => {
scrollH.value = res.windowHeight - uni.upx2px();
},
});
});
//
// ------定义方法------
// 获取列表
const getList = async () => {
reload.value = true;
page = {
...page,
page: pageNum.value,
};
await getMessagesList(page).then((res) => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
itemData.value = itemData.value.concat(res.data.items);
itemData.value.map((val) => {
if (val.isRead === 0) {
ids.value.push(val.id);
}
});
totals.value = res.data.pages;
// 存储列表数据
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = "noMore";
}
} else {
itemData.value = [];
}
}
});
};
// 进入详情,标记已读
const handleDetail = async (item) => {
// 把任务id用vuex的方法存储方便其他页面调用
store.commit("user/setTaskId", item.relevantId);
store.commit("user/setTabIndex", 0);
ids.value = [];
ids.value.push(item.id);
// 进入详情前先调用已读信息接口
await msgRead(item.id).then((res) => {});
if (title === "取件相关") {
// 方便从详情跳回列表页
store.commit("user/setNewType", 301);
if (item.status === 1) {
uni.navigateTo({
url: "/pages/details/index",
});
} else {
uni.navigateTo({
url: "/pages/details/waybill",
});
store.commit("user/setIsNew", true);
}
} else if (title === "派件相关") {
if (item.status === 2) {
store.commit("user/setTaskStatus", 5);
store.commit("user/setIsNew", true);
} else {
store.commit("user/setTaskStatus", 4);
}
store.commit("user/setNewType", 304);
uni.navigateTo({
url: "/pages/details/waybill",
});
} else if (title === "签收提醒") {
store.commit("user/setTaskStatus", 5);
store.commit("user/setNewType", 302);
uni.navigateTo({
url: "/pages/details/waybill",
});
store.commit("user/setIsNew", true);
} else {
store.commit("user/setTaskStatus", null);
store.commit("user/setNewType", 303);
uni.navigateTo({
url: "/pages/details/waybill",
});
}
};
// 全部已读
const handleAll = async () => {
let contentType = null;
if (title === "取件相关") {
contentType = 301;
} else if (title === "派件相关") {
contentType = 304;
} else if (title === "签收提醒") {
contentType = 302;
} else {
contentType = 303;
}
await msgAllRead(contentType)
.then((res) => {
itemData.value = [];
pageNum.value = 1;
getList();
})
.catch((err) => {
isReadAll.value = true;
return uni.showToast({
title: err.msg,
duration: 1000,
icon: "none",
});
});
};
// 返回上一页
const goBack = () => {
store.commit("user/setTabIndex", 1);
if (users.taskStatus === -1) {
uni.redirectTo({
url: "/pages/index/index",
});
} else {
uni.redirectTo({
url: "/pages/news/index",
});
}
store.commit("user/setTaskStatus", 0);
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,45 @@
<template>
<uni-popup
ref="uppop"
type="center"
:animation="false"
class="comPop"
:mask-click="false"
>
<view class="con">用户已支付</view>
<view><button @click="goList">返回主页</button></view>
</uni-popup>
</template>
<script setup>
import { ref } from "vue";
// 获取父组件数据
const props = defineProps({
tipInfo: {
type: String,
default: "",
},
});
// ------定义变量------
const emit = defineEmits(); //子组件向父组件事件传递
const uppop = ref();
// ------定义方法------
// 打开弹层
const dialogOpen = () => {
uppop.value.open();
};
// 关闭弹层
const dialogClose = () => {
uppop.value.close();
};
// 返回任务列表页
const goList = () => {
uni.navigateTo({
url: "/pages/pickup/index",
});
};
// 向父组件暴露方法
defineExpose({
dialogOpen,
});
</script>

View File

@@ -0,0 +1,131 @@
body,
uni-page-body,
uni-page-head,
.uni-page-head {
background-color: var(--neutral-color-background) !important;
}
.pickUp {
padding: 200rpx 0 0;
font-size: 36rpx;
line-height: 50rpx;
font-weight: 600;
text-align: center;
icon {
background: url(@/static/chenggong@2x.png) no-repeat;
background-size: contain;
width: 146rpx;
height: 146rpx;
margin-bottom: 24rpx;
}
.btnBox {
padding-top: 64rpx;
}
::v-deep .navigator-wrap {
width: 300rpx;
height: 100rpx;
line-height: 100rpx;
display: inline-block;
background: var(--neutral-color-white) !important;
border-radius: 20rpx;
color: var(--neutral-color-main) !important;
font-size: var(--font-size-16);
font-weight: 500;
}
}
.scanPay {
.boxBg {
margin-top: 32rpx;
padding: 40rpx;
}
.srCan {
text-align: center;
line-height: 40rpx;
padding-bottom: 50rpx;
image {
width: 416rpx;
height: 410rpx;
}
.text {
color: var(--essential-color-red);
font-size: 50rpx;
font-weight: 6 00;
line-height: 58rpx;
text {
font-size: var(--font-size-13);
}
}
}
}
:deep(uni-canvas){
width: 416rpx;
height: 400rpx;
margin: 0 auto;
display: inline-block;
}
.payBox {
.item {
display: flex;
align-items: center;
font-size: var(--font-size-16);
line-height: 52rpx;
icon {
width: 48rpx;
height: 48rpx;
margin-right: 20rpx;
}
& > view {
&:first-child {
flex: 1;
display: flex;
align-items: center;
icon {
background: url(@/static/weChat.png) no-repeat;
background-size: contain;
}
}
::v-deep .uni-radio-input {
margin-right: 0 !important;
}
.checkRadio {
uni-radio {
vertical-align: super;
}
}
}
}
&:last-child {
& > view {
&:first-child {
icon {
background: url(@/static/Alipay.png) no-repeat;
background-size: contain;
}
}
}
}
}
.comPop {
::v-deep .uni-popup__wrapper {width: 70%;
background: var(--neutral-color-white) !important;
border-radius: 24rpx !important;
font-size: var(--font-size-16);
.con{
padding: 68rpx;
text-align: center;
border-bottom: 1px solid var(--neutral-color-background);
}
uni-button{
background: none;
line-height: 94rpx;
height: 100rpx;
color: var(--neutral-color-font);
font-size: var(--font-size-16);
}
}
}

View File

@@ -0,0 +1,96 @@
<!-- 取件成功页 -->
<template>
<!-- 自定义头部 -->
<UniNav
:title="users.isDelivery ? '签收成功' : '取件成功'"
@goBack="goBack"
></UniNav>
<!-- end -->
<view class="pickUp">
<icon></icon>
<view>{{ users.isDelivery ? "签收成功" : "取件成功" }}</view>
<view class="btnBox" v-if="users.isDelivery">
<navigator
url="/pages/pay/scanPay?pay=true"
open-type="redirect"
v-if="type === '2'"
>去收款</navigator
>
<view class="navigator-wrap" v-else @click="handleBack">返回主页</view>
</view>
<view class="btnBox" v-else>
<navigator
url="/pages/pay/scanPay?pay=true"
open-type="redirect"
v-if="type === '1'"
>去收款</navigator
>
<view class="navigator-wrap" v-else @click="handleBack">返回主页</view>
</view>
</view>
</template>
<script setup>
import { ref } from "vue";
import { useStore } from "vuex";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const pages = getCurrentPages(); //获取加载的页面获取当前页面路由信息uniapp 做安卓不支持 vue-router
const currentPage = pages[pages.length - 1].$page.options; //获取当前页面的对象
const type = currentPage.type;
// ------定义方法------
// 返回上一页
const handleBack = () => {
if (users.isDelivery) {
if (users.taskStatus === 6 && users.detailType === 2) {
store.commit("user/setTabIndex", 1);
uni.redirectTo({
url: "/pages/history/index",
});
} else {
uni.redirectTo({
url: "/pages/delivery/index",
});
}
} else {
if (users.taskStatus === 6) {
uni.redirectTo({
url: "/pages/history/index",
});
} else {
uni.redirectTo({
url: "/pages/pickup/index",
});
}
}
store.commit("user/setIsPickUp", false);
store.commit("user/setIsSign", false);
};
// 返回上一页
const goBack = () => {
if (users.isDelivery) {
if (users.paymentMethod === 2) {
store.commit("user/setIsCollect", true);
} else {
store.commit("user/setIsSign", true);
}
uni.redirectTo({
url: "/pages/details/waybill",
});
} else {
if (users.paymentMethod === 2) {
store.commit("user/setIsCollect", true);
}
uni.redirectTo({
url: "/pages/details/index",
});
}
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,227 @@
<!-- 扫码支付页 -->
<template>
<!-- 自定义头部 -->
<UniNav :title="title" @goBack="goBack"></UniNav>
<!-- end -->
<view class="pageBox scanPay">
<view class="boxBg srCan">
<view class="qr-box" v-if="qrShow"><canvas canvas-id="qrcode" /></view>
<image :src="qrCodeImg" v-else></image>
<view class="text">
<text>¥</text>
{{ detailsData.freight }}
</view>
<view>支付运费</view>
</view>
<view class="boxBg payBox" v-for="(item, index) in PayWayData" :key="index">
<view class="item">
<view>
<icon></icon>
{{ item.label }}
</view>
<view>
<view class="checkRadio"
><radio
:value="String(index)"
:class="index === current ? 'active' : ''"
:checked="index === current"
@click="checkbox(index)"
/></view>
</view>
</view>
</view>
</view>
<!-- 支付成功弹层 -->
<Uppop ref="uppop"></Uppop>
<!-- end -->
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useStore } from "vuex";
import uQRCode from "@/utils/uqrcode.js"; //引入uqrcode.js
// 获取数据
import { PayWayData } from "@/utils/commonData.js";
// 接口
import { getQrCode, paySucceed, getDetail } from "@/pages/api/index.js";
// 导入组件
// 导航组件
import UniNav from "@/components/uni-nav/index.vue";
import Uppop from "./components/uppop.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const uppop = ref();
const title = ref("扫码支付"); //nav标题
const pages = getCurrentPages(); //获取加载的页面获取当前页面路由信息uniapp 做安卓不支持 vue-router
const currentPage = pages[pages.length - 1]; //获取当前页面的对象
const type = currentPage.$page.options.type;
const pay = currentPage.$page.options.pay;
let isLeftText = true; //是否显示左侧文字
let current = ref(0); //当前触发付款方式的值
let qrCodeImg = ref("");
let times = ref(null);
let detailsData = ref({}); //详情数据
let qrShow = ref(false); //二维码支付有两种情况一种是后端返回的base64图片另一种是后端返回的二维码地址需要前端来做处理
onMounted(() => {
getDetails();
// 10秒钟监听一下付款状态是否付款
times.value = setInterval(() => {
getPaySucceed();
}, 10000);
});
// ------定义方法------
const getPaySucceed = async () => {
await paySucceed(users.detailsData.orderId).then((res) => {
if (res.code === 200) {
// 如果付款成功,弹出付款成功弹层
if (res.data) {
// 清除定时器
clearInterval(times.value);
uppop.value.dialogOpen();
}
}
});
};
// 获取订单详情
const getDetails = async () => {
await getDetail(users.taskId).then((res) => {
detailsData.value = res.data;
store.commit("user/setDetailsData", detailsData.value);
getCode(res.data);
});
};
// 获取支付二维码
const getCode = async (obj) => {
let data = users.payData;
let params = {};
if (data.tradingAmount) {
params = {
memo: data.memo ? data.memo : "备注",
payMethod: current.value === 0 ? 2 : 1,
productOrderNo: data.productOrderNo,
tradingAmount: data.tradingAmount,
};
} else {
params = {
memo: obj.remark ? obj.remark : "备注",
payMethod: current.value === 0 ? 2 : 1,
productOrderNo: obj.orderId,
tradingAmount: obj.freight,
};
}
// 网络慢的时候添加按钮loading
let times =
setTimeout(()=>{
uni.showLoading({
title: 'loading',
});
},500)
// 调用接口
await getQrCode(params).then((res) => {
// 操作成功后清除loading
uni.hideLoading();
clearTimeout(times)
const data = res.data;
const str = data.qrCode.slice(0, 10);
// 以base图片显示二维码
if (str === "data:image") {
qrCodeImg.value = data.qrCode.replace(/[\r\n]/g, "");
} else {
// 后端直接返回的二维码地址,需要前端处理一下返回的二维码地址
qrCodeFun(data.qrCode);
}
});
};
// 处理后端返回的地址生成二维码
const qrCodeFun = (valUrl) => {
qrShow.value = true;
uQRCode.make({
canvasId: "qrcode", //放置在哪个标签中将ID设置为相同
componentInstance: this,
text: valUrl, //valUrl为存放要传输的数据的变量
size: 200, //大小左右都为200 !注意要和容器大小一致
margin: 0, //不改变大小添加白色边框
backgroundColor: "#ffffff",
foregroundColor: "#000000",
fileType: "jpg",
errorCorrectLevel: uQRCode.errorCorrectLevel.H,
success: (res) => {},
});
};
// 选项框点击事件,参数是数据的下标
const checkbox = (index) => {
current.value = index;
getCode(detailsData.value);
};
// 返回上一页
const goBack = () => {
// 清除定时器
clearInterval(times.value);
// 返回派件详情
// 派件返回
if (users.isDelivery) {
if (users.detailType === 2 && users.taskStatus === 6) {
// 如果时从历史取派的取件列表进入的,返回的时候进入到历史取派列表
store.commit("user/setTabIndex", 1);
uni.redirectTo({
url: "/pages/history/index",
});
} else if (users.detailType === 0 && users.taskStatus === 5) {
// 如果是派件列表进入的,返回的时候进入到派件列表
store.commit("user/setTabIndex", 1);
uni.redirectTo({
url: "/pages/delivery/index",
});
} else if (users.isPickUp && users.paymentMethod === 2 && !pay) {
// 如果时从派件列表进入的,返回的时候进入到派件列表
uni.redirectTo({
url: "/pages/details/waybill",
});
} else {
uni.redirectTo({
url: "/pages/pay/index?type=" + users.paymentMethod,
});
pay = false;
}
} else {
// 取件返回
if (users.detailType === 1 && users.taskStatus === 6) {
// 如果是从历史取派的取件列表进入的,返回的时候进入到历史取派列表取件
store.commit("user/setTabIndex", 0);
uni.redirectTo({
url: "/pages/history/index",
});
} else if (
users.detailType === 2 &&
users.taskStatus === 3 &&
!users.isSearch
) {
// 如果是从取件列表进入的,返回的时候进入到取件列表的已取件
store.commit("user/setTabIndex", 1);
uni.redirectTo({
url: "/pages/pickup/index",
});
} else if (users.isPickUp && users.paymentMethod === 1 && !pay) {
// 如果时从取件列表进入的,返回的时候进入到取件列表
// 返回详情
uni.redirectTo({
url: "/pages/details/index",
});
} else if (users.isSearch) {
store.commit("user/setIsSearch", false);
uni.redirectTo({
url: "/pages/search/index",
});
} else {
uni.redirectTo({
url: "/pages/pay/index?type=" + users.paymentMethod,
});
pay = false;
}
}
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,263 @@
<!--已取件-->
<template>
<view v-if="tabIndex === 1">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 列表内容-->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<!-- 父组件传递过来的isAdmi来控制是否显示多选框 -->
<view class="checkbox" v-if="isAdmin">
<view class="checkRadio"><radio :value="String(index)" :class="item.selected === true ? 'active' : ''" :checked="item.selected" @click="checkbox(index)" /></view>
</view>
<!-- end -->
<view class="boxBg" :class="isAdmin ? 'adminActive' : ''">
<view class="tabList">
<view class="item" @click="handleDetails($event, item)">
<view class="titInfo">订单号SD{{ item.orderId }}</view>
<view class="address">寄件人{{ item.name }}</view>
<view class="address">取件地址{{ item.address }}</view>
<view class="time">取件时间{{ item.taskTime }}</view>
<view class="time" v-if="item.amount > 0 && item.status === 2 && item.paymentMethod === 1">运费{{ item.amount }}</view>
<text @click.stop="handleDetails($event, item)" class="delete" v-if="item.status === 2 && item.paymentStatus === 1 && item.paymentMethod === 1">
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
</view>
<!-- 空页面 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
//接口
import { getDeliveryList, getSearch } from '@/pages/api/index.js';
// 下拉提示
import ReachBottom from '@/components/reach-bottom/index.vue';
//空页面
import EmptyPage from '@/components/uni-empty-page/index.vue';
// 获取父组件数据
const props = defineProps({
// 当前高度
// 是否触发管理按钮
tabIndex: {
type: Number,
default: 1
},
// 是否触发管理按钮,此处是用来控制是否显示多选框
isAdmin: {
type: Boolean,
default: false
},
// // 搜索分页
searchInfo: {
type: Object,
default: () => ({})
}
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const emit = defineEmits(''); //子组件向父组件事件传递
const users = store.state.user;
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let itemData = ref([]);//列表数据
let reload = ref(false);//数据加载
let pages = ref(0);//总页数
let pageNum = ref(1);//当前页
let selected = reactive(new Map());
const emptyData = ref('暂无数据');//空页面提示
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
orderDistance: null,
orderTime: null,
filterOverTime: null,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 2
});
let searchPage = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10
});
// 上下拉取
onReachBottom(() => {
store.commit('user/setIsInput', true); //是否在文本框里输入了文字
if (pageNum.value >= pages.value) {
loadMore.value.status = 'noMore';
return false;
} else {
loadMore.value.status = 'loading';
let times = setTimeout(() => {
pageNum.value++;
if (props.searchInfo.keyword) {
getSearchList();
} else {
getList();
}
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// 计算是否全选或者单选
watch(users, (newValue, oldValue) => {
if (users.selectTaskData.size > 0) {
for (let [key, value] of users.selectTaskData) {
itemData.value.forEach(element => {
if (value === element.id) {
element.selected = true;
}
});
}
} else {
itemData.value.forEach(element => {
element.selected = false;
});
}
});
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
// 是否触发了搜索清空
if (users.isSearchClear) {
pageNum.value = 1;
store.commit('user/setSearchClear', false);
}
page = {
...page,
page: pageNum.value,
orderDistance: users.orderDistance,
orderTime: users.orderTime,
filterOverTime: users.filterOverTime
};
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
// 触发tab切换
// 如果触发了tab切换或者触发了搜索清空
if (users.istabChange || users.isSearchClear) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
}
});
};
// 搜索数据
const getSearchList = async () => {
reload.value = true;
let valNum = 0;
if (!users.isInput) {
valNum = 1;
pageNum.value = 1;
store.commit('user/setIsFiltrate', false);
}
searchPage = {
...searchPage,
keyword: props.searchInfo.keyword,
status: props.searchInfo.status,
taskType: props.searchInfo.taskType,
page: valNum ? 1 : pageNum.value
};
// 后端接口调用
await getSearch(searchPage).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange || !users.isInput) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
const getSelected = array => {
selected.value = array;
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 取件详情页
const handleDetails = (e, item) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', item.id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit('user/setTaskType', 1);
store.commit('user/setDetailType', 2); //从已取件跳入订单详情
if (item.status === 2 && item.paymentStatus === 1 && item.paymentMethod === 1) {
// 未付款进入付款二维码页面
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit('user/setTaskStatus', 3);
store.commit('user/setPayData', {});
uni.redirectTo({
url: '/pages/pay/scanPay'
});
} else {
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit('user/setTaskStatus', 2);
// 进入详情页
uni.redirectTo({
url: '/pages/details/waybill'
});
}
};
//把数据、方法暴漏给父组件
defineExpose({
getList,
getSearchList
});
</script>

View File

@@ -0,0 +1,250 @@
<template>
<view v-if="tabIndex === 2">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 列表内容-->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<view class="checkbox" v-if="isAdmin">
<view class="checkRadio"><radio :value="String(index)" :class="item.selected === true ? 'active' : ''" :checked="item.selected" @click="checkbox(index)" /></view>
</view>
<view class="boxBg" :class="isAdmin ? 'adminActive' : ''">
<view class="tabList cancelList">
<view class="item" @click.stop="handleDetails($event, item.id)">
<view>寄件人{{ item.name }}</view>
<view>取件地址{{ item.address }}</view>
<view>取消原因{{ item.cancelReason }}</view>
<view>原因描述{{ item.cancelReasonDescription }}</view>
<text @click.stop="handleOpen($event, item.id)" class="delete"><button class="uni-btn concelBtn">删除</button></text>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
</view>
<!-- 空页面 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
//接口
import { getDeliveryList, getSearch } from '@/pages/api/index.js';
// 下拉提示
import ReachBottom from '@/components/reach-bottom/index.vue';
//空页面
import EmptyPage from '@/components/uni-empty-page/index.vue';
// 获取父组件数据
const props = defineProps({
tabIndex: {
type: Number,
default: 0
},
// 是否触发管理按钮
isAdmin: {
type: Boolean,
default: false
},
// // 搜索分页
searchInfo: {
type: Object,
default: () => ({})
}
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const emit = defineEmits(''); //子组件向父组件事件传递
const users = store.state.user;
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let itemData = ref([]);//列表数据
let reload = ref(false); //数据加载
let pages = ref(0); //总页数
let pageNum = ref(1);//当前页
let selected = reactive(new Map());
const emptyData = ref('暂无数据');//空页面提示
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
orderDistance: null,
orderTime: null,
filterOverTime: null,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 3
});
let searchPage = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10
});
// 上下拉取
onReachBottom(() => {
store.commit('user/setIsInput', true); //是否在文本框里输入了文字
if (pageNum.value >= pages.value) {
loadMore.value.status = 'noMore';
return false;
} else {
loadMore.value.status = 'loading';
let times = setTimeout(() => {
pageNum.value++;
if (props.searchInfo.keyword) {
getSearchList();
} else {
getList();
}
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// 计算是否全选或者单选
watch(users, (newValue, oldValue) => {
if (users.selectTaskData.size > 0) {
for (let [key, value] of users.selectTaskData) {
itemData.value.forEach(element => {
if (value === element.id) {
element.selected = true;
}
});
}
} else {
itemData.value.forEach(element => {
element.selected = false;
});
}
});
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
// 是否触发了搜索清空
if (users.isSearchClear) {
pageNum.value = 1;
store.commit('user/setSearchClear', false);
}
page = {
...page,
page: pageNum.value,
orderDistance: users.orderDistance,
orderTime: users.orderTime,
filterOverTime: users.filterOverTime
};
// 后端接口调用
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
// 触发tab切换
// 如果触发了tab切换或者触发了搜索清空
if (users.istabChange || users.isSearchClear) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
}
});
};
// 搜索数据
const getSearchList = async () => {
reload.value = true;
let valNum = 0;
if (!users.isInput) {
valNum = 1;
pageNum.value = 1;
store.commit('user/setIsFiltrate', false);
}
searchPage = {
...searchPage,
keyword: props.searchInfo.keyword,
status: props.searchInfo.status,
taskType: props.searchInfo.taskType,
page: valNum ? 1 : pageNum.value
};
// 后端接口调用
await getSearch(searchPage).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange || !users.isInput) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
const getSelected = array => {
selected.value = array;
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 删除弹层
const handleOpen = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
emit('handleOpen', id);
};
// 取消详情页
const handleDetails = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit('user/setTaskType', 1);
// 已取件\已取消\去派件\已签收\详情页用的是一个,所以用类型status声明 1:待取件2:已取件,3:已取消,4:待派件,5:已签收
// 用vuex保存状态,因为当从详情页返回列表页的时候要显示对应的tab列表项
store.commit('user/setTaskStatus', 3);
// 进入详情页
uni.redirectTo({
url: '/pages/details/waybill'
});
};
//把数据、方法暴漏给父组件
defineExpose({
getList,
getSearchList
});
</script>

View File

@@ -0,0 +1,299 @@
<template>
<view v-if="tabIndex === 0">
<view v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<!-- 列表内容 -->
<view v-for="(item, index) in itemData" :key="index" class="expressage">
<!-- 父组件传递过来的isAdmi来控制是否显示多选框 -->
<view class="checkbox" v-if="isAdmin">
<view class="checkRadio"><radio :value="String(index)" :class="item.selected === true ? 'active' : ''" :checked="item.selected" @click="checkbox(index)" /></view>
</view>
<!-- end -->
<view class="boxBg" :class="isAdmin ? 'adminActive' : ''">
<view class="tabList">
<view class="item" @click.stop="handleDetails($event, item.id)">
<view class="titInfo">
<view>
<text class="name">{{ item.name }}</text>
{{ item.phone }}
<icon class="phone" @click.stop="handlePhone($event, item.phone)"></icon>
<icon class="note" @click.stop="handleNote"></icon>
</view>
</view>
<view class="address">{{ item.address }}</view>
<view class="address">{{ item.distance }}公里</view>
<view class="time">预约取件时间{{ taskTimeFormat(item.estimatedStartTime) }} {{ overTimeFormat(item.estimatedEndTime) }}</view>
<text @click.stop="handleCancel($event, item.id)" class="delete"><button class="uni-btn concelBtn">取消</button></text>
</view>
</view>
</view>
</view>
<!-- end -->
<!-- 上拉 -->
<ReachBottom ref="loadMore"></ReachBottom>
<!-- end -->
</scroll-view>
</view>
<!-- 空页面 -->
<view v-else><EmptyPage :emptyData="emptyData"></EmptyPage></view>
<!-- end -->
<!-- 拨打手机弹层 -->
<Phone ref="phone" :phoneData="phoneData"></Phone>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { getTimeDate } from '@/utils/index.js';
import { useStore } from 'vuex';
import { taskTimeFormat, overTimeFormat } from '@/utils/index.js';
//接口
import { getDeliveryList, getSearch } from '@/pages/api/index.js';
// 下拉提示
import ReachBottom from '@/components/reach-bottom/index.vue';
//空页面
import EmptyPage from '@/components/uni-empty-page/index.vue';
import Phone from '@/components/uni-phone/index.vue';
// 获取父组件数据
const props = defineProps({
// 当前触发的tab值
tabIndex: {
type: Number,
default: 0
},
// 是否触发管理按钮,此处是用来控制是否显示多选框
isAdmin: {
type: Boolean,
default: false
},
// // 搜索分页
searchInfo: {
type: Object,
default: () => ({})
},
isInput: {
type: Boolean,
default: false
}
});
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const emit = defineEmits(''); //子组件向父组件事件传递
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
const phone = ref();
let itemData = ref([]);//列表数据
let reload = ref(false);//数据加载
let pages = ref(0); //总页数
let pageNum = users.isFiltrate ? 1 : ref(1); //存放当前页
let selected = reactive(new Map());
const emptyData = ref('暂无数据');//空页面提示
const phoneData = ref('');
let page = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10,
orderDistance: null,
orderTime: null,
filterOverTime: null,
dateTime: getTimeDate(new Date()).veryDayDate,
taskStatus: 1
});
let searchPage = reactive({
latitude: users.loacation.latitude !== undefined ? users.loacation.latitude : 40.062595,
longitude: users.loacation.longitude !== undefined ? users.loacation.longitude : 116.372809,
page: 1,
pageSize: 10
});
onMounted(() => {});
// 上下拉取
onReachBottom(() => {
store.commit('user/setIsInput', true); //是否在文本框里输入了文字
if (pageNum.value >= Number(pages.value)) {
loadMore.value.status = 'noMore';
return false;
} else {
loadMore.value.status = 'loading';
let times = setTimeout(() => {
pageNum.value++;
if (props.searchInfo.keyword) {
getSearchList();
} else {
getList();
}
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// ------生命周期------
// 计算是否全选或者单选
watch(users, (newValue, oldValue) => {
if (users.selectTaskData.size > 0) {
for (let [key, value] of users.selectTaskData) {
itemData.value.forEach(element => {
if (value === element.id) {
element.selected = true;
}
});
}
} else {
itemData.value.forEach(element => {
element.selected = false;
});
}
});
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
//判断是否进行了距离、时间、超时任务筛选如果是当前页设为第一页上拉的数值设为1便于第二次上拉
let valNum = 0;
if (users.isFiltrate || users.isSearchClear) {
valNum = 1;
pageNum.value = 1;
// 如果触发了距离、时间、超时筛选
if (users.isFiltrate) {
store.commit('user/setIsFiltrate', false);
}
// 是否触发了搜索清空
if (users.isSearchClear) {
store.commit('user/setSearchClear', false);
}
}
page = {
...page,
page: valNum ? 1 : pageNum.value,
orderDistance: users.orderDistance,
orderTime: users.orderTime,
filterOverTime: users.filterOverTime
};
// 后端接口调用
await getDeliveryList(page).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
// 触发tab切换
// 如果触发了tab切换或者触发了搜索清空
if (users.istabChange || users.isSearchClear) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
}
});
};
// 搜索数据
const getSearchList = async () => {
reload.value = true;
let valNum = 0;
if (!users.isInput) {
valNum = 1;
pageNum.value = 1;
store.commit('user/setIsFiltrate', false);
}
searchPage = {
...searchPage,
keyword: props.searchInfo.keyword,
status: props.searchInfo.status,
taskType: props.searchInfo.taskType,
page: valNum ? 1 : pageNum.value
};
// 后端接口调用
await getSearch(searchPage).then(res => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (users.deliveryData.length === 0) {
itemData.value = [];
}
if (users.istabChange || !users.isInput) {
itemData.value = res.data.items;
store.commit('user/setIstabChange', false);
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
// 存储列表数据
store.commit('user/setDeliveryData', itemData.value);
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = 'noMore';
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
// 获取多选或者单选的数据
const getSelected = array => {
selected.value = array;
};
// 选项框点击事件,参数是数据的下标
const checkbox = index => {
emit('checkbox', index);
};
// 去取件
const handleDetails = (e, id) => {
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', id);
// 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
store.commit('user/setTaskType', 1);
store.commit('user/setIsBack', '');
store.commit('user/setDetailType', 2); //从待取件跳入订单详情
e.stopPropagation();
// 进入详情页
uni.redirectTo({
url: '/pages/details/index'
});
};
// 取消
const handleCancel = (e, id) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit('user/setTaskId', id);
// 进入订单取消申请页
uni.redirectTo({
url: '/pages/cancel/index'
});
};
// 拨打电话弹层
const handlePhone = (e, val) => {
// 阻止事件冒泡
e.stopPropagation();
phoneData.value = val;
phone.value.dialogOpen();
};
// 发短信
const handleNote = () => {
uni.showToast({
title: '程序员哥哥正在实现中',
duration: 1000,
icon: 'none'
});
};
//把数据、方法暴漏给父组件
defineExpose({ getList, getSearchList });
</script>

View File

@@ -0,0 +1,171 @@
<template>
<!-- 待取件 -->
<DealParcel
ref="dealparcel"
:tabIndex="tabIndex"
:isAdmin="isAdmin"
@checkbox="checkbox"
@getSelected="getSelected"
:searchInfo="searchInfo"
></DealParcel>
<!-- end -->
<!-- 已取件 -->
<AlreadyParcel
ref="already"
:tabIndex="tabIndex"
:isAdmin="isAdmin"
@checkbox="checkbox"
:searchInfo="searchInfo"
></AlreadyParcel>
<!-- end -->
<!-- 已取消 -->
<CancelParcel
:tabIndex="tabIndex"
ref="cancel"
:isAdmin="isAdmin"
@checkbox="checkbox"
@handleOpen="handleOpen"
:searchInfo="searchInfo"
></CancelParcel>
<!-- end -->
<!-- 提示窗 -->
<UniPopup
ref="popup"
:tipInfo="tipInfo"
@handleClick="handleClick"
></UniPopup>
<!-- end -->
</template>
<script setup>
import { ref, reactive } from "vue";
import { useStore } from "vuex";
//接口
import { taskDelete } from "@/pages/api/index.js";
// 导入组件
// 待取件
import DealParcel from "./components/dealParcel.vue";
// 已取件
import AlreadyParcel from "./components/alreadyParcel.vue";
// 已取消
import CancelParcel from "./components/cancelParcel.vue";
// 弹层
import UniPopup from "@/components/uni-popup/index.vue";
// 获取父组件数据
const props = defineProps({
// tab切换数据
tabBars: {
type: Array,
default: () => [],
},
tabIndex: {
type: Number,
default: 0,
},
// 是否触发管理按钮
isAdmin: {
type: Boolean,
default: false,
},
// 获取当前筛选的距离升序还是降序
orderDistance: {
type: Number,
default: 0,
},
// 获取当前筛选的时间升序还是降序
orderTime: {
type: Number,
default: 0,
},
// 获取当前筛选超时
filterOverTime: {
type: Number,
default: 0,
},
});
// ------定义变量------
const emit = defineEmits(""); //子组件向父组件事件传递
const store = useStore(); //设置、获取储存的数据
const users = store.state.user;
let popup = ref();
let dealparcel = ref();
let already = ref();
let cancel = ref();
const tipInfo = ref("确定要删除吗?");
let taskId = ref("");
let searchInfo = reactive({
keyword: null,
status: null,
taskType: null,
});
// ------生命周期------
// ------定义方法------
// 获取已经选的任务
const getSelected = (array) => {
emit("getSelected", array);
};
// 获取待取件列表方法
const dealPList = () => {
dealparcel.value.getList();
};
// 搜索待取件列表方法
const dealSearchList = () => {
dealparcel.value.getSearchList();
};
// 获取已取件列表方法
const alreadList = () => {
already.value.getList();
};
// 搜索已取件列表方法
const alreadSearchList = () => {
already.value.getSearchList();
};
// 获取取消件列表方法
const cancelList = () => {
cancel.value.getList();
};
// 搜索取消件列表方法
const cancelSearchList = () => {
cancel.value.getSearchList();
};
// 确认删除
const handleClick = async () => {
await taskDelete(taskId.value).then((res) => {
if (res.code === 200) {
store.commit("user/setDeliveryData", []);
cancel.value.getList();
store.commit("user/setIsDelete", true);
return uni.showToast({
title: "删除成功!",
duration: 1000,
icon: "none",
});
}
});
};
//左右滑动tab切换
const onChangeSwiperTab = (e) => {
emit("onChangeSwiperTab", e);
};
// 选项框点击事件,参数是数据的下标
const checkbox = (index) => {
emit("checkbox", index);
};
// 删除弹层id
const handleOpen = (id) => {
popup.value.dialogOpen();
taskId.value = id;
};
//把数据、方法暴漏给父组件
defineExpose({
dealPList,
dealSearchList,
alreadList,
alreadSearchList,
cancelList,
cancelSearchList,
searchInfo,
});
</script>

View File

@@ -0,0 +1,7 @@
.expressage {
.tabScroll {
.scroll-row-item {
margin-right: 80rpx;
}
}
}

View File

@@ -0,0 +1,237 @@
<!-- 取件页面 -->
<template>
<!-- 搜索nav -->
<SearchPage
@handleSearch="handleSearch"
ref="search"
@clearSearchData="clearSearchData"
></SearchPage>
<!-- end -->
<view>
<!-- tab切换 -->
<UniTab
:tabBars="tabBars"
ref="tab"
@getTabIndex="getTabIndex"
class="pickupTab"
></UniTab>
<!-- end -->
<!-- 距离\时间\超时筛选 -->
<ListFiltrate
v-if="tabIndex === 0"
@getList="getList"
class="pickupFilrate"
></ListFiltrate>
<!-- end -->
<!-- 取件状态列表 -->
<view
:class="tabIndex === 0 ? 'pickupBoxTop' : 'pickupTop'"
style="padding: 0 0 200rpx 0"
>
<TabList
:tabBars="tabBars"
:tabIndex="tabIndex"
:isAdmin="isAdmin"
@onChangeSwiperTab="onChangeSwiperTab"
@checkbox="checkbox"
:isInput="isInput"
ref="list"
></TabList>
</view>
<!-- end -->
</view>
<ExpressageFoot
ref="expressageFoot"
@getAdmin="getAdmin"
:isAdmin="isAdmin"
:selected="selected"
:tabIndex="tabIndex"
@allSelect="allSelect"
@handleClick="handleClick"
></ExpressageFoot>
<!-- footer -->
<UniFooter :pagePath="'pages/delivery/index'"></UniFooter>
<!-- end -->
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useStore } from "vuex";
// 基本数据
import { DeliveryData } from "@/utils/commonData.js";
// 接口api
import { taskBatchDelete } from "@/pages/api/index.js";
// 导入组件
// 导航
import UniNav from "@/components/uni-nav/index.vue";
// 搜索组件
import SearchPage from "@/components/uni-search/index.vue";
// 底部导航
import UniFooter from "@/components/uni-footer/index.vue";
// tab切换
import UniTab from "@/components/uni-tab/index.vue";
// 筛选
import ListFiltrate from "@/components/uni-list-filtrate/index.vue";
// 底部管理全选组件
import ExpressageFoot from "@/components/uni-expressage-foot/index.vue";
// list
import TabList from "./components/list.vue";
// ------定义变量------
const store = useStore();
const users = store.state.user;
const emit = defineEmits(""); //子组件向父组件事件传递
const tab = ref();
const list = ref(); //定义列表 ref
const search = ref(); //定义搜索 ref
const tabBars = DeliveryData;
let tabIndex = ref(0); //当前tab
let isInput = ref(false); //是否触发了输入框
let isAdmin = ref(false); //是否触发管理按钮
// 存储已选内容, 因为这个列表是增删很频繁的所以选用map而不是数组key对应的是数据的下标。至于value存放什么完全由自己定
let selected = reactive(new Map());
// ------生命周期------
onMounted(() => {
if (users.tabIndex) {
tabIndex.value = users.tabIndex;
}
if (users.tabIndex === 0) {
list.value.dealPList();
} else if (users.tabIndex === 1) {
list.value.alreadList();
} else {
list.value.cancelList();
}
});
// ------定义方法------
// 搜索
const handleSearch = () => {
list.value.searchInfo.taskType = 1;
list.value.searchInfo.keyword = search.value.searchVal;
store.commit("user/setIsInput", false); //是否在文本框里输入了文字默认false
store.commit("user/setDeliveryData", []);
if (tabIndex.value === 0) {
list.value.searchInfo.status = 1;
list.value.dealSearchList();
} else if (tabIndex.value === 1) {
list.value.searchInfo.status = 2;
list.value.alreadSearchList();
} else {
list.value.searchInfo.status = 3;
list.value.cancelSearchList();
}
};
// 批量删除
const handleClick = async () => {
const ids = [];
// 要批量删除的id
for (const [key, value] of selected) {
ids.push(value);
}
await taskBatchDelete({ idList: ids }).then((res) => {
if (res.code === 200) {
list.value.cancelList();
return uni.showToast({
title: "删除成功!",
duration: 1000,
icon: "none",
});
}
});
};
// 清除搜索
const clearSearchData = () => {
store.commit("user/setIsInput", true);
store.commit("user/setDeliveryData", []); //清空列表数据
store.commit("user/setSearchText", ""); //清空搜索框内容
store.commit("user/setSearchClear", true); //是否清空搜索框
list.value.searchInfo.keyword = ""; //清空搜索框内容
// 总页数清空
store.commit("user/setPages", 0);
if (tabIndex.value === 0) {
list.value.dealPList();
} else if (tabIndex.value === 1) {
list.value.alreadList();
} else {
list.value.cancelList();
}
};
// 获取tab切换当前的index
const getTabIndex = (index) => {
store.commit("user/setFilterOverTime", null);
search.value.searchVal = "";
store.commit("user/setSearchText", ""); //清空搜索框内容
store.commit("user/setSearchClear", true); //是否清空搜索框
tabIndex.value = index;
// 根据不同的tab值切更新 取件数据
if (index === 0) {
list.value.dealPList();
} else if (index === 1) {
list.value.alreadList();
} else {
list.value.cancelList();
}
selected.clear();
// 修改底部管理按钮状态因为取件、派件公用了一个底部管理组件因此切换tab的时候先把isAdmin设置成false以防数据混搅。
isAdmin.value = false;
// 存储列表数据
store.commit("user/setDeliveryData", []);
// 总页数清空
store.commit("user/setPages", 0);
store.commit("user/setSelectTaskData", new Map());
};
// 触发选项卡事件
const onChangeSwiperTab = (e) => {
tab.value.changeTab(e.detail.current);
};
// 获取foot底部组件的管理按钮触发值向列表页传递用来控制全选单选功能
const getAdmin = (val) => {
isAdmin.value = val;
};
// 给筛选组件传递,刷新列表
const getList = () => {
list.value.dealPList();
};
// 全选与反选事件
const allSelect = () => {
// 已经全选情况下,就是反选,全选就说明长度一样
let itemData = users.deliveryData;
if (selected.size === itemData.length) {
selected.clear(); // 全部清除
itemData.forEach((element) => {
element.selected = false; // 全部不选,就行了
});
}
// 还未全选的状态下
else {
itemData.forEach((element, index) => {
// 因为可能存在部分已经选择了,所以得先判断是否已存在,不存在才需要添加
if (!selected.has(index)) {
selected.set(index, element.id); // key是对应的下标index而value是可以自定义的
element.selected = true; // 设为选中
}
});
}
emit("getSelected", selected);
store.commit("user/setSelectTaskData", selected);
};
// 选项框点击事件,参数是数据的下标
const checkbox = (index) => {
// 选中的状态下再次点击,即为取消选中
let itemData = users.deliveryData;
if (itemData[index].selected) {
itemData[index].selected = false;
selected.delete(index); // 然后删除对应key即可
}
// 未选中状态下点击
else {
itemData[index].selected = true;
selected.set(index, itemData[index].id);
}
store.commit("user/setSelectTaskData", selected);
};
</script>
<style src="../../styles/expressage.scss" lang="scss" scoped></style>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,38 @@
<!--完成未付款-->
<template>
<view class="item" v-if="item.taskType === 2 && item.status === 2">
<view class="titInfo">运单号{{ item.transportOrderId }}</view>
<view class="address">收件人{{ item.name }}</view>
<view class="address">派件地址{{ item.address }}</view>
<view class="address">签收时间{{ item.taskTime }}</view>
<view class="time" v-if="item.amount > 0 && item.status === 2"
>运费{{ item.amount }}</view
>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 2
"
>
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
item: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(""); //子组件向父组件事件传递
//进入待取件详情
const handleDetails = (e, item) => {
emit("handleDetails", e, item);
};
</script>

View File

@@ -0,0 +1,40 @@
<!--已取件-->
<template>
<view class="item" v-if="item.taskType === 1 && item.status === 2">
<view class="titInfo">订单号SD{{ item.orderId }}</view>
<view class="address">寄件人{{ item.name }}</view>
<view class="address">取件地址{{ item.address }}</view>
<view class="time">取件时间{{ item.taskTime }}</view>
<view
class="time"
v-if="item.amount > 0 && item.status === 2 && item.paymentMethod === 1"
>运费{{ item.amount }}</view
>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 1
"
>
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
item: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(""); //子组件向父组件事件传递
//进入已取件详情
const handleDetails = (e, item) => {
emit("handleDetails", e, item);
};
</script>

View File

@@ -0,0 +1,24 @@
<!--已取消-->
<template>
<view class="expressage" v-if="item.taskType === 1 && item.status === 3">
<view class="cancelList">
<view class="item">
<view>寄件人{{ item.name }}</view>
<view>取件地址{{ item.address }}</view>
<view>取消原因{{ item.cancelReason }}</view>
<view>原因描述{{ item.cancelReasonDescription }}</view>
</view>
</view>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
item: {
type: Object,
default: () => ({}),
},
});
</script>

View File

@@ -0,0 +1,38 @@
<!--已签收-->
<template>
<view class="item" v-if="item.taskType === 2 && item.status === 5">
<view class="titInfo">运单号{{ item.transportOrderId }}</view>
<view class="address">收件人{{ item.name }}</view>
<view class="address">派件地址{{ item.address }}</view>
<view class="address">签收时间{{ item.taskTime }}</view>
<view class="time" v-if="item.amount > 0 && item.status === 2"
>运费{{ item.amount }}</view
>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="
item.status === 2 &&
item.paymentStatus === 1 &&
item.paymentMethod === 2
"
>
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</template>
<script setup>
// 获取父组件数据
const props = defineProps({
item: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(""); //子组件向父组件事件传递
//进入待取件详情
const handleDetails = (e, item) => {
emit("handleDetails", e, item);
};
</script>

View File

@@ -0,0 +1,47 @@
<!--待取件-->
<template>
<view
class="item"
v-if="(item.taskType === 1 || item.taskType === 2) && item.status === 1"
>
<view class="titInfo">
{{ item.name }}
<text>{{ item.phone }}</text>
</view>
<view class="address">{{ item.address }}</view>
<view class="distance">{{ item.distance }}公里</view>
<view class="time" v-if="item.taskType === 1"
>预约取件时间{{ taskTimeFormat(item.estimatedStartTime) }}
{{ overTimeFormat(item.estimatedEndTime) }}</view
>
<view class="time" v-else>运单号{{ item.transportOrderId }}</view>
<text
@click.stop="handleDetails($event, item)"
class="delete"
v-if="
((item.taskType === 1 && item.paymentMethod === 1) ||
(item.taskType === 2 && item.paymentMethod === 2)) &&
item.status === 2 &&
item.paymentStatus === 1
"
>
<button class="uni-btn btn-default">去收款</button>
</text>
</view>
</template>
<script setup>
import { taskTimeFormat, overTimeFormat } from '@/utils/index.js';
// 获取父组件数据
const props = defineProps({
item: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(''); //子组件向父组件事件传递
//进入待取件详情
const handleDetails = (e, item) => {
emit("handleDetails", e, item);
};
</script>

View File

@@ -0,0 +1,87 @@
body,
uni-page-body,
{
background-color: var(--neutral-color-white) !important;
}
.recentBox {
padding: 34rpx 38rpx;
font-size: var(--font-size-12);
box-shadow: inset 0 22rpx 22rpx 0 rgba(162,162,162,0.06);
background: #fff;
// min-height: calc(100vh - 280rpx);
.tit {
height: 36rpx;
line-height: 36rpx;
font-size: var(--font-size-13);
display: flex;
align-items: center;
text{
flex: 1;
line-height: 36rpx;
}
icon{
width: 32rpx;
height: 32rpx;
background: url(@/static/delete.png) no-repeat;
background-size: contain;
}
}
.recentList {
display: flex;
flex-flow: row wrap;
align-content: flex-start;
margin-left: -18rpx;
position: relative;
.item {
margin-top: 28rpx;
box-sizing: border-box;
flex: 0 0 25%;
height: 56rpx;
line-height: 56rpx;
background: var(--neutral-color-background);
border-radius: 28rpx;
margin-left: 18rpx;
padding: 0 32rpx;
}
}
}
.iconUp{
position: absolute;
bottom: 0;
right:22rpx;
text-align: center;
.icon_img{
width: 44rpx;
height: 44rpx;
}
}
.serachList{
.iconTip{
icon{
width:40rpx ;
height: 44rpx;
background: url(@/static/icon013.png);
background-size: contain;
}
.send{
background: url(@/static/icon014.png);
background-size: contain;
}
}
.item{
// box-shadow: 0 0 11px 11px rgba(162,162,162,0.06);
border-radius: 20rpx;
}
}
::v-deep .uni-scroll-view{
overflow:inherit !important;
}
.concelBtn{
position: absolute;
right: 20rpx;
bottom: 70rpx;
}
.boxBg{
box-shadow: 0 0 22rpx 22rpx rgba(162, 162, 162, 0.06)
}

View File

@@ -0,0 +1,395 @@
<!-- 首页搜索页 -->
<template>
<!-- 搜索nav -->
<SearchPage
ref="search"
@handleSearch="handleSearch"
@handleBlur="handleBlur"
@clearSearchData="clearSearchData"
@goBack="goBack"
:isShowCancel="isShowCancel"
></SearchPage>
<!-- end -->
<view class="searchTop">
<view class="pageBox">
<!-- 最近查找 -->
<view class="recentBox" v-if="!isClear">
<view class="tit">
<text>最近查找</text>
<icon @click="handleClear"></icon>
</view>
<view class="recentList">
<view
class="item"
v-for="(item, index) in listDataes.value"
:key="index"
@click="handleTransportOrderId(item)"
>{{ item }}</view
>
<view class="iconUp" v-if="!showDisplay">
<view
@click="showDisplay = !showDisplay"
v-if="itemDataRecent.length > 10"
><image
class="icon_img"
src="../../static/open.png"
mode=""
></image
></view>
</view>
</view>
</view>
<!-- end -->
<!-- 搜索列表 -->
<scroll-view scroll-y="true" class="swiperH" v-if="itemData.length > 0">
<view class="serachList">
<view class="">
<view class="tabList">
<view
class="boxBg"
v-for="(item, index) in itemData"
:key="index"
@click.stop="handleDetails($event, item)"
>
<!-- 待取件 -->
<StayPicup
:item="item"
@handleDetails="handleDetails"
></StayPicup>
<!-- end -->
<!-- 已取件 -->
<AlreadyPicUp
:item="item"
@handleDetails="handleDetails"
></AlreadyPicUp>
<!-- end -->
<!-- 取件取消 -->
<Canceled :item="item"></Canceled>
<!-- end -->
<!-- 已签收 -->
<SignFor :item="item" @handleDetails="handleDetails"></SignFor>
<!-- end -->
<!-- 已经完成到付未付款 -->
<Accomplish
:item="item"
@handleDetails="handleDetails"
></Accomplish>
<!-- end -->
</view>
</view>
</view>
</view>
<ReachBottom ref="loadMore"></ReachBottom>
</scroll-view>
<!-- end -->
<!-- 无数据 -->
<view v-if="itemData.length === 0 && isClear"
><EmptyPage :emptyData="emptyData"></EmptyPage
></view>
<!-- end -->
</view>
<!-- 提示窗示例 -->
<UniPopup
ref="popups"
:tipInfo="tipInfo"
@handleClick="clearData"
></UniPopup>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted, computed, onUnmounted } from "vue";
import { onReachBottom } from "@dcloudio/uni-app";
import { useStore } from "vuex";
// 接口
import {
getSearch,
getRecentSearch,
setMarkRecent,
clearRecentSearch,
} from "@/pages/api/index.js";
// 导入组件
// 搜索组件
import SearchPage from "@/components/uni-search/index.vue";
// 暂无搜索内容
import EmptyPage from "@/components/uni-empty-page/index.vue";
// 弹层
import UniPopup from "@/components/uni-popup/index.vue";
// 下拉提示
import ReachBottom from "@/components/reach-bottom/index.vue";
//
// 待取件
import StayPicUp from "./components/StayPicUp.vue";
// 已取件
import AlreadyPicUp from "./components/AlreadyPicUp.vue";
//已取消
import Canceled from "./components/Canceled.vue";
// 已签收
import SignFor from "./components/SignFor.vue";
// 完成未付款
import Accomplish from "./components/Accomplish.vue";
// ------定义变量------
const store = useStore(); //vuex获取、储存数据
const users = store.state.user;
const search = ref(); //定义搜索框的ref
let showDisplay = ref(false); //最近查找更多触发,触发之后按钮隐藏
let isClear = ref(false); //触发清除按钮
const tipInfo = ref("确定要全部清空吗?");
let popups = ref();
let isShowCancel = ref(true);
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let reload = ref(false);
let pages = ref(0); //总页数
let pageNum = ref(1); //存放当前页
const itemData = ref([]); //数据
const itemDataRecent = reactive([]); //近期数据
const emptyData = ref("没有找到相关内容");
let keyword = ref(""); //当前的搜索对象
let isInput = ref(false); //是否触发了输入框
let page = reactive({
latitude:
users.loacation.latitude !== undefined
? users.loacation.latitude
: 40.062595,
longitude:
users.loacation.longitude !== undefined
? users.loacation.longitude
: 116.372809,
page: 1,
pageSize: 10,
});
// 计算数据
const listDataes = computed(() => {
let testList = [];
if (showDisplay.value === false) {
if (itemDataRecent.length > 10) {
for (var i = 0; i < 10; i++) {
testList.push(itemDataRecent[i]);
}
} else {
testList = itemDataRecent;
}
return testList;
} else {
return itemDataRecent;
}
});
// 上下拉取
onReachBottom(() => {
isInput.value = true;
if (pageNum.value >= Number(pages.value)) {
loadMore.value.status = "noMore";
return false;
} else {
loadMore.value.status = "loading";
let times = setTimeout(() => {
pageNum.value++;
getList();
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// 离开此页面
onUnmounted(() => {
itemData.value = [];
isInput.value = false;
});
// ------生命周期------
onMounted(() => {
init();
if (users.searchText !== "") {
keyword.value = users.searchText;
search.value.searchVal = users.searchText;
getList();
}
});
// 获取初始值
const init = () => {
getRecent(); //近期搜索数据
};
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
page = {
...page,
page: pageNum.value,
keyword: keyword.value,
};
// 后端接口调用
await getSearch(page).then((res) => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (!isInput.value) {
itemData.value = res.data.items;
} else {
itemData.value = itemData.value.concat(res.data.items);
}
pages.value = res.data.pages;
if (Number(res.data.pages) === pageNum.value) {
loadMore.value.status = "noMore";
}
} else {
itemData.value = [];
}
// 有搜索数据的时候隐藏最近查询标题和清除按钮
if (itemData.value.length > 0) {
isClear.value = true;
}
}
});
};
// 显示最近查找
const getRecent = async () => {
await getRecentSearch().then((res) => {
if (res.code === 200) {
itemDataRecent.value = res.data;
// 没数据的时候隐藏最近查询标题和清除按钮
if (itemDataRecent.value.length === 0) {
isClear.value = true;
}
}
});
};
// 搜索框搜索
const handleSearch = (val) => {
if (val.value.trim().length > 0) {
isInput.value = false;
keyword = val;
getList();
}
};
// input焦点
const handleBlur = () => {
isInput.value = true;
};
// 清除最近查找
const handleClear = () => {
popups.value.dialogOpen();
};
// 点击关闭按钮之后页面为显示最近查找页
const clearSearchData = () => {
itemData.value = []; //清空搜索列表
// 设置搜索的内容,从详情页返回搜索页的时候显示默认搜索的内容
isClear.value = false;
store.commit("user/setSearchText", "");
getRecent();
};
// 清空
const clearData = async (val) => {
isClear.value = val;
await clearRecentSearch().then(() => {
if (res.code === 200) {
uni.showToast({
title: "清除成功",
icon: "none",
});
}
});
};
// 标记为最近查找
const setRecent = async (id) => {
await setMarkRecent(id);
itemData.value = []; //清空搜索列表
};
// 取件详情页
const handleDetails = (e, item) => {
// 阻止事件冒泡
e.stopPropagation();
// 把任务id用vuex的方法存储方便其他页面调用
store.commit("user/setTaskId", item.id);
// // 由于取件详情地址和派件详情地址样式一致,所以用类型 1取件2派件区分开
// store.commit('user/setTaskType', 1);
// 设置是否由搜索页进的详情页,方便详情页返回
store.commit("user/setIsSearch", true);
// 设置搜索的内容,从详情页返回搜索页的时候显示默认搜索的内容
store.commit("user/setSearchText", search.value.searchVal);
// 如果有运单号标记为最近查询记录
if (item.transportOrderId) {
setRecent(item.transportOrderId);
}
// 取件
if (item.taskType === 1) {
// 待取件
if (item.status === 1) {
uni.redirectTo({
url: "/pages/details/index",
});
return false;
} else if (item.status === 2) {
// 如果是已取件
// 未付款的状态进入二维码付款页面
if (item.paymentStatus === 1 && item.paymentMethod === 1) {
store.commit("user/setDetailType", 2);
store.commit("user/setTaskStatus", 3);
store.commit("user/setPayData", {});
uni.redirectTo({
url: "/pages/pay/scanPay",
});
return false;
} else {
// 已取件
store.commit("user/setTaskStatus", 2);
}
} else {
// 取消的订单
store.commit("user/setTaskStatus", 3);
}
// 如果是已付款或者是到付,取消的订单,进入运单详情页
uni.redirectTo({
url: "/pages/details/waybill",
});
} else {
// 派件
// 待派件
if (item.status === 1) {
store.commit("user/setTaskStatus", 4);
} else if (item.status === 2) {
// 如果是已取件
// 未付款的状态进入二维码付款页面
if (item.paymentStatus === 1 && item.paymentMethod === 2) {
store.commit("user/setPayData", {});
uni.redirectTo({
url: "/pages/pay/scanPay",
});
return false;
} else {
store.commit("user/setTaskStatus", 5);
uni.redirectTo({
url: "/pages/details/waybill",
});
}
}
// 如果是去派件\已付款或者是寄付,进入运单详情页
uni.redirectTo({
url: "/pages/details/waybill",
});
}
};
// 根据最近查找的运单id搜索
const handleTransportOrderId = (val) => {
// 给搜索对象赋值
keyword.value = val;
// 把值赋给子组件的搜索框
search.value.searchVal = val;
getList();
};
// 回首页
const goBack = () => {
uni.redirectTo({
url: "/pages/index/index",
});
};
</script>
<style src="./../../styles/expressage.scss" lang="scss" scoped></style>
<style src="./index.scss" lang="scss" scoped></style>
<style lang="scss">
body {
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,42 @@
.starPage{
.page{
background: #fff url(@/static/goink.jpg) no-repeat 50% 50%;
background-size:contain;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.tit{
width: 38rpx;
line-height: 40rpx;
font-size: 36rpx;
margin: 0 auto;
letter-spacing: 0.42rpx;
text-align: center;
text{
display: block;
padding:0 0 10rpx 0;
}
}
.map{
background: url(@/static/star.png) no-repeat;
background-size: contain;
width: 320rpx;
height: 232rpx;
margin-top: 80rpx;
margin-bottom: 142rpx;
}
.logo{
background: url(@/static/logo.png) no-repeat;
background-size: contain;
width: 712rpx;
height: 184rpx;
left: 50%;
transform: translate(-50%, 0);
position: absolute;
bottom: 5%;
}
}

View File

@@ -0,0 +1,26 @@
<!-- 引导页 -->
<template>
<view class="starPage">
<view class="page"> </view>
</view>
</template>
<script>
import { onMounted } from "vue";
export default {
name: "StarPage",
setup: (props) => {
onMounted(() => {
const times = setTimeout(() => {
uni.redirectTo({
url: "/pages/login/user",
});
clearTimeout(times);
}, 3000);
});
return {};
},
};
</script>
<style src="./index.scss" lang="scss" scoped></style>

View File

@@ -0,0 +1,201 @@
<!-- 转单页 -->
<template>
<!-- 搜索nav -->
<view class="navBox">
<view class="search">
<!-- 头部自定义导航 -->
<view class="uni-navbar">
<view class="input-view">
<uni-icons
class="input-uni-icon"
type="search"
size="18"
color="#999"
/>
<input
confirm-type="search"
class="nav-bar-input"
type="text"
v-model="searchVal"
placeholder="请输入快递员账号查询"
@input="handleSearch"
/>
<!-- 先保留后期可能要加次功能 -->
<!-- <view class="scanIcon" @click="handleQr"></view> -->
</view>
<view class="concelBox" @click="handleCancel" v-if="isShowCancel"
>取消</view
>
</view>
<!-- end -->
</view>
</view>
<!-- end -->
<view class="boxTop">
<view class="btnBox turnBox" v-if="itemData.length > 0">
<scroll-view scroll-y="true">
<view
class="boxBg"
v-for="(item, index) in itemData"
:key="index"
@click="handleOpen(item.userId)"
>
<view class="turnItem">
<view class="item">
<view>{{ item.employeeNumber }}</view>
<view>{{ item.name }}</view>
</view>
</view>
</view>
<ReachBottom ref="loadMore"></ReachBottom>
</scroll-view>
</view>
<view v-else
><EmptyPage :emptyData="emptyData" :emptyImage="'emptyImage'"></EmptyPage
></view>
<!-- 提示窗示例 -->
<UniPopup
ref="popup"
:tipInfo="tipInfo"
@handleClick="handleClick"
></UniPopup>
<!-- end -->
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { onReachBottom } from "@dcloudio/uni-app";
import { useStore } from "vuex";
// 接口api
import { getSameAgency, transferBatch } from "@/pages/api/index.js";
// 导入组件
// 下拉提示
import ReachBottom from "@/components/reach-bottom/index.vue";
// 弹层
import UniPopup from "@/components/uni-popup/index.vue";
//空页面
import EmptyPage from "@/components/uni-empty-page/index.vue";
// ------定义变量------
const store = useStore(); //设置、获取数据
const users = store.state.user;
const loadMore = ref(); //定义子组件的ref,可以调取子组件的值
let popup = ref(); //定义ref
let isShowCancel = ref(true);
const tipInfo = ref("确定要转单吗?"); //转单提示语
let reload = ref(false);
let pages = ref(0); //总页数
let pageNum = ref(1);
const emptyData = ref("暂无排班内快递员");
const anotherCourierId = ref(""); //快递员id
const searchVal = ref(""); //搜索内容
let page = reactive({
keyword: "",
page: 1,
pageSize: 10,
});
let itemData = ref([]); //列表数据
// 上下拉取
onReachBottom(() => {
if (pageNum.value >= pages.value) {
loadMore.value.status = "noMore";
return false;
} else {
loadMore.value.status = "loading";
let times = setTimeout(() => {
pageNum.value++;
getList();
}, 1000); //这里延时一秒在加载方法有个loading效果
}
});
// ------生命周期------
// ------定义方法------
// 获取数据
const getList = async () => {
reload.value = true;
await getSameAgency(page).then((res) => {
if (res.code === 200) {
if (res.data) {
reload.value = false;
if (res.data.items) {
itemData.value = itemData.value.concat(res.data.items);
pages.value = res.data.pages;
if (Number(pages.value) === pageNum.value) {
loadMore.value.status = "noMore";
}
}
} else {
itemData.value = null;
}
}
});
};
// 转单弹层
const handleOpen = (id) => {
anotherCourierId.value = id;
// 打开确认转单弹层
popup.value.dialogOpen();
};
// 确认转单
const handleClick = async () => {
// 获取已经选择的任务id
let ids = [];
for (const [key, value] of users.selectTaskData) {
ids.push(value);
}
let params = {
anotherCourierId: anotherCourierId.value,
idList: ids,
};
await transferBatch(params).then((res) => {
if (res.code === 200) {
uni.navigateTo({
url: "/pages/pickup/index",
});
return uni.showToast({
title: "转单成功!",
duration: 1000,
icon: "none",
});
}
});
};
const handleSearch = () => {
page.keyword = searchVal.value;
itemData.value = [];
getList();
};
// 取消搜索
const handleCancel = () => {
searchVal.value = "";
store.commit("user/setIsDelivery", false);
store.commit("user/setTabIndex", 0);
clearData();
if (users.isDelivery) {
uni.redirectTo({
url: "/pages/delivery/index",
});
} else {
uni.redirectTo({
url: "/pages/pickup/index",
});
}
};
// 返回上一页
const goBack = () => {
clearData();
uni.redirectTo({
url: "/pages/delivery/index",
});
};
// 清空数据
const clearData = () => {
// 存储列表数据
store.commit("user/setDeliveryData", []);
// 总页数清空
store.commit("user/setPages", 0);
store.commit("user/setSelectTaskData", new Map());
};
</script>
<style src="../../styles/expressage.scss" lang="scss" scoped></style>