Files
delivery_admin_app/pages/index/deliveryBill.vue

683 lines
21 KiB
Vue
Raw Permalink Normal View History

2026-03-11 14:52:32 +08:00
<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>