货无忧
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1003 lines
23 KiB

<template>
<BasicContainer ref="basicContainer" :option="option">
<template #body>
<view class="main_container">
<!-- 车牌号 -->
<view :class="{row: true, must: details.isError.carNumber}" @click="handleChooseVehicle">
<text class="title msg must">
车牌号
</text>
<view class="flex-c-c">
<text :class="{mr20:true, msg: !details.form.carNumber}">
{{ details.form.carNumber || '请选择车牌号' }}
</text>
<u-icon name="arrow-right" color="#888" size="36"></u-icon>
</view>
</view>
<!-- 主驾司机 -->
<view :class="{row: true, must: details.isError.driverName}" @click="() => handleChooseDriver(1)">
<text class="title msg must">
主驾司机
</text>
<view class="flex-c-c">
<text :class="{mr20:true, msg: !details.form.driverName}">
{{ details.form.driverName || '请选择主驾司机'}}
</text>
<u-icon name="arrow-right" color="#888" size="36"></u-icon>
</view>
</view>
<!-- 副驾司机 -->
<view class="row" @click="() => handleChooseDriver(2)">
<text class="title msg">
副驾司机
</text>
<view class="flex-c-c">
<text :class="{mr20:true, msg: !details.form.assistantName}">
{{ details.form.assistantName || '请选择副驾司机'}}
</text>
<u-icon name="arrow-right" color="#888" size="36"></u-icon>
</view>
</view>
<!-- 配载类型 -->
<view :class="{row: true, must: details.isError.loadType}" @click="() => handleChooseStowageType(3)">
<text class="title msg must">
配载类型
</text>
<view class="flex-c-c">
<text :class="{mr20:true, msg: !details.form.loadTypeName}">
{{ details.form.loadTypeName || '请选择配载类型'}}
</text>
<u-icon name="arrow-right" color="#888" size="36"></u-icon>
</view>
</view>
<!-- 分摊方式 -->
<view :class="{row: true, must: details.isError.chargeType}" @click="() => handleChooseStowageType(4)">
<text class="title msg must">
分摊方式
</text>
<view class="flex-c-c">
<text :class="{mr20:true, msg: !details.form.chargeType}">
{{ details.form.chargeTypeName || '请选择分摊方式'}}
</text>
<u-icon name="arrow-right" color="#888" size="36"></u-icon>
</view>
</view>
<!-- 车辆线路 -->
<u-divider text="线 路 编 辑"></u-divider>
<!-- 线路 -->
<view class="car_line">
<block v-for="(item, index) in details.nodeList" :key="item.nodeName">
<template v-if="index !== 0">
<view class="line mt20 mb20 flex-c-c">
<view class="flex-c-c">
<view class="line_iocn">
<u-icon name="car-fill" color="#d3832a" size="45"></u-icon>
</view>
<view class="line_item">
</view>
</view>
</view>
</template>
<uni-swipe-action-item>
<view class="flex-c-sb">
<!-- 节点 -->
<view class="flex mr20 node flex1">
<u-icon name="map-fill" color="#0086f1" size="40"></u-icon>
<text class="ml20">{{item.nodeName}}</text>
</view>
<!-- 编辑 -->
<view class="addButton mr20" v-if="index !== 0" @click="() => handleAddNode(index, true)">
<u-icon name="setting" color="#fff" size="40"></u-icon>
</view>
<!-- 新增 -->
<view class="addButton" @click="() => handleAddNode(index, false)">
<u-icon name="plus-circle" color="#fff" size="40"></u-icon>
</view>
</view>
<template v-if="index !== 0" v-slot:right>
<!-- 移除 -->
<view @click="()=> handleRemoveNode(index)" class="removeButton" hover-class="clickClass">
<u-icon name="trash" color="#fff" size="40"></u-icon>
</view>
</template>
</uni-swipe-action-item>
</block>
</view>
<!-- 提交按钮 -->
<view class="submitBtn" @click="handleSubmit">
提 交
</view>
</view>
</template>
</BasicContainer>
<MyDrawer ref="myDrawerByTime">
<template #head v-if="![3, 4].includes(details.search.searchType)">
<view class="flex header_search">
<template v-if="details.search.searchType === 0">
<view class="searchInput flex1">
<MyInput clearable v-model="details.search.searchTextByVehicle"
:placeholder="details.search.placeholder" />
</view>
</template>
<template v-if="details.search.searchType === 1">
<view class="searchInput flex1">
<MyInput clearable v-model="details.search.searchTextByDriver1"
:placeholder="details.search.placeholder" />
</view>
</template>
<template v-if="details.search.searchType === 2">
<view class="searchInput flex1">
<MyInput clearable v-model="details.search.searchTextByDriver2"
:placeholder="details.search.placeholder" />
</view>
</template>
<template v-if="details.search.searchType === 5">
<view class="searchInput flex1">
<MyInput clearable v-model="details.search.searchTextByNode"
:placeholder="details.search.placeholder" />
</view>
</template>
<view class="searchBtn ml20" @click="handleSearch">
搜 索
</view>
</view>
</template>
<template #content>
<picker-view v-if="details.visible" :indicator-style="details.indicatorStyle" :immediate-change="true"
:value="details.value" @change="bindChangeByTime" class="picker-view">
<picker-view-column>
<view class="item" v-for="(item, index) in details.renderList" :key="item.label">
{{ item.label }}
</view>
</picker-view-column>
</picker-view>
</template>
</MyDrawer>
<tips ref="tip"></tips>
<!-- #ifdef APP -->
<saomiao2 :ishidestop="scanState !== 0"></saomiao2>
<!-- #endif -->
</template>
<script lang="ts" setup>
import {
postFindCarListByName,
postFindDriverListByName,
postFindWarehouseListByName,
postFindLoadInitData,
postSaveNew
} from '@/api/user.js'
import {
onLoad,
onShow,
} from '@dcloudio/uni-app'
import { nextTick, onMounted, reactive, ref, toRefs, } from "vue";
import useSystemSettingsStore from '@/store/useSystemSettingsStore';
import { storeToRefs } from 'pinia';
import utils from '@/utils/utils.js';
const { scanState } = storeToRefs(useSystemSettingsStore())
const option = reactive({
title: '新增配载计划',
haveData: true,
async pullDownRefreshInitPage() {
console.log('222 :>> ', 222);
},
haveReachBottom: false,
pageLoading: false
})
let details = reactive({
/** 节点列表 */
nodeList: [],
/** 扫描的码值 */
scancode: '',
jpScorllViewHeight: '60vh',
visible: false,
/** picker被选中的索引位置 */
value: [0],
/** picker行高度 */
indicatorStyle: `height: 40px;`,
renderList: [],
search: {
/** 输入框搜索的值 */
searchTextByVehicle: '', // 车辆
searchTextByDriver1: '', // 主驾
searchTextByDriver2: '', // 副驾
searchTextByNode: '', // 节点
placeholder: '',
/** 搜索类型
* 0 -- 车牌号
* 1 -- 主驾司机
* 2 -- 副驾司机
* 3 -- 配载类型
* 4 -- 分摊类型
* 5 -- 节点
*/
searchType: 1,
/** 车牌 */
VehicleArr: [],
/** 司机 */
DriverArr1: [], // 主驾
DriverArr2: [], // 副驾
/** 配载类型 */
StowageTypeArr: [
{
id: "1",
label: "干线",
},
{
id: "2",
label: "支线",
},
{
id: "3",
label: "专车",
}
],
/** 分摊类型 */
AllocationTypeArr: [
{
id: "1",
label: "按件数分摊",
},
{
id: "2",
label: "按体积分摊",
},
{
id: "3",
label: "按重量分摊",
}
],
/** 节点 */
NodeArr: [],
},
isError: {
carNumber: false,
driverName: false,
loadType: false,
chargeType: false,
},
form: {
// 车辆
carNumber: '',
carId: '',
// 主驾
driverName: '',
driverId: '',
driverMobile: '',
driverType: '',
// 副驾
assistantName: '',
assistantMobile: '',
assistantId: '',
// 装车方式
loadingType: 2,
/** 配载类型 */
loadType: '',
loadTypeName: '',
/** 分摊方式 */
chargeType: '',
chargeTypeName: '',
/** 外请人 */
outDriverPerson: '',
/** 新增节点 */
addCarsLoadLineList: [],
/** 移除节点 -- 新增空数组即可 */
removeCarsLoadLineList: [],
/** 始发仓 */
startWarehouseName: '',
startWarehouseId: '',
/** 创建人 */
operator: '',
/** 合计运费 */
countTransportCost: 0
},
/** 费用 */
nodeFree: [
// 现付运输费
'nowTransportCost',
// 现付油卡费
'oilCost',
// 路桥费
'tollFee',
// 回付运输费
'backTransportCost',
// 到付运输费
'reachTransportCost',
// 整车信息费
'wholeCarInfoCost',
// 整车保险费
'wholeCarInsuranceCost',
// 整车落地费
'wholeCarGroundCost',
// 发站装车费
'hairWholeCost',
// 发站其它费
'hairWholeOtherCost',
// 到站卸车费
'unloadCost',
// 到站其它费
'unloadOtherCost'
]
})
// 获取组件实例
const basicContainer = ref(null)
const tip = ref()
const myDrawerByTime = ref()
onLoad((op) => {
})
onShow(async () => {
// #ifdef APP
uni.$off('scancodedate')
uni.$on('scancodedate', function (code) {
if (code) {
console.log('code', code);
details.scancode = code
scandata()
}
})
// #endif
await nextTick()
})
/** 扫描接口
* */
async function scandata() {
}
/** 初始化节点价格 */
const initFree = (item) => {
for (let i = 0; i < details.nodeFree.length; i++) {
const _key = details.nodeFree[i]
item[_key] = 0
}
}
const initPage = async () => {
try {
option.pageLoading = true
const res = await postFindLoadInitData({})
console.log('res :>> ', res);
const { code, data } = res
if (code !== 200 || !data || !data.startWarehouseInfo) return
details.nodeList = [{
nodeName: data.startWarehouseInfo.warehouseName,
nodeId: data.startWarehouseInfo.warehouseId,
linkMan: data.startWarehouseInfo.linkMan,
linkPhone: data.startWarehouseInfo.linkMobile,
linkAddress: data.startWarehouseInfo.linkAddress,
}]
initFree(details.nodeList[0])
console.log('data :>> ', data);
details.form.startWarehouseName = data.startWarehouseInfo.warehouseName;
details.form.startWarehouseId = data.startWarehouseInfo.warehouseId;
console.log(uni.getStorageSync('userinfo'))
details.form.operator = (uni.getStorageSync('userinfo') || {}).nick_name;
console.log('details.form :>> ', details.form);
console.log('details.nodeList :>> ', details.nodeList);
} catch (err) {
console.log('err :>> ', err);
//TODO handle the exception
} finally {
option.pageLoading = false
}
}
initPage()
/** picker切换时执行 -- 时间 */
const bindChangeByTime = function (e) {
console.log('e :>> ', e);
details.value = e.detail.value
}
const handleClose = async () => {
myDrawerByTime.value.details.showPopUp = false
await nextTick()
// details.visible = false
}
/** 选择车牌 */
const handleChooseVehicle = async () => {
details.visible = true
details.value = [0]
details.search.placeholder = '请输入车牌号'
details.search.searchType = 0
details.renderList = details.search.VehicleArr
if (details.form.carId)
for (let i = 0; i < details.renderList.length; i++) {
const value = details.renderList[i]
if (value.carId !== details.form.carId) continue
details.value = [i]
break
}
await nextTick()
myDrawerByTime.value.setDetails({
showPopUp: true,
title: '选择车牌',
success() {
let timer = setTimeout(() => {
const _value = details.renderList[details.value[0]]
if (!_value) return utils.handleToast('暂无可选车辆')
console.log('_value :>> ', _value);
details.form.carNumber = _value.carNumber
details.form.carId = _value.carId
details.isError.carNumber = false
clearTimeout(timer)
timer = null
}, 300)
handleClose()
},
async close() {
handleClose()
}
})
}
/** 选择司机 */
const handleChooseDriver = async (type : 1 | 2) => {
details.visible = true
details.value = [0]
details.search.placeholder = '请输入司机名称'
details.search.searchType = type
details.renderList = type === 1 ? details.search.DriverArr1 : details.search.DriverArr2
const _id = type === 1 ? details.form.driverId : details.form.assistantId
if (_id)
for (let i = 0; i < details.renderList.length; i++) {
const value = details.renderList[i]
if (value.driverId !== _id) continue
details.value = [i]
break
}
await nextTick()
myDrawerByTime.value.setDetails({
showPopUp: true,
title: type === 1 ? '选择主驾司机' : '选择副驾司机',
success() {
let timer = setTimeout(() => {
const _value = details.renderList[details.value[0]]
if (!_value) return utils.handleToast('暂无可选司机')
console.log('_value :>> ', _value);
if (type === 1) {
details.form.driverName = _value.driverName
details.form.driverId = _value.driverId
details.form.driverMobile = _value.driverPhone
details.form.driverType = _value.driverType
details.isError.driverName = false
} else {
details.form.assistantName = _value.driverName
details.form.assistantId = _value.driverId
details.form.assistantMobile = _value.driverPhone
}
clearTimeout(timer)
timer = null
}, 300)
handleClose()
},
async close() {
handleClose()
}
})
}
/** 选择配载类型 || 分摊方式 */
const handleChooseStowageType = async (type : 3 | 4) => {
details.visible = true
details.value = [0]
details.search.searchType = type
details.renderList = type === 3 ? details.search.StowageTypeArr : details.search.AllocationTypeArr
const _id = type === 3 ? details.form.loadType : details.form.chargeType
if (_id)
for (let i = 0; i < details.renderList.length; i++) {
const value = details.renderList[i]
if (value.id !== _id) continue
details.value = [i]
break
}
await nextTick()
myDrawerByTime.value.setDetails({
showPopUp: true,
title: '选择配载类型',
success() {
let timer = setTimeout(() => {
const _value = details.renderList[details.value[0]]
if (!_value) return utils.handleToast('暂无可选类型')
console.log('_value :>> ', _value);
if (type === 3) {
details.form.loadType = _value.id
details.form.loadTypeName = _value.label
details.isError.loadType = false
} else {
details.form.chargeType = _value.id
details.form.chargeTypeName = _value.label
details.isError.chargeType = false
}
clearTimeout(timer)
timer = null
}, 300)
handleClose()
},
async close() {
handleClose()
}
})
}
/** 新增节点 */
const handleAddNode = async (index : number, isEdit) => {
details.visible = true
details.value = [0]
details.search.placeholder = '请输入节点名称'
details.search.searchType = 5
details.renderList = details.search.NodeArr
myDrawerByTime.value.setDetails({
showPopUp: true,
title: isEdit ? '编辑节点' : '新增节点',
success() {
uni.showLoading({
mask: true
})
let timer = setTimeout(() => {
uni.hideLoading()
const _value = details.renderList[details.value[0]]
if (!_value) return utils.handleToast('暂无可选类型')
console.log('_value :>> ', _value);
for (let i = 0; i < details.nodeList.length; i++) {
const _node = details.nodeList[i]
if (_node.nodeId === _value.warehouseId) return utils.handleToast('节点已存在, 请勿重复选择节点')
}
// 编辑
if (isEdit) {
const _node = details.nodeList[index]
_node.nodeName = _value.warehouseName
_node.nodeId = _value.warehouseId
}
// 新增
else {
details.nodeList.splice(index + 1, 0, {
nodeName: _value.warehouseName,
nodeId: _value.warehouseId
})
}
clearTimeout(timer)
timer = null
handleClose()
}, 300)
},
async close() {
handleClose()
}
})
}
/** 删除节点 */
const handleRemoveNode = (index : number) => {
details.nodeList.splice(index, 1)
}
/** 搜索车牌 */
const searchVehicle = async () => {
const res = await postFindCarListByName({ carNumber: details.search.searchTextByVehicle })
const { code, data } = res
if (code !== 200) return
details.search.VehicleArr = data.map(val => {
let _obj = { ...val }
_obj.label = val.carNumber
return _obj
})
console.log('122112 :>> ', 122112);
details.renderList = [...details.search.VehicleArr]
}
searchVehicle()
/** 搜索司机 */
const searchDriver = async (isOnLoad = false) => {
const res = await postFindDriverListByName({ driverName: details.search.searchType === 1 ? details.search.searchTextByDriver1 : details.search.searchTextByDriver2, jobType: 1 })
const { code, data } = res
if (code !== 200) return
const _data = data.map(val => {
let _obj = { ...val }
_obj.label = val.driverName
return _obj
})
if (isOnLoad || details.search.searchType === 1) {
details.search.DriverArr1 = [..._data]
details.renderList = [...details.search.DriverArr1]
}
if (isOnLoad || details.search.searchType === 2) {
details.search.DriverArr2 = [..._data]
details.renderList = [...details.search.DriverArr2]
}
}
searchDriver(true)
/** 搜索节点 */
const searchNode = async () => {
console.log('222 :>> ', 222);
const res = await postFindWarehouseListByName({ warehouseName: details.search.searchTextByNode, })
const { code, data } = res
if (code !== 200) return
details.search.NodeArr = data.map(val => {
let _obj = { ...val }
_obj.label = val.warehouseName
return _obj
})
details.renderList = [...details.search.NodeArr]
}
searchNode()
/** 搜索 */
const handleSearch = async () => {
// 搜索类型
switch (details.search.searchType) {
// 车牌
case 0:
await searchVehicle()
break
// 主驾
case 1:
await searchDriver()
break
// 副驾
case 2:
await searchDriver()
break
// 节点
case 5:
await searchNode()
break
}
await nextTick()
details.value = [0]
}
const handleSubmit = async () => {
try {
!details.form.carNumber && (details.isError.carNumber = true)
let _flag = false
for (let key in details.isError) {
details.isError[key] = false
if (details.form[key]) continue
_flag = true
details.isError[key] = true
}
if (_flag) return utils.handleToast('存在必选项或必填项未处理')
if (details.nodeList.length < 2) return utils.handleToast('请选择节点')
// 开启loading
option.pageLoading = true
const _submitData : any = {
...details.form
}
_submitData.addCarsLoadLineList = JSON.parse(JSON.stringify(details.nodeList))
const _warehouseIds = [];
const _warehouseNames = [];
for (let i = 0; i < _submitData.addCarsLoadLineList.length; i++) {
const value = _submitData.addCarsLoadLineList[i]
value.nodeType = '1';
value.sort = i + 1;
value.updateType = 2;
_warehouseNames.push(value.nodeName);
if (i === 0) continue
_warehouseIds.push(value.nodeId);
}
// 目的仓
_submitData.endWarehouseIds = _warehouseIds.join(',');
_submitData.endWarehouseNames = _warehouseNames.slice(1).join(',');
_submitData.carsLineName = _warehouseNames.join('->');
console.log('11 :>> ', 11);
const res = await postSaveNew(_submitData)
const { code, msg } = res
if (code !== 200) return
utils.handleToast(msg)
let timer = setTimeout(() => {
uni.navigateBack()
clearTimeout(timer)
timer = null
}, 500)
} catch (err) {
console.log('err :>> ', err);
//TODO handle the exception
} finally {
option.pageLoading = false
}
}
</script>
<style lang="scss" scoped>
@import url(@/utils/style/common.scss);
.main_container {
// font-size: 1rem;
font-size: 0.9rem;
.row {
margin-top: 20upx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20upx;
background-color: #fff;
margin-left: 20upx;
margin-right: 20upx;
border-radius: 10upx;
border: 4upx solid #fff;
transition: all 0.3s;
&.must {
border-color: var(--errColor);
}
}
.title {
font-weight: bold;
// 必选
&.must {
&::before {
content: '*';
display: inline-block;
color: var(--errColor);
}
}
}
.msg {
color: #888;
}
// 线路
.car_line {
padding: 20upx;
padding-top: 0;
// 节点
.node {
background: #fff;
padding: 20upx;
}
// 新增按钮
.addButton {
background: var(--primaryColor);
color: #fff;
padding: 20upx 40upx;
border-radius: 6upx;
}
// 移除按钮
.removeButton {
// margin: 20upx;
margin-left: 20upx;
background: #f8625a;
padding: 0 40upx;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10upx;
color: #fff;
transition: all 0.3s;
}
.clickClass {
opacity: 0.7;
}
.line {
margin: 40upx;
}
.line_iocn {
transform: rotateZ(-270deg) rotateX(180deg);
}
.line_item {
height: 80upx;
width: 0;
border: 1upx solid var(--subjectColor);
position: relative;
&::before {
position: absolute;
content: '';
display: block;
width: 20upx;
border-radius: 50%;
height: 20upx;
background: var(--subjectColor);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
}
&::after {
height: 0upx;
width: 0upx;
content: '';
display: block;
border: 10upx solid transparent;
border-top: 20upx solid var(--subjectColor);
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
}
}
}
// 提交按钮
.submitBtn {
padding: 20upx;
background: var(--subjectColor);
width: 70%;
text-align: center;
border-radius: 10upx;
font-weight: bold;
color: #fff;
margin: 20upx auto;
}
}
// picker样式
.picker-view {
height: 40vh;
.item {
text-align: center;
line-height: 40px;
}
}
// 标头
:deep(.u-divider) {
.u-divider__text {
font-size: 1rem !important;
color: var(--subjectColor) !important;
}
.u-line {
border-color: var(--subjectColor) !important;
}
}
// 抽屉 -- 顶部搜索
.header_search {
padding: 0 20upx;
// 搜索输入框
.searchInput {
height: 60upx;
background: #F5F5F6;
border-radius: 8upx;
opacity: 1;
border: 2upx solid #00000020;
padding: 4upx;
:deep(.input_container) {
border: none;
}
}
// 搜索按钮
.searchBtn {
height: 70upx;
line-height: 70upx;
padding: 0 30upx;
background: var(--subjectColor);
color: #fff;
border-radius: 5upx;
}
}
</style>