Files
delivery_manage_front/src/views/order/historyDelivery/index.vue
2026-02-06 10:57:56 +08:00

711 lines
23 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryRef" v-show="showSearch" label-width="100px">
<el-row>
<el-col :span="4">
<el-form-item label="目的地" prop="destName">
<el-input v-model="queryParams.destName" placeholder="请输入目的地" clearable
@keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="配送日期" prop="deliveryDate">
<el-date-picker clearable v-model="queryParams.deliveryDate" type="date"
value-format="YYYY-MM-DD" placeholder="请选择配送日期">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="配送车牌" prop="vehiclePlate">
<el-input v-model="queryParams.vehiclePlate" placeholder="请输入配送车牌" clearable
@keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="接收单位" prop="receiverOrgName">
<el-input v-model="queryParams.receiverOrgName" placeholder="请输入接收单位" clearable
@keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport"
v-hasPermi="['document:order:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="orderList" @selection-change="handleSelectionChange">
<!-- <el-table-column type="selection" width="55" align="center" /> -->
<el-table-column label="配送单号" align="center" prop="orderNo" width="200" />
<el-table-column label="出库单据号" align="center" prop="billNoCk" width="200" >
<template #default="scope">
<span v-if="scope.row.billNoCk">{{ scope.row.billNoCk }}</span>
<el-tag type="danger" v-else>手动创建</el-tag>
</template>
</el-table-column>
<el-table-column label="起始地点名称" align="center" prop="originName" />
<el-table-column label="目的地点名称" align="center" prop="destName" />
<el-table-column label="配送日期" align="center" prop="deliveryDate" width="180">
<template #default="scope">
<span>{{ parseTime(scope.row.deliveryDate, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="配送车牌" align="center" prop="plateNo" />
<!-- <el-table-column label="配送费" align="center" prop="" /> -->
<el-table-column label="发货人姓名" align="center" prop="shipperName" />
<el-table-column label="接收人姓名" align="center" prop="receiverName" />
<el-table-column label="明细数量" align="center" prop="itemCount">
<template #default="scope">
<span>{{ scope.row.items.length }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="250">
<template #default="scope">
<el-button link type="primary" @click="handlePic(scope.row)">现场照片</el-button>
<el-button link type="primary" @click="handleBill(scope.row)">配送单</el-button>
<el-button link type="primary" @click="handleHistory(scope.row)">历史轨迹</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改配送单据主对话框 -->
<el-dialog title="历史轨迹" v-model="open" :fullscreen="true" append-to-body>
<div style="position: relative">
<div style="position: absolute; right: 10px; top: 10px; z-index: 1">
<el-button @click="silderInput">开始回放</el-button>
<el-button @click="pauseAnimation">暂停回放</el-button>
<el-button @click="resumeAnimation">继续回放</el-button>
<!-- <el-slider v-model="sliderVal" :step="1" @input="sliderChange" @change="silderInput" :max="lineArr.length - 1"
:min="0"></el-slider> -->
</div>
<div style="position: absolute; left: 50px; bottom: 30px;z-index: 1; width: 95%; height: 20px; "></div>
<div id="amapcontainer" style="width: 100%; height: 770px"></div>
</div>
</el-dialog>
<el-dialog title="物料明细" v-model="openDetail" append-to-body width="80%">
<el-table v-loading="detailLoading" :data="wlList">
<el-table-column label="项目号" align="center" prop="xmNo" width="150" />
<el-table-column label="项目描述" align="center" prop="xmMs" width="250" />
<el-table-column label="物料号" align="center" prop="wlNo" width="100" />
<el-table-column label="物料描述" align="center" prop="wlMs" width="250" />
<el-table-column label="供应商名称" align="center" prop="gysMc" width="200" />
<el-table-column label="合同单价" align="center" prop="htDj" />
<el-table-column label="订单编号" align="center" prop="sapNo" width="150" />
<el-table-column label="计量单位" align="center" prop="dw" />
<el-table-column label="实际入库数量" align="center" prop="realQty" width="120" />
<el-table-column label="库位码" align="center" prop="pcode" width="120" />
<el-table-column label="托盘码" align="center" prop="trayCode" />
<el-table-column label="身份码" align="center" prop="entityId" width="200" />
<el-table-column label="物资类型" align="center" prop="wlTypeName" />
<el-table-column label="所属仓库" align="center" prop="cangkuName" width="150" />
<el-table-column label="库龄" align="center" prop="stockAge" />
<el-table-column label="理货员" align="center" prop="lihuoYName" />
<el-table-column label="备注" align="center" prop="remark" width="200" />
</el-table>
</el-dialog>
<el-dialog title="配送单" v-model="openBill" append-to-body width="80%">
<div class="delivery-form">
<!-- 表格容器 -->
<table border="1" cellpadding="5" cellspacing="0"
style="width: 90%; margin: 20px auto; border-collapse: collapse; font-size: 14px;">
<!-- 标题行 -->
<tr>
<td colspan="8" style="text-align: center; font-weight: bold; font-size: 18px;">
国网唐山供电公司贾庵子仓库货物配送单
</td>
</tr>
<!-- 配送项目名称 & 项目编号 -->
<tr>
<td colspan="2">配送项目名称</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].xmMs : '' }}</td>
<td style="width: 100px;">项目编号</td>
<td colspan="2">{{ billData.length > 0 ? billData[0].xmNo : '' }}</td>
</tr>
<!-- 配送地址 -->
<tr>
<td colspan="2">配送地址</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].destName : '' }}</td>
<td style="width: 100px;">配送日期</td>
<td colspan="2">{{ billData.length > 0 ? billData[0].deliveryDate : '' }}</td>
</tr>
<!-- 配送车号 -->
<tr>
<td colspan="2">配送车号</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].plateNo : '' }}</td>
<td style="width: 100px;">司机签名</td>
<td colspan="2">
<el-image style="width: 50px; height: 20px"
:src="urlFun(billData.length > 0 ? billData[0].attachments : [], 'SIGN_DRIVER')"
:preview-src-list="urlFun1(billData.length > 0 ? billData[0].attachments : [], 'SIGN_DRIVER')"
/>
</td>
</tr>
<!-- 接收单位 -->
<tr>
<td colspan="2">接收单位</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].receiverOrgName : '' }}</td>
<td style="width: 100px;">配送吨数</td>
<td colspan="2">{{ billData.length > 0 ? billData[0].deliveryTon : '' }}</td>
</tr>
<!-- 发货人联系方式 -->
<tr>
<td colspan="2">发货人联系方式姓名</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].shipperName : '' }}/{{ billData.length > 0 ?
billData[0].shipperPhone : '' }}</td>
<td style="width: 100px;">制单</td>
<td colspan="2">{{ billData.length > 0 ? billData[0].makerUserName : '' }}</td>
</tr>
<!-- 接收人联系 -->
<tr>
<td colspan="2">接收人联系方式姓名</td>
<td colspan="3">{{ billData.length > 0 ? billData[0].receiverName : '' }}/{{ billData.length > 0 ?
billData[0].receiverPhone : '' }}</td>
<td style="width: 100px;">出库复核</td>
<td colspan="2">
<el-image style="width: 50px; height: 20px"
:src="urlFun(billData.length > 0 ? billData[0].attachments : [], 'SIGN_COURIER')"
:preview-src-list="urlFun1(billData.length > 0 ? billData[0].attachments : [], 'SIGN_COURIER')"
/>
</td>
</tr>
<!-- 物资明细表 -->
<tr>
<td style="width: 50px;">序号</td>
<td>物料编码</td>
<td class="wzmc">物资名称</td>
<td>数量</td>
<td>单位</td>
<td style="width: 100px;">订单号</td>
<td>供应商名称</td>
<td>备注</td>
</tr>
<!-- 数据行 1 -->
<tr v-for="(item, index) in billData" :key="index">
<td style="width: 50px;">{{ index + 1 }}</td>
<td>{{ item.wlNo }}</td>
<td class="wzmc">{{ item.wlMs }}</td>
<td>{{ item.realQty }}</td>
<td>{{ item.dw }}</td>
<td style="width: 150px;">{{ item.sapNo }}</td>
<td>{{ item.gysMc }}</td>
<td>{{ item.remark }}</td>
</tr>
<!-- 接收物资状态 -->
<tr>
<td colspan="2">接收物资状态</td>
<td colspan="6">
<label><input type="radio" name="status"
:checked="billData.length > 0 ? (billData[0].receiveStatus == 1 ? 'true' : '') : ''" />
数量齐全、状态完好</label>
<label><input type="radio" name="status"
:checked="billData.length > 0 ? (billData[0].receiveStatus == 2 ? 'checked' : '') : ''" />
存在问题:</label>
<input type="text" style="width: 70%;"
:value="billData.length > 0 ? billData[0].receiveProblem : ''" readonly />
</td>
</tr>
<!-- 接收人确认 -->
<!-- :preview-src-list="srcList" -->
<tr>
<td colspan="2">接收人确认</td>
<td colspan="6">
签字:<el-image style="width: 50px; height: 20px"
:src="urlFun(billData.length > 0 ? billData[0].attachments : [], 'SIGN_RECEIVER')"
:preview-src-list="urlFun1(billData.length > 0 ? billData[0].attachments : [], 'SIGN_RECEIVER')"
/>
<br /><br />
日期:
<input type="text" readonly style="width: 50px;" :value="deliveryDate.year" /> 年
<input type="text" readonly style="width: 50px;" :value="deliveryDate.month" /> 月
<input type="text" readonly placeholder="" style="width: 50px;"
:value="deliveryDate.day" /> 日
</td>
</tr>
</table>
</div>
</el-dialog>
<el-dialog title="现场照片" v-model="openPic" append-to-body>
<div class="demo-image">
<div v-for="(fit, index) in picList" :key="index" class="block">
<span class="demonstration">{{ bizTypeCn(fit) }}</span>
<el-image style="width: 100px; height: 100px" :src="fit.url" :preview-src-list="srcList" />
</div>
</div>
</el-dialog>
</div>
</template>
<script setup name="Order">
import { listOrder, detailOrder, getHistory } from "@/api/waitDelivery/waitDelivery"
import AMapLoader from "@amap/amap-jsapi-loader";
import { ref } from "vue";
const { proxy } = getCurrentInstance()
const orderList = ref([])
const open = ref(false)
const openDetail = ref(false)
const openBill = ref(false)
const openPic = ref(false)
const loading = ref(true)
const detailLoading = ref(false)
const showSearch = ref(true)
const ids = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")
const wlList = ref([])
const data = reactive({
queryParams: {
pageNum: 1,
pageSize: 10,
orderStatus: 3,
destName: null, // 目的地名称
deliveryDate: null, // 配送日期
plateNo: null, // 车牌号
receiverOrgName: null // 接收单位
},
})
//配送单
const billData = ref([])
const deliveryDate = ref({})
const srcList = ref([])
const picList = ref([])
const map = ref(null) // 高德地图实例
const int = ref(null)
const lineArr = ref([]) // 轨迹
const marker = ref(null)
const polyline = ref(null)
const passedPolyline = ref(null)
const percentage = ref(50) // 进度条进度
const sliderVal = ref(0)
const progressTime = ref(0)
const markerSpeed = ref(100) // 初始化速度
const afterData = ref([])//后面的数据
const { queryParams } = toRefs(data)
/** 查询配送单据主列表 */
function getList() {
loading.value = true
listOrder(queryParams.value).then(response => {
orderList.value = response.rows
total.value = response.total
loading.value = false
})
}
// 取消按钮
function cancel() {
open.value = false
reset()
}
// 表单重置
function reset() {
proxy.resetForm("orderRef")
}
/** 搜索按钮操作 */
function handleQuery() {
queryParams.value.pageNum = 1
getList()
}
/** 重置按钮操作 */
function resetQuery() {
proxy.resetForm("queryRef")
handleQuery()
}
// 多选框选中数据
function handleSelectionChange(selection) {
ids.value = selection.map(item => item.id)
single.value = selection.length != 1
multiple.value = !selection.length
}
/** 新增按钮操作 */
function handleAdd() {
reset()
open.value = true
title.value = "添加配送单据主"
}
//明细
// function handleDetail(row) {
// openDetail.value = true
// // detailLoading.value = true
// detailOrder({orderNo:row.orderNo}).then(response=>{
// // detailLoading.value = false
// wlList.value = response.data
// })
// }
//现场照片
function handlePic(row) {
openPic.value = true
detailOrder({ orderNo: row.orderNo }).then(response => {
if (response.data.length > 0) {
picList.value = response.data[0].attachments.filter(item => item.bizType == 'PHOTO_SITE' || item.bizType == 'PHOTO_BILL')
srcList.value = picList.value.map(item => item.url)
}
})
}
function bizTypeCn(row) {
if (row.bizType == 'PHOTO_SITE' && row.scene == 'ORIGIN') {
return '起点照片'
} else if (row.bizType == 'PHOTO_SITE' && row.scene == 'DEST') {
return '终点照片'
} else if (row.bizType == 'PHOTO_BILL') {
return '配送单照片'
}
}
//配送单
function handleBill(row) {
openBill.value = true
detailOrder({ orderNo: row.orderNo }).then(response => {
console.log(2222222)
console.log(response)
// return
// detailLoading.value = false
// wlList.value = response.data
billData.value = response.data
deliveryDate.value = {
year: response.data[0].deliveryDate.substring(0, 4),
month: response.data[0].deliveryDate.substring(5, 7),
day: response.data[0].deliveryDate.substring(8, 10),
}
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// billData.value.push(response.data[0])
// srcList.value = billData.value[0].attachments.map(item => item.url)
// console.log(billData.value)
})
}
function urlFun(attachments, bizType) {
// console.log(33333)
const onedata = attachments.find((x) => x.bizType == bizType)
// srcList.value = onedata ? [onedata.url] : []
return onedata ? onedata.url : ''
// console.log(onedata)
}
function urlFun1(attachments, bizType) {
// console.log(44444)
const onedata = attachments.find((x) => x.bizType == bizType)
return onedata ? [onedata.url] : []
// console.log(onedata)
}
// 点击历史轨迹按钮,打开弹框
async function handleHistory(row) {
const response = await getHistory({ orderNo: row.orderNo, plateNo: row.plateNo })
if (response.data.length > 0) {
open.value = true
// let lonLat = []
for (let i = 0; i < response.data.length; i++) {
lineArr.value.push([response.data[i].lng, response.data[i].lat])
}
// 轨迹经纬度去重,否定有问题;
const uniqueCoordinates = lineArr.value.filter((item, index, self) => {
return self.findIndex(t => t[0] === item[0] && t[1] === item[1]) === index;
});
lineArr.value = uniqueCoordinates;
initAMap();
}else{
proxy.$modal.msgWarning("暂无轨迹");
}
}
function initAMap() {
AMapLoader.load({
key: "27719d64d6131bd1b93a15c49cab39c3", // 申请好的Web端开发者Key首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: [
"AMap.Scale",
"AMap.ToolBar",
"AMap.ControlBar",
"AMap.Geocoder",
"AMap.Marker",
"AMap.CitySearch",
"AMap.Geolocation",
"AMap.AutoComplete",
"AMap.InfoWindow",
"AMap.moveAnimation"
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
// 获取到作为地图容器的DOM元素创建地图实例
map.value = new AMap.Map("amapcontainer", {
//设置地图容器id
resizeEnable: true,
viewMode: "3D", // 使用3D视图
zoomEnable: true, // 地图是否可缩放默认值为true
dragEnable: true, // 地图是否可通过鼠标拖拽平移默认为true
doubleClickZoom: true, // 地图是否可通过双击鼠标放大地图默认为true
zoom: 17, //初始化地图级别
center: [118.201897, 39.70742], // 初始化中心点坐标 北京
// mapStyle: "amap://styles/darkblue", // 设置颜色底层
});
marker.value = new AMap.Marker({
position: [118.201897, 39.70742],
icon: "https://a.amap.com/jsapi_demos/static/demo-center-v2/car.png",
offset: new AMap.Pixel(-13, -26),
});
map.value.add(marker.value);
// 所有路线轨迹(初始轨迹);
polyline.value = new AMap.Polyline({
path: lineArr.value,
showDir: true,
strokeColor: "#28F", //线颜色
// strokeOpacity: 1, //线透明度
strokeWeight: 6, //线宽
// strokeStyle: "solid" //线样式
});
map.value.add(polyline.value);
// 走过的路径
passedPolyline.value = new AMap.Polyline({
strokeColor: "#AF5", //线颜色
strokeWeight: 6, //线宽
});
map.value.add(passedPolyline.value);
map.value.setFitView(); // 根据覆盖物自适应展示地图
})
.catch((e) => {
console.log(e);
});
}
// 松开手触发
function silderInput() {
let array = [];
if (afterData.value.length === 0) {
array = lineArr.value;
} else {
array = afterData.value
}
// var that = this;
AMap.plugin("AMap.MoveAnimation", () => {
progressTime.value = formatSeconds(0);
// markerSpeed.value = markerSpeed.value * speedCount.value;//markerSpeed的值是100;
marker.value.moveAlong(array, {
duration: 500, //可根据实际采集时间间隔设置速度
// JSAPI2.0 是否延道路自动设置角度在 moveAlong 里设置
autoRotation: true,
});
marker.value.on("moving", (e) => {
// lat:34.998553
// lng:108.478947
let lngs = e.passedPath[e.passedPath.length - 1].lng; // 经度 当前所在的位置信息;
let lats = e.passedPath[e.passedPath.length - 1].lat; // 纬度
let info = [];
info.push('<div style="margin-top: -10px;">');
info.push("经度 :" + lngs);
info.push("纬度 :" + lats);
let infoWindow = new AMap.InfoWindow({
content: info.join("<br/>"),
offset: new AMap.Pixel(3, -20),
});
infoWindow.open(map.value, e.target.getPosition());
var index = lineArr.value.findIndex(([lng, lat]) => lng === lngs && lat === lats);
if (index > 0) {
sliderVal.value = index;
}
});
});
}
function sliderChange(val) {
let beforeData = lineArr.value.slice(0, val + 1);//包含val;
afterData.value = lineArr.value.slice(val + 1);//不包含val;因为不包含所以不能设置setPostion;
let info = [];
info.push('<div style="margin-top: -10px;">');
info.push("经度 :" + lineArr.value[val][0]);
info.push("纬度 :" + lineArr.value[val][1]);
let infoWindow = new AMap.InfoWindow({
content: info.join("<br/>"),
offset: new AMap.Pixel(3, -20),
});
infoWindow.open(map.value, new AMap.LngLat(lineArr.value[val][0], lineArr.value[val][1]));
if (val > 0) {//解决车闪烁问题;
marker.value.setPosition(new AMap.LngLat(lineArr.value[val][0], lineArr.value[val][1]));
}
if (beforeData.length > 0) {
passedPolyline.value.setPath(beforeData);//设置走过的路径;
}
}
function monitorInterval() {
int.value = setInterval(() => {
sliderVal.value += 1 * speedCount.value;
}, (TIME_VARIABLE / 100) * 100);
}
function pauseAnimation() {
clearInterval(int.value);
marker.value.pauseMove();
}
function resumeAnimation() {
marker.value.resumeMove();
}
function formatSeconds(value) {
var secondTime = parseInt(value); // 秒
var minuteTime = 0; // 分
var hourTime = 0; // 小时
if (secondTime > 60) {
minuteTime = parseInt(secondTime / 60);
secondTime = parseInt(secondTime % 60);
if (minuteTime > 60) {
hourTime = parseInt(minuteTime / 60);
minuteTime = parseInt(minuteTime % 60);
}
}
var result =
parseInt(secondTime) < 10
? "0" + parseInt(secondTime)
: "" + parseInt(secondTime);
result =
parseInt(minuteTime) < 10
? "0" + parseInt(minuteTime) + ":" + result
: "" + parseInt(minuteTime) + ":" + result;
return result;
}
/** 导出按钮操作 */
function handleExport() {
proxy.download('document/order/export', {
...queryParams.value
}, `order_${new Date().getTime()}.xlsx`)
}
getList()
// handleBill({orderNo:'DO20251203155940338'})
// handlePic({orderNo:'DO20251203155940338'})
</script>
<style scoped>
.delivery-form {
font-family: "SimSun", "宋体", sans-serif;
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
max-height: 700px;
overflow: auto;
}
table {
border: 1px solid black;
border-collapse: collapse;
font-size: 14px;
line-height: 1.5;
}
td {
border: 1px solid black;
padding: 5px;
text-align: left;
vertical-align: top;
}
td:first-child {
width: 120px;
}
/* 表格标题居中 */
table tr:first-child td {
text-align: center;
font-weight: bold;
font-size: 18px;
}
/* 修正列宽和换行 */
/* td:nth-child(3) {
width: 250px;
} */
/* 调整输入框样式 */
input[type="text"] {
border: none;
border-bottom: 1px solid #ccc;
padding: 2px;
font-size: 14px;
}
.wzmc {
width: 300px;
}
.demo-image .block {
padding: 30px 0;
text-align: center;
border-right: solid 1px var(--el-border-color);
display: inline-block;
width: 20%;
min-width: 100px;
box-sizing: border-box;
vertical-align: top;
}
.demo-image .block:last-child {
border-right: none;
}
.demo-image .demonstration {
display: block;
color: var(--el-text-color-secondary);
font-size: 14px;
margin-bottom: 20px;
}
</style>