Files
sh-app/pipe/deliver/detail.vue
T
2026-06-16 19:26:48 +08:00

691 lines
18 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>
<view class="page-detail">
<!-- 基本信息 -->
<view class="info-group">
<!-- 车次 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>车次</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.TrainNumber" placeholder="请输入车次" />
</view>
<text class="info-value" v-else>{{ form.TrainNumber || '--' }}</text>
</view>
<!-- 单位名称 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>单位名称</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.UnitName" :clearable="false" type="select" placeholder="请选择单位"
@click="handleShowUnit" />
</view>
<text class="info-value" v-else>{{ form.UnitName || '--' }}</text>
</view>
<!-- 驾驶员姓名 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>驾驶员姓名</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.DriverName" placeholder="请输入驾驶员姓名" />
</view>
<text class="info-value" v-else>{{ form.DriverName || '--' }}</text>
</view>
<!-- 驾驶员电话 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>驾驶员电话</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.DriverPhone" placeholder="请输入驾驶员电话" type="number" />
</view>
<text class="info-value" v-else>{{ form.DriverPhone || '--' }}</text>
</view>
<!-- 车牌号 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>车牌号</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.LicensePlateNumber" placeholder="请输入车牌号" />
</view>
<text class="info-value" v-else>{{ form.LicensePlateNumber || '--' }}</text>
</view>
<!-- 联系人姓名 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>联系人姓名</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.ContactName" placeholder="请输入联系人姓名" />
</view>
<text class="info-value" v-else>{{ form.ContactName || '--' }}</text>
</view>
<!-- 联系人电话 -->
<view class="info-item">
<text class="info-label"><text class="required">*</text>联系人电话</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.ContactPhone" placeholder="请输入联系人电话" type="number" />
</view>
<text class="info-value" v-else>{{ form.ContactPhone || '--' }}</text>
</view>
<!-- 备注 -->
<view class="info-item info-textarea">
<text class="info-label">备注</text>
<view class="info-input-wrap" v-if="!form.State || form.State === 0">
<u-input v-model="form.Remark" placeholder="请输入备注" type="textarea" />
</view>
<text class="info-value" v-else>{{ form.Remark || '--' }}</text>
</view>
<!-- 状态 -->
<view class="info-item">
<text class="info-label">状态</text>
<text class="card-tag" :class="'status-' + form.State">{{ getStatus(form.State) }}</text>
</view>
<!-- 扫码添加 -->
<view class="info-item info-link" v-if="form.Id && (!form.State || form.State === 0)" @click="handleScan">
<text class="info-label">扫码添加</text>
<view class="scan-icon">
<u-icon name="scan" :size="36" color="#3577FF"></u-icon>
</view>
</view>
</view>
<!-- 包装列表 -->
<view class="package-group" v-if="form.Id">
<view class="section-title">
<text>包装列表</text>
<text class="section-count">({{ BZList.length }})</text>
</view>
<view v-for="(item, index) in BZList" :key="index" class="package-card">
<view class="card-header">
<text class="card-title">{{ item.PackagingCode || '--' }}</text>
<view class="del-btn" @click="handleDeletePackage(item)" v-if="!form.State || form.State === 0">
<u-icon name="trash" :size="36" color="#ffffff"></u-icon>
</view>
</view>
<view class="card-body">
<view class="card-row">
<text class="row-label">预制工作包</text>
<text class="row-value">{{ item.StackingPosition || '--' }}</text>
</view>
</view>
</view>
<view v-if="BZList.length === 0" class="package-empty">
<u-empty text="暂无数据" mode="list"></u-empty>
</view>
</view>
<!-- 底部按钮 -->
<view class="bottom-btns" v-if="!form.State || form.State === 0">
<u-button v-if="form.Id" size="medium" type="primary" :disabled="disabledBtn" @click="handleSave(0,true)">保存</u-button>
<u-button v-else size="medium" type="primary" :disabled="disabledBtn" @click="handleSave(0)">保存</u-button>
<u-button v-if="form.Id" size="medium" type="success" :disabled="disabledBtn"
@click="handleSave(1)">发货</u-button>
</view>
<view class="bottom-btns" v-if="form.State === 1">
<u-button size="medium" type="warning" :disabled="disabledBtn" @click="handleSign">签收</u-button>
</view>
<!-- 单位选择弹窗 -->
<nbd-select v-model="showUnitSelect" v-model:model="selectedUnit" :show-search="true" title="选择单位"
:list="unitList" label-key="UnitName" value-key="UnitId" @confirm="handleUnitConfirm" />
<!-- 包装详情弹窗 -->
<u-popup v-model="showBZDetail" mode="center" :closeable="true" @close="closeBZDetail">
<view class="bz-detail-modal">
<view class="modal-header">
<text class="modal-title">包装详情</text>
</view>
<view class="modal-body" v-if="currentBZInfo">
<view class="detail-row">
<text class="detail-label">包装编号</text>
<text class="detail-value">{{ currentBZInfo.PackagingCode || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">最早安装时间</text>
<text class="detail-value">{{ currentBZInfo.EarliestInstallTime || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">堆放位置</text>
<text class="detail-value">{{ currentBZInfo.StackingPosition || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">到场状态</text>
<text class="detail-value">{{ getBZStatus(currentBZInfo.State) }}</text>
</view>
<view class="detail-row">
<text class="detail-label">联系人及电话</text>
<text class="detail-value">{{ currentBZInfo.ContactName || '' }}/{{ currentBZInfo.ContactPhone
|| '' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">收货人</text>
<text class="detail-value">{{ currentBZInfo.ReceiveMan || '--' }}</text>
</view>
<view class="detail-row">
<text class="detail-label">收货日期</text>
<text class="detail-value">{{ currentBZInfo.ReceiveDate || '--' }}</text>
</view>
<!-- 预制组件列表 -->
<view class="prepipe-section" v-if="currentBZItems && currentBZItems.length > 0">
<view class="prepipe-title">预制组件列表{{ currentBZItems.length }}</view>
<view class="prepipe-list">
<view class="prepipe-card" v-for="(item, index) in currentBZItems" :key="index">
<view class="card-header">
<text class="card-code">{{ item.PipelineComponentCode || '--' }}</text>
</view>
<view class="card-body">
<view class="card-row">
<text class="card-label">数量/单位</text>
<text class="card-value">{{ item.PreUnit || '--' }}</text>
</view>
<view class="card-row">
<text class="card-label">所属主项</text>
<text class="card-value">{{ item.UnitWorkName || '--' }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="modal-footer">
<view class="cancel-btn" @click="closeBZDetail">取消</view>
<view class="confirm-btn" @click="confirmAddBZ">确认添加</view>
</view>
</view>
</u-popup>
</view>
</template>
<script setup>
import {
ref
} from 'vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
useUserStore
} from '@/store'
import {
storeToRefs
} from 'pinia'
import {
reqUnitByProjectIdUnitType
} from '@/api/base'
import {
reqTrainNumberDetail,
reqPackagingByTrain,
reqPackagingInformationById,
reqSavePackagingToTrain,
reqSaveTrainNumber,
reqTrainInfoConfirmArrival,
reqDeletePackagingFromTrain
} from '@/api/hj'
const userStore = useUserStore()
const {
currentProject,
userInfo
} = storeToRefs(userStore)
// ===== 页面状态 =====
const trainNumberId = ref('')
const disabledBtn = ref(false)
// ===== 表单数据 =====
const form = ref({
TrainNumberId: '',
TrainNumber: '',
UnitId: '',
UnitName: '',
DriverName: '',
DriverPhone: '',
LicensePlateNumber: '',
ContactName: '',
ContactPhone: '',
Remark: '',
State: 0,
StateName: ''
})
// ===== 单位选择 =====
const showUnitSelect = ref(false)
const unitList = ref([])
const selectedUnit = ref({})
// ===== 包装列表 =====
const BZList = ref([])
// ===== 包装详情弹窗 =====
const showBZDetail = ref(false)
const currentBZInfo = ref(null)
const currentBZItems = ref([])
// ===== 获取单位列表 =====
const getUnitList = async () => {
try {
if (!currentProject.value?.ProjectId) {
console.warn('currentProject 未初始化,无法获取单位列表')
return
}
const res = await reqUnitByProjectIdUnitType(currentProject.value?.ProjectId || '')
unitList.value = [...(res.data || [])]
} catch (error) {
console.error('获取单位列表失败:', error)
}
}
// ===== 显示单位选择 =====
const handleShowUnit = () => {
if (!form.value.State || form.value.State === 0) {
showUnitSelect.value = true
}
}
// ===== 单位选择确认 =====
const handleUnitConfirm = (item) => {
form.value.UnitId = item.UnitId
form.value.UnitName = item.UnitName
showUnitSelect.value = false
}
// ===== 获取状态文本 =====
const getStatus = (val) => {
const map = {
'0': '待发货',
'1': '已发货',
'2': '已签收'
}
return form.value.Id && map[String(val)] || '--'
}
// ===== 扫码添加 =====
const handleScan = () => {
uni.scanCode({
success(res) {
let result = res.result
console.log('扫码结果:', result)
if (!result.includes('PackagingManage$')) {
uni.showToast({
title: '非包装二维码',
icon: 'none',
duration: 3000
})
return
}
let bzId = result.split('$')[1]
if (bzId) {
getBZInfo(bzId)
} else {
uni.showToast({
title: '二维码格式错误',
icon: 'none'
})
}
},
fail(err) {
console.error('扫码失败:', err)
if (err.errMsg && err.errMsg.includes('cancel')) {
// 用户取消扫码
} else {
uni.showToast({
title: '扫码失败,请重试',
icon: 'none'
})
}
}
})
}
// ===== 获取包装信息 =====
const getBZInfo = async (bzId) => {
try {
uni.showLoading({
title: '加载中...',
mask: true
})
let params = {
projectId: currentProject.value?.ProjectId,
personId: userInfo.value?.PersonId,
packagingManageId: bzId
}
let res = await reqPackagingInformationById(params)
if (res.code == 1) {
const BZInfo = res.data.packagingManageDetailItem
const BZItem = res.data.packagingPrepipeItems
// 检查是否已存在
if (BZList.value.some(item => item.PackagingId === bzId)) {
uni.hideLoading()
return uni.showToast({
title: '该包装已存在',
icon: 'none'
})
}
// 保存当前包装信息用于弹窗显示
currentBZInfo.value = BZInfo
currentBZItems.value = BZItem || []
uni.hideLoading()
// 显示包装详情弹窗
showBZDetail.value = true
} else {
uni.hideLoading()
uni.showToast({
title: res.msg || '获取包装信息失败',
icon: 'none'
})
}
} catch (error) {
console.error('获取包装信息失败:', error)
uni.hideLoading()
uni.showToast({
title: '获取包装信息失败',
icon: 'none'
})
}
}
// ===== 删除包装 =====
const handleDeletePackage = (item) => {
uni.showModal({
title: '确认删除',
content: '确定要删除该包装吗?',
success: (res) => {
if (res.confirm) {
// TODO: 调用删除接口
reqDeletePackagingFromTrain(item.PackagingManageId).then(
(res) => {
if (res.code === 1) {
uni.showToast({
title: '删除成功',
icon: 'none'
})
getPackagingList(form.value.Id)
} else {
uni.showToast({
title: '删除成功',
icon: 'none'
})
}
}
).catch((err) => {
uni.showToast({
title: '删除失败',
icon: 'none'
})
})
}
}
})
}
// ===== 关闭包装详情弹窗 =====
const closeBZDetail = () => {
showBZDetail.value = false
currentBZInfo.value = null
currentBZItems.value = []
}
// ===== 确认添加包装 =====
const confirmAddBZ = () => {
if (currentBZInfo.value) {
reqSavePackagingToTrain({
packagingManageId: currentBZInfo.value.PackagingManageId,
trainNumberId: trainNumberId.value
}).then(
(res) => {
if (res.code === 1) {
uni.showToast({
title: '添加成功',
})
closeBZDetail()
} else {
uni.showToast({
title: res.msg || '添加失败',
icon: 'none'
})
}
}
)
}
}
// ===== 车次详情 =====
const getInfo = async (id) => {
try {
const res = await reqTrainNumberDetail(id)
if (res.code === 1) {
form.value = res.data?.getDataList?.[0] || {}
// 直接使用接口返回的 UnitName,避免依赖 unitList
if (form.value.UnitId && unitList.value.length > 0) {
selectedUnit.value = unitList.value.find(e => e.UnitId === form.value.UnitId) || {}
if (selectedUnit.value.UnitName) {
form.value.UnitName = selectedUnit.value.UnitName
}
}
getPackagingList(form.value.Id)
}
} catch (error) {
console.error('获取车次详情失败:', error)
}
}
// 获取包装列表
const getPackagingList = async (id) => {
try {
const res = await reqPackagingByTrain(id)
if (res.code === 1) {
BZList.value = res.data || []
}
} catch (error) {
console.error('获取包装列表失败:', error)
}
}
// ===== 保存 =====
const handleSave = (type, isSkip=false) => {
if (!form.value.TrainNumber) {
return uni.showToast({
title: '请输入车次',
icon: 'none'
})
}
if (!form.value.UnitId) {
return uni.showToast({
title: '请选择单位',
icon: 'none'
})
}
if (!form.value.DriverName) {
return uni.showToast({
title: '请输入驾驶员姓名',
icon: 'none'
})
}
if (!form.value.DriverPhone) {
return uni.showToast({
title: '请输入驾驶员电话',
icon: 'none'
})
}
if (!form.value.LicensePlateNumber) {
return uni.showToast({
title: '请输入车牌号',
icon: 'none'
})
}
if (!form.value.ContactName) {
return uni.showToast({
title: '请输入联系人姓名',
icon: 'none'
})
}
if (!form.value.ContactPhone) {
return uni.showToast({
title: '请输入联系人电话',
icon: 'none'
})
}
const params = {
Id: form.value.Id || '',
TrainNumber: form.value.TrainNumber,
UnitId: form.value.UnitId,
ProjectId: userInfo.value?.ProjectId || '',
State: type,
DriverName: form.value.DriverName,
DriverPhone: form.value.DriverPhone,
LicensePlateNumber: form.value.LicensePlateNumber,
ContactName: form.value.ContactName,
ContactPhone: form.value.ContactPhone,
Remark: form.value.Remark || ''
}
if (type === 1) {
// 检查是否已添加包装
if (!BZList.value.length) {
return uni.showToast({
title: '请添加包装',
icon: 'none'
})
}
// 发货需要确认
uni.showModal({
title: '提示',
content: '是否确认发货',
showCancel: true,
success: (res) => {
if (res.confirm) {
saveTrainNumber(params,true)
}
}
})
} else {
// 保存直接调用
saveTrainNumber(params)
}
}
// 保存车次信息
const saveTrainNumber = async (params,isSkip=false) => {
try {
disabledBtn.value = true
const res = await reqSaveTrainNumber(params)
if(isSkip){
uni.showToast({
title: params.State === 1 ? '发货成功' : '保存成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack()
}, 2000)
return
}
if (res.code === 1) {
// 更新表单状态
form.value.Id = res.data?.id
form.value.State = params.State
uni.showToast({
title: params.State === 1 ? '发货成功' : '保存成功',
icon: 'none'
})
} else {
uni.showToast({
title: res.msg || '操作失败',
icon: 'none'
})
}
} catch (error) {
console.error('保存失败:', error)
uni.showToast({
title: '操作失败',
icon: 'none'
})
} finally {
disabledBtn.value = false
}
}
// ===== 签收 =====
const handleSign = () => {
// 检查是否已添加包装
if (!BZList.value.length) {
return uni.showToast({
title: '请添加包装',
icon: 'none'
})
}
uni.showModal({
title: '提示',
content: '是否确认签收',
showCancel: true,
success: (res) => {
if (res.confirm) {
signTrainNumber()
}
}
})
}
// 签收车次
const signTrainNumber = async () => {
try {
const res = await reqTrainInfoConfirmArrival(userInfo.value?.UserId || '', form.value.Id)
if (res.code === 1) {
uni.showToast({
title: '签收成功',
icon: 'none'
})
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 900)
} else {
uni.showToast({
title: res.msg || '签收失败',
icon: 'none'
})
}
} catch (error) {
console.error('签收失败:', error)
uni.showToast({
title: '签收失败',
icon: 'none'
})
}
}
// ===== 包装到场状态 =====
const getBZStatus = (state) => {
const statusMap = {
0: '待发货',
1: '已发货',
2: '已签收',
3: '已到场'
}
return statusMap[state] || '--'
}
// ===== 生命周期 =====
onLoad(async (options) => {
trainNumberId.value = options?.id || ''
// 并行加载单位列表和车次详情
if (trainNumberId.value) {
await Promise.all([
getUnitList(),
getInfo(trainNumberId.value)
])
} else {
await getUnitList()
}
})
</script>