Files
delivery_admin_app/pages/index/deliveryBill.vue
2026-03-11 14:52:32 +08:00

683 lines
21 KiB
Vue
Raw Permalink 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>
<view class="page" :style="formModel.scene == 'COMPLETE' ? '' : 'padding-bottom: 160rpx;'">
<uv-form labelPosition="left" :model="formModel" :rules="deliveryRules" ref="deliveryFormRef">
<!-- 标题 -->
<view class="title">
国网唐山供电公司贾庵子仓库货物配送单
</view>
<!-- 副标题/说明 -->
<!-- <view class="subtitle">
现场仓库接物配送单
</view> -->
<!-- 表单整体 -->
<view class="form-box">
<!-- 第一行配变项目名称项目编号 -->
<view class="row">
<view class="cell">
配送项目名称{{ goodsData?.[0]?.xmMs }}
</view>
<view class="cell">
项目编号{{ goodsData?.[0]?.xmNo }}
</view>
</view>
<!-- 配送地址配送日期 -->
<view class="row">
<view class="cell">
配送地址{{ goodsData?.[0]?.originName }} {{ goodsData?.[0]?.destName }}
</view>
<view class="cell">
配送日期{{ goodsData?.[0]?.deliveryDate }}
</view>
</view>
<!-- 配送车号司机签名 -->
<view class="row">
<view class="cell">
<uv-form-item label="配送车牌:" prop="plateNo" v-if="formModel.scene == 'ORIGIN'">
<uv-input border="bottom" v-model="formModel.plateNo" />
</uv-form-item>
<text v-else> 配送车号{{ formModel.plateNo }}</text>
</view>
<view class="cell">
<uv-form-item label="司机签名:" prop="SIGN_DRIVER">
<uv-button type="warning" size="small" text="去签名" v-if="formModel.scene == 'ORIGIN' && !formModel.SIGN_DRIVER.length" @tap="goSign('SIGN_DRIVER')"></uv-button>
<uv-upload :fileList="formModel.SIGN_DRIVER" v-else :maxCount="1" @delete="deleteImg(index, 'SIGN_DRIVER')" :deletable="formModel.scene == 'ORIGIN'" :previewFullImage="true"></uv-upload>
</uv-form-item>
<uv-form-item label="联系方式:" prop="driverPhone" v-if="formModel.scene == 'ORIGIN'">
<uv-input border="bottom" maxlength="11" type="number" v-model="formModel.driverPhone" />
</uv-form-item>
<view v-else>联系方式{{ formModel.driverPhone }}</view>
</view>
</view>
<!-- 接收单位配送吨数 -->
<view class="row">
<view class="cell">
接收单位{{ goodsData?.[0]?.receiverOrgName }}
</view>
<view class="cell">
配送吨数{{ goodsData?.[0]?.deliveryTon }}
</view>
</view>
<!-- 发货人联系方式 -->
<view class="row">
<view class="cell">
发货人/联系方式
<view>{{ goodsData?.[0]?.shipperName }}/{{ goodsData?.[0]?.shipperPhone }}</view>
</view>
<view class="cell">
制单{{ goodsData?.[0]?.makerUserName }}
</view>
</view>
<!-- 接收人联系方式 -->
<view class="row">
<view class="cell">
接收人/联系方式
<view>{{ goodsData?.[0]?.receiverName }}/{{ goodsData?.[0]?.receiverPhone }}</view>
</view>
<view class="cell">
<uv-form-item label="配送人签名:" prop="SIGN_COURIER">
<uv-button type="warning" text="去签名" size="small" v-if="formModel.scene == 'ORIGIN' && !formModel.SIGN_COURIER.length" @tap="goSign('SIGN_COURIER')"></uv-button>
<uv-upload :fileList="formModel.SIGN_COURIER" v-else :maxCount="1" @delete="deleteImg(index, 'SIGN_COURIER')" :deletable="formModel.scene == 'ORIGIN'" :previewFullImage="true"></uv-upload>
</uv-form-item>
</view>
</view>
<!-- ======= 可左右滑动的明细表格 ======= -->
<view class="table-scroll">
<view class="table">
<!-- 表头 -->
<view class="table-header">
<view class="th col-seq">序号</view>
<view class="th col-wlno">物料编码</view>
<view class="th col-wlname">物资名称</view>
<view class="th col-qty">数量(单位)</view>
<view class="th col-order">订单号</view>
<view class="th col-supplier">供应商名称</view>
<view class="th col-remark">备注</view>
</view>
<!-- 表体 -->
<view class="table-body">
<view class="tr" v-for="(item, index) in goodsData" :key="index">
<view class="td col-seq">{{ index + 1 }}</view>
<view class="td col-wlno">{{ item.wlNo }}</view>
<view class="td col-wlname">{{ item.wlMs }}</view>
<view class="td col-qty">{{ item.realQty }}({{ item.dw }})</view>
<view class="td col-order">{{ item.sapNo }}</view>
<view class="td col-supplier">{{ item.gysMc }}</view>
<view class="td col-remark">{{ item.remark }}</view>
</view>
</view>
</view>
</view>
<!-- ======= 明细表格结束 ======= -->
<!-- 收货确认 -->
<view class="row">
<view class="cell">
<uv-form-item label="接收物资状态:" prop="receiveStatus">
<view style="display: inline-block;">
<uv-radio-group v-model="formModel.receiveStatus">
<uv-radio :name="1" label="数量齐全、状态完好" shape="square" :disabled="formModel.scene !== 'DEST'"></uv-radio>
<span style="vertical-align: middle; font-size: 26rpx;line-height: 14px;">&nbsp;/&nbsp;</span>
<uv-radio :name="2" label="存在问题" shape="square" :disabled="formModel.scene !== 'DEST'"></uv-radio>
</uv-radio-group>
</view>
</uv-form-item>
<uv-form-item label="" v-if="formModel.scene == 'DEST'" prop="receiveProblem">
<uv-input border="bottom" v-model="formModel.receiveProblem" />
</uv-form-item>
<view v-else>{{ formModel.receiveProblem }}</view>
</view>
</view>
<view class="row" style="justify-content: space-between;">
<view class="cell" style="border-right: unset;">
<uv-form-item label="接收人签名:" prop="SIGN_RECEIVER">
<uv-button type="warning" text="去签名" size="small" v-if="formModel.scene == 'DEST' && !formModel.SIGN_RECEIVER.length" @tap="goSign('SIGN_RECEIVER')"></uv-button>
<uv-upload :fileList="formModel.SIGN_RECEIVER" v-else-if="formModel.SIGN_RECEIVER.length" :maxCount="1" :deletable="formModel.scene == 'DEST'" @delete="deleteImg(index, 'SIGN_RECEIVER')" :previewFullImage="true"></uv-upload>
<view v-else></view>
</uv-form-item>
</view>
<view class="cell">
日期{{ goodsData?.[0]?.deliveryDate.split("-")[0] }}{{ goodsData?.[0]?.deliveryDate.split("-")[1] }}{{ goodsData?.[0]?.deliveryDate.split("-")[2] }}
</view>
</view>
<!-- <view class="row">
<view class="cell">
<uv-form-item label="日期:" prop="SIGN_RECEIVER">
<uv-input border="bottom" type="number" style="width: 120rpx;flex: unset;" readonly="formModel.scene !== 'DEST'" v-model="formModel.receiveYear" />
<uv-input border="bottom" type="number" style="width: 80rpx;flex: unset;" :readonly="formModel.scene !== 'DEST'" v-model="formModel.receiveMonth" />
<uv-input border="bottom" type="number" style="width: 80rpx;flex: unset;" :readonly="formModel.scene !== 'DEST'" v-model="formModel.receiveDay" />
</uv-form-item>
</view>
</view> -->
</view>
<uv-form-item label="开始配送现场照片" prop="ORIGIN_PHOTO_SITE" style="margin-top: 16rpx;" class="form-item">
<uv-upload
:fileList="formModel.ORIGIN_PHOTO_SITE"
name="ORIGIN_PHOTO_SITE"
:maxCount="formModel.scene == 'ORIGIN' ? 10 : formModel.ORIGIN_PHOTO_SITE.length"
:deletable="formModel.scene == 'ORIGIN'"
@afterRead="afterRead($event, 'ORIGIN_PHOTO_SITE')"
@delete="deleteImg(index, 'ORIGIN_PHOTO_SITE')"
></uv-upload>
</uv-form-item>
<uv-form-item label="单据照片" prop="PHOTO_BILL">
<uv-upload
:fileList="formModel.PHOTO_BILL"
name="PHOTO_BILL"
:maxCount="1"
:deletable="formModel.scene == 'ORIGIN'"
@afterRead="afterRead($event, 'PHOTO_BILL')"
@delete="deleteImg(index, 'PHOTO_BILL')"
></uv-upload>
</uv-form-item>
<uv-form-item label="完成配送现场照片" prop="DEST_PHOTO_SITE" v-show="formModel.scene !== 'ORIGIN'" class="form-item">
<uv-upload
:fileList="formModel.DEST_PHOTO_SITE"
:deletable="formModel.scene == 'DEST'"
name="DEST_PHOTO_SITE"
:maxCount="formModel.scene == 'DEST' ? 10 : formModel.DEST_PHOTO_SITE.length"
@afterRead="afterRead($event, 'DEST_PHOTO_SITE')"
@delete="deleteImg(index, 'DEST_PHOTO_SITE')"
></uv-upload>
</uv-form-item>
</uv-form>
<view class="btn" v-show="formModel.scene !== 'COMPLETE'">
<uv-button type="primary" text="确定" size="large" style="width: 100%;" class="mainBtn" @tap="startForm">{{ formModel.scene == 'ORIGIN' ? ' ' : ' '}}</uv-button>
</view>
</view>
</template>
<script setup>
import { ref, onUnmounted } from 'vue'
import { onLoad } from "@dcloudio/uni-app"
import { getOrderDetail, uploadDeliveryAttachment, startOrder } from "@/api/index"
const form = ref({
// projectName: '冀北唐山路南区10kV广鑫环网柜等6座环网柜配电自动化改造工程',
// projectNo: '1801032401LD',
// address: '唐山市路南区女织寨赵田庄村民源电气',
// date: '2025年12月2日',
// carNo: '冀B1947L',
// receiveCompany: '路南供电中心',
// senderName: '林生',
// senderPhone: '15531558587',
// receiverName: '王小强',
// receiverPhone: '18733306661',
// driverSign: '',
// deliveryWeight: '',
// maker: '孟利红',
// checker: '',
// // 明细 items字段**齐全**:物料编码、名称、数量、单位、订单号、供应商名称、备注
// items: [
// {
// wlNo: '500138342',
// wlName: '二次融合成套环网箱AC10kV, 630A固体二进四出',
// qty: 1,
// unit: '套',
// orderNo: '101378434',
// supplierName: '宁波奥克斯智能科技股份有限公司',
// remark: 'ID: 169736945'
// },
// {
// wlNo: '500138342',
// wlName: '二次融合成套环网箱AC10kV, 630A固体二进四出',
// qty: 1,
// unit: '套',
// orderNo: '101378434',
// supplierName: '宁波奥克斯智能科技股份有限公司',
// remark: 'ID: 169736952'
// }
// ]
})
const formModel = ref({
orderNo: null,
scene: '',
lng: '',
lat: '',
SIGN_DRIVER: [],
SIGN_COURIER: [],
ORIGIN_PHOTO_SITE: [],
PHOTO_BILL: [],
DEST_PHOTO_SITE: [],
SIGN_RECEIVER: [],
receiveStatus: '',
receiveProblem: '',
// SIGN_DRIVER: [{"url":
// "http://192.168.1.5:8087/delivery/2025-12-03/origin/sign_driver/8f5253492a63469a90406fd9c1d702b8.png"}
// ],
})
const deliveryFormRef = ref()
const deliveryRules = ref({
'plateNo': [
{
type: 'string',
required: true,
message: '请输入配送车牌',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
// 自定义校验逻辑
return uni.$uv.test.carNo(value)
},
message: '请输入正确的车牌',
trigger: ['blur', 'change']
}
],
'driverPhone': [
{
type: 'string',
required: true,
message: '请输入司机电话',
trigger: ['blur', 'change']
},
{
validator: (rule, value, callback) => {
// 自定义校验逻辑
return uni.$uv.test.mobile(value)
},
message: '请输入正确的手机号',
trigger: ['blur', 'change']
}
],
'SIGN_DRIVER': {
type: 'array',
required: true,
message: '请司机签名',
trigger: ['blur', 'change']
},
'SIGN_COURIER': {
type: 'array',
required: true,
message: '请配送人签名',
trigger: ['blur', 'change']
},
'ORIGIN_PHOTO_SITE': {
type: 'array',
required: true,
message: '请上传现场照片',
trigger: ['blur', 'change']
},
'PHOTO_BILL': {
type: 'array',
required: true,
message: '请上传单据照片',
trigger: ['blur', 'change']
},
})
const bizTypeMap = {
'ORIGIN_PHOTO_SITE': { scene: 'ORIGIN', bizType: 'PHOTO_SITE' },
'DEST_PHOTO_SITE': { scene: 'DEST', bizType: 'PHOTO_SITE' },
'SIGN_DRIVER': { scene: 'ORIGIN', bizType: 'SIGN_DRIVER' },
'SIGN_COURIER': { scene: 'ORIGIN', bizType: 'SIGN_COURIER' },
'PHOTO_BILL': { scene: 'ORIGIN', bizType: 'PHOTO_BILL' },
'SIGN_RECEIVER': { scene: 'DEST', bizType: 'SIGN_RECEIVER' }
};
// 遍历form中的数组字段
// Object.keys(form).forEach(key => {
// if (Array.isArray(form[key]) && bizTypeMap[key]) {
// const matchRule = bizTypeMap[key];
// // 筛选符合条件的元素并添加
// const matchedItems = arr.filter(item => {
// return item.bizType === matchRule.bizType &&
// (!matchRule.scene || item.scene === matchRule.scene);
// });
// form[key].push(...matchedItems);
// }
// });
const goodsData = ref([])
const getDetail = () => {
getOrderDetail({ orderNo: formModel.value.orderNo }).then(res => {
goodsData.value = res.data
formModel.value.plateNo = res.data[0].plateNo
formModel.value.driverPhone = res.data[0].driverPhone
formModel.value.receiveStatus = res.data[0].receiveStatus
formModel.value.receiveProblem = res.data[0].receiveProblem
// res.data[0].attachments.forEach(item => {
// if (formModel.value.hasOwnProperty(item.bizType) && Array.isArray(formModel.value[item.bizType])) {
// formModel.value[item.bizType].push(item);
// }
// if (item.bizType === 'PHOTO_SITE') {
// const targetKey = item.scene === 'ORIGIN' ? 'ORIGIN_PHOTO_SITE' : 'DEST_PHOTO_SITE';
// if (Array.isArray(formModel.value[targetKey])) {
// formModel.value[targetKey].push(item);
// }
// }
// })
Object.keys(formModel.value).forEach(key => {
if (Array.isArray(formModel.value[key]) && bizTypeMap[key]) {
const matchRule = bizTypeMap[key];
// 筛选符合条件的元素并添加
const matchedItems = res.data[0].attachments.filter(item => {
return item.bizType === matchRule.bizType &&
(!matchRule.scene || item.scene === matchRule.scene);
});
formModel.value[key].push(...matchedItems);
}
});
console.log(formModel.value)
})
}
// 去签名页面
const goSign = (type) => {
// 移除之前的事件监听器避免重复
uni.$off('getSignImg')
uni.navigateTo({
url: '/pages/index/signature?bizType=' + type + '&scene=' + formModel.value.scene,
success: () => {
// 监听签名返回事件
const eventHandler = (e) => {
console.log('getSignImg', e)
// signBase64.value = e.base64
// signTempimg.value = e.path
formModel.value[type] = [{url: e.url[0], scene: formModel.value.scene, bizType: type}]
}
// 监听一次自定义事件
uni.$once('getSignImg', eventHandler)
// 页面卸载时移除监听(避免内存泄漏)
// uni.$off('getSignImg', eventHandler)
}
})
}
// 上传照片
const afterRead = async (event, type) => {
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
console.log(event)
let files = event.file.url
let formData = {
scene: formModel.value.scene,
bizType: type
}
console.log(files)
uploadDeliveryAttachment(files, formData).then(res => {
console.log(res)
if (!formModel.value[type] || formModel.value[type].length == 0) {
formModel.value[type] = []
}
res.data.forEach(item => {
formModel.value[type].push({url: item, scene: formModel.value.scene, bizType: type})
})
})
}
// 删除图片
const deleteImg = (index, type) => {
formModel.value[type].splice(index, 1)
}
onLoad((options) => {
formModel.value.orderNo = options.orderNo
formModel.value.scene = options.type
formModel.value.billNoCk = options.billNoCk
if (formModel.value.scene == "DEST") {
deliveryRules.value.receiveStatus = {
type: 'number',
required: true,
validator: (rule, value, callback) => {
if (value > 0) {
callback()
} else {
callback(new Error('请选择接收状态'))
}
},
message: '请选择接收状态',
trigger: ['blur', 'change']
}
deliveryRules.value.receiveProblem = {
type: 'string',
validator: (rule, value, callback) => {
if (formModel.value.receiveStatus == 2 && value.length == 0) {
callback(new Error('请描述存在问题'))
} else {
callback()
}
},
message: '请描述存在问题',
trigger: ['blur', 'change']
}
deliveryRules.value.SIGN_RECEIVER = {
type: 'array',
required: true,
message: '请接收人签名',
trigger: ['blur', 'change']
}
deliveryRules.value.DEST_PHOTO_SITE = {
type: 'array',
required: true,
message: '请上传现场照片',
trigger: ['blur', 'change']
}
}
getDetail()
})
const startForm = () => {
deliveryFormRef.value.validate().then(res => {
console.log("验证通过")
uni.showLoading({
title: '正在编辑表单中...'
})
uni.getLocation({
type: 'gcj02', // 高德地图使用gcj02坐标系
success: (res) => {
console.log(res)
// uni.$uv.toast('获取当前位置成功')
formModel.value.lng = res.longitude
formModel.value.lat = res.latitude
let attachments = []
for (const key in bizTypeMap) {
if (bizTypeMap[key].scene === formModel.value.scene) {
const formattedData = formModel.value[key].map(item => ({
...item,
bizType: bizTypeMap[key].bizType,
}))
attachments.push(...formattedData);
}
}
let params = {
...formModel.value,
attachments: attachments,
}
console.log(params)
startOrder(params).then(res => {
console.log(res)
uni.$uv.toast('开始配送')
uni.navigateBack({ delta: 1 })
uni.hideLoading()
})
},
fail: (err) => {
reject(err);
}
});
}).catch(errors => {
})
}
// 组件卸载时移除事件监听器
onUnmounted(() => {
console.log("协助")
uni.$off('getSignImg')
})
</script>
<style scoped lang="scss">
.page {
padding: 20rpx;
background-color: #fff;
min-height: calc(100vh - 192rpx);
}
/* 标题 */
.title {
text-align: center;
font-size: 38rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.subtitle {
text-align: center;
font-size: 30rpx;
margin-bottom: 30rpx;
}
/* 外框 */
.form-box {
border: 2rpx solid #000;
padding: 20rpx;
font-size: 26rpx;
}
/* 每一行 */
.row {
display: flex;
border-bottom: 2rpx solid #000;
}
.row:last-child {
border-bottom: 0;
}
:deep(.uv-form-item__body) {
padding: 0 !important;
}
:deep(.uv-border-bottom) {
border-color: #000 !important;
padding: 0 !important;
border-bottom-width: 2rpx !important;
}
:deep(.uv-form-item__body__left) {
width: unset !important;
}
:deep(.uv-form-item__body__left__content__label) {
font-size: 26rpx !important;
color: #000 !important;
}
:deep(.uv-form-item__body__right__message) {
margin-left: 0 !important;
}
:deep(.form-item .uv-form-item__body__left) {
width: 120rpx !important;
}
:deep(.uv-radio__label-wrap text) {
font-size: 26rpx !important;
color: #000 !important;
}
:deep(.uv-radio__icon-wrap--square) {
width: 30rpx !important;
height: 30rpx !important;
}
.cell {
flex: 1;
padding: 10rpx;
border-right: 2rpx solid #000;
}
.cell:last-child {
border-right: 0;
}
/* ====== 明细表格滚动区域 ====== */
.table-scroll {
margin-top: 20rpx;
border-top: 2rpx solid #000;
overflow-x: auto;
}
/* 给表格一个最小宽度,超出屏幕时就可以左右拖动 */
.table {
/* min-width: 2800rpx; */
width: 1700rpx;
}
/* 表头 */
.table-header {
display: flex;
border-bottom: 2rpx solid #000;
background-color: #f5f5f5;
}
.th {
padding: 12rpx;
border-right: 2rpx solid #000;
font-weight: bold;
box-sizing: border-box;
}
.th:last-child {
border-right: 0;
}
/* 表体 */
.table-body .tr {
display: flex;
border-bottom: 2rpx solid #000;
box-sizing: border-box;
}
.td {
padding: 12rpx;
border-right: 2rpx solid #000;
box-sizing: border-box;
}
.td:last-child {
border-right: 0;
}
.col-seq {
width: 80rpx;
}
.col-wlno {
width: 180rpx;
}
.col-wlname {
width: 500rpx;
}
.col-qty {
width: 210rpx;
}
.col-order {
width: 180rpx;
}
.col-supplier {
width: 400rpx;
}
.col-remark {
width: 150rpx;
}
.btn {
position: fixed;
width: calc(100vw - 64rpx);
bottom: 0;
left: 32rpx;
z-index: 99;
// display: flex;
// align-items: center;
// justify-content: space-between;
}
</style>