提交
This commit is contained in:
263
pages/index/addDelivery.vue
Normal file
263
pages/index/addDelivery.vue
Normal file
@@ -0,0 +1,263 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<z-paging ref="pagingRef" class="containerBox" v-model="listData" :default-page-size="queryParams.pageSize"
|
||||
@query="queryList">
|
||||
<view class="box">
|
||||
<view v-for="(item, index) in listData" :key="index" @tap="changeSelect(index)" class="item"
|
||||
:style="index == selectIndex ? 'border: #199793 4rpx solid;' : ''">
|
||||
<uv-icon v-show="index == selectIndex" class="selectIcon" color="#199793" size="20"
|
||||
name="checkbox-mark"></uv-icon>
|
||||
<view class="title">单据号:{{ item.billNo }}</view>
|
||||
<view><text>出库类型:</text>{{ item.operationTypeName }}</view>
|
||||
<view><text>理货员:</text>{{ item.operatorName }}</view>
|
||||
<view><text>施工队:</text>{{ item.teamName }}</view>
|
||||
<view><text>出库时间:</text>{{ parseTime(item.operationTime) }}</view>
|
||||
<view class="more" v-show="!item.showMore" @tap="openMore(index)">
|
||||
<view>详细信息</view>
|
||||
<uv-icon name="arrow-right" color="#A4A6A7" size="28rpx" style="margin-left: 10rpx;"></uv-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
</z-paging>
|
||||
<view class="btn">
|
||||
<uv-button type="primary" text="确定" size="large" style="width: 48%;" @tap="toAddHandDelivery">录 入 配 送 货
|
||||
物</uv-button>
|
||||
<uv-button type="primary" text="确定" size="large" style="width: 48%;" class="mainBtn" @tap="openMore(selectIndex)">选 择 配 送 货
|
||||
物</uv-button>
|
||||
</view>
|
||||
|
||||
<uv-modal ref="modalRef" title="单据明细" class="modalInfo" confirmText="确定" showCancelButton @close="closeModal" @confirm="confirmSelected"
|
||||
:closeOnClickOverlay="false" asyncClose>
|
||||
<view class="box">
|
||||
<view v-for="items in detailList" class="item" :style="items.selected ? 'border-color: #2979ff;' : ''" @tap="changeSelected(items)">
|
||||
<uv-icon v-show="items.selected" class="selectIcon" color="#2979ff" size="20" name="checkbox-mark"></uv-icon>
|
||||
<view><text>订单编号:</text>{{ items.sapNo }}</view>
|
||||
<view><text>项目描述:</text>{{ items.xmMs }}</view>
|
||||
<view><text>项目号:</text>{{ items.xmNo }}</view>
|
||||
<view><text>物料号:</text>{{ items.wlNo }}</view>
|
||||
<view><text>物料描述:</text>{{ items.wlMs }}</view>
|
||||
<view><text>供应商名称:</text>{{ items.gysMc }}</view>
|
||||
<view><text>存放位置:</text>{{ items.pcode || "-" }}</view>
|
||||
<view><text>身份码:</text>{{ items.entityId || "-" }}</view>
|
||||
<view><text>备注:</text>{{ items.remark || '-' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</uv-modal>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { selectInfo } from "@/api/index"
|
||||
// selectInfoDetail
|
||||
|
||||
|
||||
const selectIndex = ref(0)
|
||||
const changeSelect = (index) => {
|
||||
selectIndex.value = index
|
||||
}
|
||||
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const pagingRef = ref(null)
|
||||
const listData = ref([])
|
||||
// 获取列表
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
queryParams.value.pageNum = pageNo
|
||||
console.log(pageNo, pageSize, queryParams.value)
|
||||
selectInfo(queryParams.value).then(res => {
|
||||
console.log(res.data)
|
||||
res.data.forEach(e => {
|
||||
e.showMore = false;
|
||||
e.recordList.forEach(e => {
|
||||
e.totalWeight = e.realQty * e.weightKg
|
||||
e.totalVolume = e.realQty * e.volumeM3
|
||||
e.rkRecordId = e.id
|
||||
e.selected = false
|
||||
})
|
||||
});
|
||||
pagingRef.value.complete(res.data)
|
||||
}).catch(res => {
|
||||
pagingRef.value.complete(false)
|
||||
})
|
||||
}
|
||||
|
||||
// const getDetail = (index) => {
|
||||
// return new Promise((resolve, reject) => {
|
||||
// if (listData.value[index].detailList.length > 0) {
|
||||
// detailList.value = listData.value[index].detailList
|
||||
// resolve("完成")
|
||||
// } else {
|
||||
// selectInfoDetail({ billNoCk: listData.value[index].billNoCk }).then(res => {
|
||||
// res.rows.forEach(e => {
|
||||
// e.totalWeight = e.realQty * e.weightKg
|
||||
// e.totalVolume = e.realQty * e.volumeM3
|
||||
// })
|
||||
// detailList.value = res.rows
|
||||
// listData.value[index].detailList = res.rows
|
||||
// resolve()
|
||||
// }).catch(res => {
|
||||
// reject(res)
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// 跳转至录入货物信息页面
|
||||
const toAddHandDelivery = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/addHandDelivery',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 打开弹窗
|
||||
const detailList = ref([])
|
||||
const openMore = async (index) => {
|
||||
// await getDetail(index)
|
||||
console.log(listData.value[index])
|
||||
detailList.value = listData.value[index].recordList
|
||||
modalRef.value.open()
|
||||
};
|
||||
|
||||
// 弹窗
|
||||
const modalRef = ref()
|
||||
|
||||
const changeSelected = (items) => {
|
||||
items.selected = !items.selected
|
||||
}
|
||||
|
||||
const closeModal = () => {
|
||||
console.log("关闭")
|
||||
listData.value[selectIndex.value].showMore = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
const confirmSelected = async () => {
|
||||
// await getDetail(selectIndex.value)
|
||||
let selectedList = JSON.parse(JSON.stringify(listData.value[selectIndex.value]))
|
||||
selectedList.detailList = selectedList.recordList.filter(e => e.selected)
|
||||
if (selectedList.detailList.length == 0) {
|
||||
uni.showToast({
|
||||
title: "请选择货物信息",
|
||||
icon: "none"
|
||||
})
|
||||
modalRef.value.closeLoading()
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/addDeliveryInfo',
|
||||
success: function(res) {
|
||||
// 通过eventChannel向被打开页面传送数据
|
||||
res.eventChannel.emit('acceptData', { data: selectedList })
|
||||
modalRef.value.close()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
pagingRef.value?.reload()
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
position: relative;
|
||||
|
||||
.containerBox {
|
||||
padding: 32rpx;
|
||||
padding-bottom: 120rpx;
|
||||
|
||||
|
||||
|
||||
}
|
||||
.btn {
|
||||
position: fixed;
|
||||
width: calc(100vw - 64rpx);
|
||||
bottom: 0;
|
||||
left: 32rpx;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.box {
|
||||
|
||||
// background-color: #ebebeb;
|
||||
// background-color: #f5f5f5;
|
||||
// padding-top: 20rpx;
|
||||
.item {
|
||||
border: #969696 4rpx solid;
|
||||
padding: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #333;
|
||||
position: relative;
|
||||
|
||||
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
text {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.more {
|
||||
color: #A4A6A7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
}
|
||||
.selectIcon {
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
right: 10rpx;
|
||||
}
|
||||
|
||||
.modalInfo {
|
||||
.box {
|
||||
width: 100%;
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
.item {
|
||||
background: #efefef;
|
||||
padding: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #333;
|
||||
border: 2rpx solid #efefef;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep uni-toast {
|
||||
z-index: 30000 !important;
|
||||
}
|
||||
</style>
|
||||
372
pages/index/addDeliveryInfo.vue
Normal file
372
pages/index/addDeliveryInfo.vue
Normal file
@@ -0,0 +1,372 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<uv-form labelPosition="top" labelWidth="100" :model="formModel" :rules="deliveryRules" ref="deliveryFormRef">
|
||||
<view class="formContent">
|
||||
<view class="title">配送信息</view>
|
||||
<uv-line color="#a8a8a8"></uv-line>
|
||||
<uv-form-item label="发货地" prop="originName">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.originName" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="目的地" prop="destName">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.destName" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="配送日期" prop="deliveryDate">
|
||||
<view class="carPickerText" :style="formModel.deliveryDate ? 'color: #303133;' : ''" @tap="openCalendar">{{ formModel.deliveryDate || '请选择配送日期'}}</view>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="发货人" prop="shipperName">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.shipperName" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="发货人电话" prop="shipperPhone">
|
||||
<uv-input placeholder="请输入内容" maxlength="11" type="number" v-model="formModel.shipperPhone" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="接收人" prop="receiverName">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.receiverName" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="接收人电话" prop="receiverPhone">
|
||||
<uv-input placeholder="请输入内容" maxlength="11" type="number" v-model="formModel.receiverPhone" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="接收单位" prop="receiverOrgName">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.receiverOrgName" clearable />
|
||||
</uv-form-item>
|
||||
</view>
|
||||
<view class="formContent" style="margin-top: 20rpx;">
|
||||
<view class="title">货物信息</view>
|
||||
<uv-line color="#a8a8a8"></uv-line>
|
||||
<view style="display: flex;align-items: center;">
|
||||
<!-- <uv-form-item label="配送吨位" prop="deliveryTon">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.deliveryTon"clearable />
|
||||
</uv-form-item> -->
|
||||
<view style="flex: 1;">
|
||||
<!-- <uv-form-item label="高速费用" prop="tollFee">
|
||||
<uv-input placeholder="请输入内容" type="digit" v-model="formModel.tollFee" clearable />
|
||||
</uv-form-item> -->
|
||||
<uv-form-item label="总公里数" prop="totalKm">
|
||||
<uv-input placeholder="请输入内容" type="digit" v-model="formModel.totalKm" clearable />
|
||||
</uv-form-item>
|
||||
</view>
|
||||
<view class="matchCar" @tap="autoComputed(true)">
|
||||
<view>匹配</view>
|
||||
<view>车型</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="formContent" style="margin-top: 20rpx;">
|
||||
<view class="title">配送车辆</view>
|
||||
<uv-line color="#a8a8a8"></uv-line>
|
||||
<uv-form-item label="车型" prop="vehicleTypeName">
|
||||
<view class="carPickerText" :style="formModel.vehicleTypeName ? 'color: #303133;' : ''" @tap="openCarPicker">{{ formModel.vehicleTypeName || '请选择配送车型'}}</view>
|
||||
</uv-form-item>
|
||||
</view>
|
||||
</uv-form>
|
||||
|
||||
<uv-picker ref="carPicker" :columns="carColumns" keyName="name" @confirm="carConfirm"></uv-picker>
|
||||
|
||||
<uv-calendar ref="calendar" @confirm="confirm"></uv-calendar>
|
||||
|
||||
|
||||
<view class="priceBtn">
|
||||
<view>预估价格:<text style="color: #D55A66;">¥</text><text class="price">{{ formModel.suggestFee || "0.00" }}</text></view>
|
||||
<uv-button type="primary" size="large" class="mainBtn" @click="submitForm">发布配送</uv-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, getCurrentInstance } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { autoCarAndPrice, sendOrder, getCarList } from "@/api/index"
|
||||
|
||||
const deliveryFormRef = ref()
|
||||
const formModel = ref({
|
||||
originName: '',
|
||||
destName: '',
|
||||
deliveryDate: '',
|
||||
shipperName: '',
|
||||
shipperPhone: '',
|
||||
receiverName: '',
|
||||
receiverPhone: '',
|
||||
receiverOrgName: '',
|
||||
tollFee: '',
|
||||
totalKm: '',
|
||||
vehicleTypeName: '',
|
||||
// originName: '贾庵子',
|
||||
// destName: '路北供电室',
|
||||
// deliveryDate: '2025-10-28',
|
||||
// shipperName: '林总',
|
||||
// shipperPhone: '13000000000',
|
||||
// receiverName: '王总',
|
||||
// receiverPhone: '13100000000',
|
||||
// receiverOrgName: '供电所',
|
||||
// tollFee: '',
|
||||
// totalKm: '10',
|
||||
// vehicleTypeName: '',
|
||||
})
|
||||
const deliveryRules = ref({
|
||||
'originName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入起点位置',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'destName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入终点位置',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'deliveryDate': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请选择配送日期',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'shipperName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入发货人',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'shipperPhone': [
|
||||
{
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入发货人电话',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// 自定义校验逻辑
|
||||
return uni.$uv.test.mobile(value)
|
||||
},
|
||||
message: '请输入正确的手机号',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
'receiverName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入接收人',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'receiverPhone': [
|
||||
{
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入接收人电话',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
// 自定义校验逻辑
|
||||
return uni.$uv.test.mobile(value)
|
||||
},
|
||||
message: '请输入正确的手机号',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
'receiverOrgName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入接收单位',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'tollFee': {
|
||||
type: 'float',
|
||||
required: true,
|
||||
message: '请输入高速费用',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'totalKm': {
|
||||
type: 'float',
|
||||
required: true,
|
||||
message: '请输入总公里数',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'vehicleTypeName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请选择配送车型',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
})
|
||||
|
||||
// 配送日期选择器
|
||||
const openCalendar = () => {
|
||||
calendar.value.open()
|
||||
}
|
||||
const calendar = ref()
|
||||
const confirm = (e) => {
|
||||
console.log(e)
|
||||
formModel.value.deliveryDate = e[0]
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 配送车辆选择器
|
||||
const openCarPicker = () => {
|
||||
carPicker.value.open()
|
||||
}
|
||||
const carPicker = ref()
|
||||
const carColumns = ref([])
|
||||
const carConfirm = (e) => {
|
||||
console.log(e)
|
||||
// formModel.value.vehicleTypeId = e.indexs
|
||||
formModel.value.vehicleTypeName = e.value[0].name
|
||||
formModel.value.vehicleTypeId = e.value[0].id
|
||||
autoComputed(false)
|
||||
}
|
||||
|
||||
|
||||
const submitForm = () => {
|
||||
console.log(formModel.value)
|
||||
|
||||
deliveryFormRef.value.validate().then(res => {
|
||||
let params = {
|
||||
...formModel.value,
|
||||
items:selectedInfo.value.detailList
|
||||
}
|
||||
console.log(params)
|
||||
sendOrder(params).then(res => {
|
||||
console.log(res)
|
||||
uni.$uv.toast('发布成功')
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
})
|
||||
}).catch(errors => {
|
||||
})
|
||||
}
|
||||
|
||||
let selectedInfo = ref({})
|
||||
|
||||
const autoComputed = (status) => {
|
||||
if (!formModel.value.totalKm) {
|
||||
uni.$uv.toast('请输入总公里数')
|
||||
return
|
||||
}
|
||||
let selectedParams = ref({
|
||||
weightTon: 0,
|
||||
volumeM3: 0,
|
||||
distanceKm: 0,
|
||||
})
|
||||
selectedParams.value.weightTon = formModel.value.deliveryTon
|
||||
selectedParams.value.volumeM3 = formModel.value.goodsSize
|
||||
selectedParams.value.distanceKm = formModel.value.totalKm
|
||||
if (!status) {
|
||||
selectedParams.value.vehicleTypeId = formModel.value.vehicleTypeId
|
||||
}
|
||||
autoCarAndPrice(selectedParams.value).then(res => {
|
||||
console.log(res)
|
||||
if (res.data.errorMessage) {
|
||||
uni.$uv.toast(res.data.errorMessage)
|
||||
return
|
||||
}
|
||||
if (status) {
|
||||
console.log("变")
|
||||
carColumns.value[0] = res.data.candidates
|
||||
formModel.value.vehicleTypeName = res.data.vehicleTypeName
|
||||
formModel.value.vehicleTypeId = res.data.vehicleTypeId
|
||||
}
|
||||
formModel.value.suggestFee = res.data.suggestFee
|
||||
})
|
||||
}
|
||||
|
||||
// const getCarDataList = () => {
|
||||
// let obj = {
|
||||
// page: 1,
|
||||
// pageSize: 100,
|
||||
// }
|
||||
// getCarList(obj).then(res => {
|
||||
// console.log(res)
|
||||
// // carColumns.value[0] = res.rows
|
||||
// })
|
||||
// }
|
||||
|
||||
onLoad(() => {
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel();
|
||||
eventChannel.on('acceptData', function(data) {
|
||||
console.log('acceptData',data)
|
||||
selectedInfo.value = data.data
|
||||
if (data.totalWeightKg) {
|
||||
formModel.value.deliveryTon = data.totalWeightKg / 1000
|
||||
formModel.value.goodsSize = data.goodsSize / 1000
|
||||
} else {
|
||||
formModel.value.deliveryTon = selectedInfo.value.detailList.reduce((pre, cur) => pre + cur.totalWeight * 100000, 0) / 100000000
|
||||
formModel.value.goodsSize = selectedInfo.value.detailList.reduce((pre, cur) => pre + cur.totalVolume * 100000, 0) / 100000000
|
||||
}
|
||||
console.log(formModel.value)
|
||||
// getCarDataList()
|
||||
})
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 32rpx;
|
||||
background-color: #F6F8FA;
|
||||
padding-bottom: 160rpx;
|
||||
// ::v-deep .uv-input{
|
||||
// border: unset;
|
||||
// }
|
||||
.title {
|
||||
// font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// margin-left: 20rpx;
|
||||
// margin-bottom: 20rpx;
|
||||
color: #333333;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
.formContent {
|
||||
background-color: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.matchCar{
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
background-color: #3c9cff;
|
||||
border-radius: 10rpx;
|
||||
color: white;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
.carPickerText{
|
||||
padding: 16rpx 18rpx;
|
||||
padding-left: 20rpx;
|
||||
color: #c0c4cc;
|
||||
border: #dadbde 1px solid;
|
||||
border-radius: 8rpx;
|
||||
width: 100%;
|
||||
}
|
||||
.priceBtn{
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
padding: 16rpx 32rpx;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: white;
|
||||
box-sizing: border-box;
|
||||
.price {
|
||||
font-size: 42rpx;
|
||||
color: #D55A66;
|
||||
}
|
||||
::v-deep .uv-button--large{
|
||||
padding: 0 60rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
190
pages/index/addHandDelivery.vue
Normal file
190
pages/index/addHandDelivery.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<uv-form labelPosition="top" style="margin-bottom: 20rpx;" labelWidth="100" :model="item" :rules="deliveryRules" :ref="(el) => el && (deliveryFormRef[index] = el)" v-for="(item, index) in listData" :key="index">
|
||||
<view class="formContent">
|
||||
<uv-form-item label="订单编号:" prop="sapNo">
|
||||
<uv-input placeholder="请输入内容" v-model="item.sapNo" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="项目描述:" prop="xmMs">
|
||||
<uv-input placeholder="请输入内容" v-model="item.xmMs" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="项目号:" prop="xmNo">
|
||||
<uv-input placeholder="请输入内容" v-model="item.xmNo" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="物料号:" prop="wlNo">
|
||||
<uv-input placeholder="请输入内容" v-model="item.wlNo" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="物料描述:" prop="wlMs">
|
||||
<uv-input placeholder="请输入内容" v-model="item.wlMs" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="配送数量:" prop="realQty">
|
||||
<uv-input placeholder="请输入内容" type="digit" v-model="item.realQty" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="单位" prop="dw">
|
||||
<uv-input placeholder="请输入内容" v-model="item.dw" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="供应商名称:" prop="gysMc">
|
||||
<uv-input placeholder="请输入内容" v-model="item.gysMc" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="备注:" prop="remark">
|
||||
<uv-input placeholder="请输入内容" v-model="item.remark" clearable />
|
||||
</uv-form-item>
|
||||
<template v-if="index !== 0">
|
||||
<view style="display: flex; justify-content: flex-end;">
|
||||
<uv-button type="error" text="删除" style="width: 200rpx;" @tap="removeForm(index)">删 除</uv-button>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</uv-form>
|
||||
<view style="display: flex; justify-content: flex-end;margin-right: 20rpx;">
|
||||
<uv-button type="primary" text="添加" style="width: 200rpx;" @tap="addForm">添 加</uv-button>
|
||||
</view>
|
||||
<view class="btn">
|
||||
<uv-button type="primary" text="确定" size="large" style="width: 100%;" class="mainBtn" @tap="submitForm">确 定 配 送 货
|
||||
物</uv-button>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { getGoodsDict } from "@/api/index.js";
|
||||
|
||||
const listData = ref([
|
||||
{
|
||||
sapNo: "",
|
||||
xmMs: "",
|
||||
xmNo: "",
|
||||
wlNo: "",
|
||||
wlMs: "",
|
||||
realQty: null,
|
||||
dw: "",
|
||||
gysMc: "",
|
||||
remark: ""
|
||||
}
|
||||
])
|
||||
|
||||
const deliveryFormRef = ref([])
|
||||
const deliveryRules = ref({
|
||||
sapNo: [
|
||||
{ required: true, message: "请输入订单编号", trigger: "blur" }
|
||||
],
|
||||
xmMs: [
|
||||
{ required: true, message: "请输入项目描述", trigger: "blur" }
|
||||
],
|
||||
xmNo: [
|
||||
{ required: true, message: "请输入项目号", trigger: "blur" }
|
||||
],
|
||||
wlNo: [
|
||||
{ required: true, message: "请输入物料号", trigger: "blur" }
|
||||
],
|
||||
wlMs: [
|
||||
{ required: true, message: "请输入物料描述", trigger: "blur" }
|
||||
],
|
||||
realQty: [
|
||||
{ required: true, type: 'float', message: "请输入配送数量", trigger: "blur" }
|
||||
],
|
||||
dw: [
|
||||
{ required: true, message: "请输入单位", trigger: "blur" }
|
||||
],
|
||||
gysMc: [
|
||||
{ required: true, message: "请输入供应商名称", trigger: "blur" }
|
||||
],
|
||||
})
|
||||
|
||||
|
||||
const removeForm = (index) => {
|
||||
listData.value.splice(index, 1)
|
||||
}
|
||||
|
||||
const addForm = () => {
|
||||
listData.value.push({
|
||||
sapNo: "",
|
||||
xmMs: "",
|
||||
xmNo: "",
|
||||
wlNo: "",
|
||||
wlMs: "",
|
||||
realQty: "",
|
||||
dw: "",
|
||||
gysMc: "",
|
||||
remark: ""
|
||||
})
|
||||
}
|
||||
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
// 遍历所有表单实例,逐个校验
|
||||
for (const formRef of deliveryFormRef.value) {
|
||||
if (formRef) {
|
||||
// 等待单个表单校验完成,失败则抛出异常
|
||||
await formRef.validate();
|
||||
}
|
||||
}
|
||||
|
||||
// 所有表单校验通过后的逻辑
|
||||
uni.$uv.toast('校验通过,准备提交');
|
||||
|
||||
console.log("提交数据:", listData.value);
|
||||
// 这里写你的提交逻辑
|
||||
let arr = listData.value.map(item => {
|
||||
return {
|
||||
wlNo: item.wlNo,
|
||||
qty: item.realQty,
|
||||
}
|
||||
})
|
||||
getGoodsDict({ items: arr }).then(res => {
|
||||
console.log(res)
|
||||
uni.navigateTo({
|
||||
url: '/pages/index/addDeliveryInfo',
|
||||
success: function(res1) {
|
||||
// 通过eventChannel向被打开页面传送数据
|
||||
res1.eventChannel.emit('acceptData', { data: {detailList: listData.value}, totalWeightKg: res.data.totalWeightKg, goodsSize: res.data.totalVolumeM3 })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
} catch (errors) {
|
||||
// 校验失败的处理
|
||||
console.log("校验失败:", errors);
|
||||
uni.$uv.toast('请完善必填项后提交');
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 32rpx;
|
||||
background-color: #F6F8FA;
|
||||
padding-bottom: 160rpx;
|
||||
.formContent {
|
||||
background-color: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 20rpx 20rpx;
|
||||
}
|
||||
.btn {
|
||||
position: fixed;
|
||||
width: calc(100vw - 64rpx);
|
||||
bottom: 0;
|
||||
left: 32rpx;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
::v-deep uni-toast {
|
||||
z-index: 30000 !important;
|
||||
}
|
||||
</style>
|
||||
682
pages/index/deliveryBill.vue
Normal file
682
pages/index/deliveryBill.vue
Normal file
@@ -0,0 +1,682 @@
|
||||
<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;"> / </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>
|
||||
307
pages/index/editDelivery.vue
Normal file
307
pages/index/editDelivery.vue
Normal file
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<uv-form labelPosition="top" labelWidth="100" :model="formModel" :rules="deliveryRules" ref="deliveryFormRef">
|
||||
<view class="formContent">
|
||||
<view class="title">{{ formModel.scene == 'ORIGIN' ? '开始' : '完成' }}配送信息</view>
|
||||
<uv-line color="#a8a8a8"></uv-line>
|
||||
<uv-form-item label="司机姓名" prop="driverName" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.driverName" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="配送车牌" prop="plateNo" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-input placeholder="请输入内容" v-model="formModel.plateNo" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="司机电话" prop="driverPhone" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-input placeholder="请输入内容" maxlength="11" type="number" v-model="formModel.driverPhone" clearable />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="位置信息" prop="lng">
|
||||
<view class="carPickerText" :style="formModel.lng ? 'color: #303133;' : ''" @tap="getCurrentLocation">{{ formModel.lng ? '获取成功' : '请点击获取当前位置'}}</view>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="司机签名" prop="SIGN_DRIVER" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-button type="warning" text="去签名" v-if="!formModel.SIGN_DRIVER" @tap="goSign('SIGN_DRIVER')"></uv-button>
|
||||
<uv-image height="80" mode="heightFix" v-if="formModel.SIGN_DRIVER" @tap="previewImage(formModel.SIGN_DRIVER[0].url)" :src="formModel.SIGN_DRIVER[0].url"></uv-image>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="配送人签名" prop="SIGN_COURIER" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-button type="warning" text="去签名" v-if="!formModel.SIGN_COURIER" @tap="goSign('SIGN_COURIER')"></uv-button>
|
||||
<uv-image height="80" mode="heightFix" v-if="formModel.SIGN_COURIER" @tap="previewImage(formModel.SIGN_COURIER[0].url)" :src="formModel.SIGN_COURIER[0].url"></uv-image>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="接收人签名" prop="SIGN_RECEIVER" v-if="formModel.scene == 'DEST'">
|
||||
<uv-button type="warning" text="去签名" v-if="!formModel.SIGN_RECEIVER" @tap="goSign('SIGN_RECEIVER')"></uv-button>
|
||||
<uv-image height="80" mode="heightFix" v-if="formModel.SIGN_RECEIVER" @tap="previewImage(formModel.SIGN_RECEIVER[0].url)" :src="formModel.SIGN_RECEIVER[0].url"></uv-image>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="现场照片" prop="PHOTO_SITE">
|
||||
<uv-upload
|
||||
:fileList="formModel.PHOTO_SITE"
|
||||
name="PHOTO_SITE"
|
||||
:maxCount="10"
|
||||
@afterRead="afterRead($event, 'PHOTO_SITE')"
|
||||
@delete="deleteImg(index, 'PHOTO_SITE')"
|
||||
></uv-upload>
|
||||
</uv-form-item>
|
||||
<uv-form-item label="单据照片" prop="PHOTO_BILL" v-if="formModel.scene == 'ORIGIN'">
|
||||
<uv-upload
|
||||
:fileList="formModel.PHOTO_BILL"
|
||||
name="PHOTO_BILL"
|
||||
:maxCount="1"
|
||||
@afterRead="afterRead($event, 'PHOTO_BILL')"
|
||||
@delete="deleteImg(index, 'PHOTO_BILL')"
|
||||
></uv-upload>
|
||||
</uv-form-item>
|
||||
</view>
|
||||
</uv-form>
|
||||
|
||||
|
||||
<view class="btn">
|
||||
<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, getCurrentInstance } from "vue";
|
||||
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||
import { uploadDeliveryAttachment, startOrder } from "@/api/index"
|
||||
|
||||
const deliveryFormRef = ref()
|
||||
const formModel = ref({
|
||||
orderNo: null,
|
||||
scene: '',
|
||||
lng: '',
|
||||
lat: '',
|
||||
})
|
||||
const deliveryRules = ref({
|
||||
'driverName': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请输入司机姓名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'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']
|
||||
}
|
||||
],
|
||||
'lng': {
|
||||
type: 'number',
|
||||
required: true,
|
||||
message: '请点击获取当前位置',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'SIGN_DRIVER': {
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请司机签名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'SIGN_COURIER': {
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请配送人签名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'SIGN_RECEIVER': {
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请接收人签名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'PHOTO_SITE': {
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请上传现场照片',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'PHOTO_BILL': {
|
||||
type: 'array',
|
||||
required: true,
|
||||
message: '请上传单据照片',
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
})
|
||||
|
||||
const getCurrentLocation = () => {
|
||||
if (formModel.value.lng) {
|
||||
return
|
||||
}
|
||||
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
|
||||
deliveryFormRef.value.validateField("lng")
|
||||
uni.hideLoading()
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 去签名页面
|
||||
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 previewImage = (img) => {
|
||||
let arr = []
|
||||
arr.push(img)
|
||||
uni.previewImage({
|
||||
urls: arr,
|
||||
})
|
||||
}
|
||||
|
||||
// 上传照片
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
const startForm = () => {
|
||||
deliveryFormRef.value.validate().then(res => {
|
||||
let attachments = []
|
||||
if (formModel.value.scene == "ORIGIN") {
|
||||
attachments = [...formModel.value.SIGN_DRIVER, ...formModel.value.SIGN_COURIER, ...formModel.value.PHOTO_SITE, ...formModel.value.PHOTO_BILL]
|
||||
} else {
|
||||
attachments = [...formModel.value.SIGN_RECEIVER, ...formModel.value.PHOTO_SITE]
|
||||
}
|
||||
let params = {
|
||||
...formModel.value,
|
||||
attachments: attachments,
|
||||
}
|
||||
console.log(params)
|
||||
startOrder(params).then(res => {
|
||||
console.log(res)
|
||||
uni.$uv.toast('开始配送')
|
||||
uni.navigateBack({ delta: 1 })
|
||||
})
|
||||
}).catch(errors => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onLoad((options) => {
|
||||
formModel.value.orderNo = options.orderNo
|
||||
formModel.value.scene = options.type
|
||||
formModel.value.billNoCk = options.billNoCk
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
})
|
||||
|
||||
// 组件卸载时移除事件监听器
|
||||
onUnmounted(() => {
|
||||
console.log("协助")
|
||||
uni.$off('getSignImg')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 32rpx;
|
||||
background-color: #F6F8FA;
|
||||
min-height: calc(100vh - 192rpx);
|
||||
padding-bottom: 160rpx;
|
||||
.title {
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
.formContent {
|
||||
background-color: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
.carPickerText{
|
||||
padding: 16rpx 18rpx;
|
||||
padding-left: 20rpx;
|
||||
color: #c0c4cc;
|
||||
border: #dadbde 1px solid;
|
||||
border-radius: 8rpx;
|
||||
width: 100%;
|
||||
}
|
||||
.btn {
|
||||
position: fixed;
|
||||
width: calc(100vw - 64rpx);
|
||||
bottom: 0;
|
||||
left: 32rpx;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
196
pages/index/index.js
Normal file
196
pages/index/index.js
Normal file
@@ -0,0 +1,196 @@
|
||||
function getLocalFilePath(path) {
|
||||
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('file://') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/storage/emulated/0/') === 0) {
|
||||
return path
|
||||
}
|
||||
if (path.indexOf('/') === 0) {
|
||||
var localFilePath = plus.io.convertAbsoluteFileSystem(path)
|
||||
if (localFilePath !== path) {
|
||||
return localFilePath
|
||||
} else {
|
||||
path = path.substr(1)
|
||||
}
|
||||
}
|
||||
return '_www/' + path
|
||||
}
|
||||
|
||||
function dataUrlToBase64(str) {
|
||||
var array = str.split(',')
|
||||
return array[array.length - 1]
|
||||
}
|
||||
|
||||
var index = 0
|
||||
function getNewFileId() {
|
||||
return Date.now() + String(index++)
|
||||
}
|
||||
|
||||
function biggerThan(v1, v2) {
|
||||
var v1Array = v1.split('.')
|
||||
var v2Array = v2.split('.')
|
||||
var update = false
|
||||
for (var index = 0; index < v2Array.length; index++) {
|
||||
var diff = v1Array[index] - v2Array[index]
|
||||
if (diff !== 0) {
|
||||
update = diff > 0
|
||||
break
|
||||
}
|
||||
}
|
||||
return update
|
||||
}
|
||||
|
||||
export function pathToBase64(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof window === 'object' && 'document' in window) {
|
||||
if (typeof FileReader === 'function') {
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr.open('GET', path, true)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.onload = function() {
|
||||
if (this.status === 200) {
|
||||
let fileReader = new FileReader()
|
||||
fileReader.onload = function(e) {
|
||||
resolve(e.target.result)
|
||||
}
|
||||
fileReader.onerror = reject
|
||||
fileReader.readAsDataURL(this.response)
|
||||
}
|
||||
}
|
||||
xhr.onerror = reject
|
||||
xhr.send()
|
||||
return
|
||||
}
|
||||
var canvas = document.createElement('canvas')
|
||||
var c2x = canvas.getContext('2d')
|
||||
var img = new Image
|
||||
img.onload = function() {
|
||||
canvas.width = img.width
|
||||
canvas.height = img.height
|
||||
c2x.drawImage(img, 0, 0)
|
||||
resolve(canvas.toDataURL())
|
||||
canvas.height = canvas.width = 0
|
||||
}
|
||||
img.onerror = reject
|
||||
img.src = path
|
||||
return
|
||||
}
|
||||
if (typeof plus === 'object') {
|
||||
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), function(entry) {
|
||||
entry.file(function(file) {
|
||||
var fileReader = new plus.io.FileReader()
|
||||
fileReader.onload = function(data) {
|
||||
resolve(data.target.result)
|
||||
}
|
||||
fileReader.onerror = function(error) {
|
||||
reject(error)
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}, function(error) {
|
||||
reject(error)
|
||||
})
|
||||
}, function(error) {
|
||||
reject(error)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
|
||||
wx.getFileSystemManager().readFile({
|
||||
filePath: path,
|
||||
encoding: 'base64',
|
||||
success: function(res) {
|
||||
resolve('data:image/png;base64,' + res.data)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
reject(new Error('not support'))
|
||||
})
|
||||
}
|
||||
|
||||
export function base64ToPath(base64) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof window === 'object' && 'document' in window) {
|
||||
base64 = base64.split(',')
|
||||
var type = base64[0].match(/:(.*?);/)[1]
|
||||
var str = atob(base64[1])
|
||||
var n = str.length
|
||||
var array = new Uint8Array(n)
|
||||
while (n--) {
|
||||
array[n] = str.charCodeAt(n)
|
||||
}
|
||||
return resolve((window.URL || window.webkitURL).createObjectURL(new Blob([array], { type: type })))
|
||||
}
|
||||
var extName = base64.split(',')[0].match(/data\:\S+\/(\S+);/)
|
||||
if (extName) {
|
||||
extName = extName[1]
|
||||
} else {
|
||||
reject(new Error('base64 error'))
|
||||
}
|
||||
var fileName = getNewFileId() + '.' + extName
|
||||
if (typeof plus === 'object') {
|
||||
var basePath = '_doc'
|
||||
var dirPath = 'uniapp_temp'
|
||||
var filePath = basePath + '/' + dirPath + '/' + fileName
|
||||
if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
|
||||
plus.io.resolveLocalFileSystemURL(basePath, function(entry) {
|
||||
entry.getDirectory(dirPath, {
|
||||
create: true,
|
||||
exclusive: false,
|
||||
}, function(entry) {
|
||||
entry.getFile(fileName, {
|
||||
create: true,
|
||||
exclusive: false,
|
||||
}, function(entry) {
|
||||
entry.createWriter(function(writer) {
|
||||
writer.onwrite = function() {
|
||||
resolve(filePath)
|
||||
}
|
||||
writer.onerror = reject
|
||||
writer.seek(0)
|
||||
writer.writeAsBinary(dataUrlToBase64(base64))
|
||||
}, reject)
|
||||
}, reject)
|
||||
}, reject)
|
||||
}, reject)
|
||||
return
|
||||
}
|
||||
var bitmap = new plus.nativeObj.Bitmap(fileName)
|
||||
bitmap.loadBase64Data(base64, function() {
|
||||
bitmap.save(filePath, {}, function() {
|
||||
bitmap.clear()
|
||||
resolve(filePath)
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
}, function(error) {
|
||||
bitmap.clear()
|
||||
reject(error)
|
||||
})
|
||||
return
|
||||
}
|
||||
if (typeof wx === 'object' && wx.canIUse('getFileSystemManager')) {
|
||||
var filePath = wx.env.USER_DATA_PATH + '/' + fileName
|
||||
wx.getFileSystemManager().writeFile({
|
||||
filePath: filePath,
|
||||
data: dataUrlToBase64(base64),
|
||||
encoding: 'base64',
|
||||
success: function() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
reject(new Error('not support'))
|
||||
})
|
||||
}
|
||||
285
pages/index/index.vue
Normal file
285
pages/index/index.vue
Normal file
@@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<z-paging ref="pagingRef" class="containerBox" v-model="listData" :default-page-size="queryParams.pageSize" @query="queryList">
|
||||
<template #top>
|
||||
<view style="padding: 20rpx 32rpx 0;">
|
||||
<!-- 项目号/项目描述/物料号/物料描述/供应商编码/供应商名称/订单编号 -->
|
||||
<uv-search v-model="queryParams.keyword" placeholder="请输入" :showAction="true" actionText="搜索" @search="searchList" @custom="searchList"></uv-search>
|
||||
<uv-tabs :list="list" @click="click" lineColor="#199793"></uv-tabs>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
<view class="box">
|
||||
<view v-for="item in listData" class="item">
|
||||
<view class="title">配送号:{{ item.orderNo }}</view>
|
||||
<view><text>发货人:</text>{{ item.shipperName }}</view>
|
||||
<view><text>发货地:</text>{{ item.originName }}</view>
|
||||
<view><text>接收人:</text>{{ item.receiverName }}</view>
|
||||
<view style="display: flex; align-items: center;">
|
||||
<view><text>目的地:</text>{{ item.destName }}</view>
|
||||
<image :src="openMap" mode="scaleToFill" v-show="item.orderStatus == 2" style="width: 30rpx; height: 30rpx;margin-left: 20rpx;" @tap="toMap(item, 'location')" />
|
||||
</view>
|
||||
<view><text>货物数量:</text>{{ item.items.length }}</view>
|
||||
<view><text>配送日期:</text>{{ item.deliveryDate }}</view>
|
||||
<view class="statusBtn">
|
||||
<!-- <uv-button :plain="true" shape="circle" text="查看详情" @tap="goDetail(item)" style="margin-right: 20rpx;"></uv-button> -->
|
||||
<uv-button type="primary" shape="circle" v-show="item.orderStatus == 1" text="开始配送" @tap="changeOrder(item, 'ORIGIN')"></uv-button>
|
||||
<uv-button type="primary" shape="circle" v-show="item.orderStatus == 2" text="完成配送" @tap="changeOrder(item, 'DEST')"></uv-button>
|
||||
<uv-button type="primary" shape="circle" v-show="item.orderStatus == 3" text="查看单据" style="margin-right: 20rpx;" @tap="changeOrder(item, 'COMPLETE')"></uv-button>
|
||||
<!-- <uv-button type="primary" shape="circle" v-show="item.orderStatus == 1" text="开始配送" @tap="change1(item, 'ORIGIN')"></uv-button>
|
||||
<uv-button type="primary" shape="circle" v-show="item.orderStatus == 2" text="完成配送" @tap="change1(item, 'DEST')"></uv-button> -->
|
||||
<uv-button type="primary" v-show="item.orderStatus == 3" :plain="true" shape="circle" text="历史轨迹" @tap="toMap(item, 'history')"></uv-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
|
||||
<view class="btn">
|
||||
<uv-button type="primary" text="确定" size="large" style="width: 100%;" class="mainBtn" @tap="handleAdd">新 增 配 送 任 务</uv-button>
|
||||
</view>
|
||||
|
||||
<uv-modal ref="modalRef" title="单据明细" class="modalInfo" confirmText="关闭" confirmColor="#606266"
|
||||
:closeOnClickOverlay="false">
|
||||
<view class="box">
|
||||
<view v-for="items in infoList" class="item">
|
||||
<view><text>订单编号:</text>{{ items.sapNo }}</view>
|
||||
<view><text>项目描述:</text>{{ items.xmMs }}</view>
|
||||
<view><text>项目号:</text>{{ items.xmNo }}</view>
|
||||
<view><text>物料号:</text>{{ items.wlNo }}</view>
|
||||
<view><text>物料描述:</text>{{ items.wlMs }}</view>
|
||||
<view><text>供应商名称:</text>{{ items.gysMc }}</view>
|
||||
<view><text>存放位置:</text>{{ items.pcode || "-" }}</view>
|
||||
<view><text>身份码:</text>{{ items.entityId || "-" }}</view>
|
||||
<view><text>备注:</text>{{ items.remark || '-' }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</uv-modal>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import { onLoad, onShow, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { getOrder, getOrderDetail } from "@/api/index"
|
||||
import openMap from "@/static/openMap.png"
|
||||
|
||||
|
||||
|
||||
const list = ref([{
|
||||
name: '待接单',
|
||||
value: '0',
|
||||
}, {
|
||||
name: '待配送',
|
||||
value: '1',
|
||||
}, {
|
||||
name: '配送中',
|
||||
value: '2',
|
||||
}, {
|
||||
name: '已完成',
|
||||
value: '3',
|
||||
}])
|
||||
const click = (item) => {
|
||||
queryParams.value.orderStatus = item.value
|
||||
pagingRef.value.reload()
|
||||
console.log('item', item);
|
||||
}
|
||||
const queryParams = ref({
|
||||
// keyword: "",
|
||||
orderStatus: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
})
|
||||
const pagingRef = ref(null)
|
||||
const listData = ref([])
|
||||
// 获取列表
|
||||
const queryList = (pageNo, pageSize) => {
|
||||
queryParams.value.pageNum = pageNo
|
||||
console.log(pageNo, pageSize, queryParams.value)
|
||||
getOrder(queryParams.value).then(res => {
|
||||
console.log(res.rows)
|
||||
pagingRef.value.complete(res.rows)
|
||||
}).catch(res => {
|
||||
pagingRef.value.complete(false)
|
||||
})
|
||||
}
|
||||
const searchList = () => {
|
||||
pagingRef.value.reload()
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
pagingRef.value?.reload()
|
||||
})
|
||||
|
||||
|
||||
// 弹窗
|
||||
const modalRef = ref()
|
||||
const infoList = ref([])
|
||||
const goDetail = (item) => {
|
||||
if (item.infoList && item.infoList.length > 0) {
|
||||
infoList.value = item.infoList
|
||||
modalRef.value.open()
|
||||
return
|
||||
}
|
||||
getOrderDetail({ orderNo: item.orderNo }).then(res => {
|
||||
infoList.value = res.data
|
||||
item.infoList = res.data
|
||||
modalRef.value.open()
|
||||
})
|
||||
}
|
||||
|
||||
// 跳转地图
|
||||
const toMap = (item, type) => {
|
||||
if (type === 'location') {
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/map?orderNo=" + item.orderNo + "&plateNo=" + item.plateNo + "&type=" + type
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/map?orderNo=" + item.orderNo + "&plateNo=" + item.plateNo + "&type=" + type
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const changeOrder = (item, type) => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/deliveryBill?type=" + type + "&orderNo=" + item.orderNo + "&billNoCk=" + item.billNoCk
|
||||
})
|
||||
}
|
||||
|
||||
const change1 = (item, type) => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/editDelivery?type=" + type + "&orderNo=" + item.orderNo + "&billNoCk=" + item.billNoCk
|
||||
})
|
||||
}
|
||||
|
||||
const handleAdd = (item) => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/addDelivery"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
position: relative;
|
||||
.containerBox{
|
||||
// padding: 32rpx;
|
||||
padding-bottom: 120rpx;
|
||||
min-height: calc(100vh - 196rpx - 120rpx);
|
||||
|
||||
::v-deep .uv-tabs__wrapper__nav{
|
||||
width: 100%;
|
||||
}
|
||||
::v-deep .uv-tabs__wrapper__nav__item{
|
||||
flex: 1;
|
||||
}
|
||||
.dropDown{
|
||||
padding: 0 30rpx;
|
||||
border-bottom: 1px solid #dadbde;
|
||||
.item_dropDown{
|
||||
padding: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
::v-deep .uv-text{
|
||||
width: unset !important;
|
||||
flex: unset !important;
|
||||
}
|
||||
::v-deep .item_text{
|
||||
max-width: 200rpx;
|
||||
margin-right: 10rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .uv-search {
|
||||
flex: unset;
|
||||
}
|
||||
::v-deep .zp-scroll-view-super {
|
||||
background-color: #F3F4F8;
|
||||
}
|
||||
.box{
|
||||
// background-color: #ebebeb;
|
||||
// background-color: #f5f5f5;
|
||||
// padding-top: 20rpx;
|
||||
background-color: #F3F4F8;
|
||||
padding: 0 32rpx;
|
||||
box-sizing: border-box;
|
||||
padding-top: 20rpx;
|
||||
.item {
|
||||
background: white;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #333;
|
||||
position: relative;
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
text{
|
||||
color: #666;
|
||||
}
|
||||
.statusBtn {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid #c7c7c7;
|
||||
padding-top: 10rpx;
|
||||
::v-deep .uv-button{
|
||||
height: 60rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
::v-deep .uv-input__content{
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.btn {
|
||||
position: fixed;
|
||||
width: calc(100vw - 64rpx);
|
||||
bottom: 0;
|
||||
left: 32rpx;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.modalInfo {
|
||||
.box {
|
||||
width: 100%;
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
.item {
|
||||
background: #efefef;
|
||||
padding: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
line-height: 50rpx;
|
||||
font-size: 28rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #333;
|
||||
border: unset;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .zp-paging-container-content{
|
||||
background-color: #F3F4F8;
|
||||
}
|
||||
|
||||
</style>
|
||||
439
pages/index/map.vue
Normal file
439
pages/index/map.vue
Normal file
@@ -0,0 +1,439 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- {{ polyline[0]?.points.length }} - {{ pointArr.length }} -->
|
||||
<!-- <button @tap="startWatch()">{{ polyline[0].points.length }}开始监听</button>
|
||||
<button @click="fileWriter">存储</button> -->
|
||||
<!-- 地图容器 -->
|
||||
<view class="map-container">
|
||||
<map id="amap" ref="mapRef" :latitude="latitude" :longitude="longitude" :scale="scale" :polyline="polyline"
|
||||
:markers="markers" style="width: 100%; height: 100%;" />
|
||||
<!-- <cover-image class="center-img" :src="center" v-show="reportParams.type == 'location'"></cover-image> -->
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { onLoad, onUnload, onReady } from '@dcloudio/uni-app';
|
||||
import center from "../../static/mark.png";
|
||||
import { locationReport, historyLocation } from '@/api/index'
|
||||
|
||||
// 地图相关变量
|
||||
const mapRef = ref(null);
|
||||
const latitude = ref(null); // 默认北京纬度
|
||||
const longitude = ref(null); // 默认北京经度
|
||||
const scale = ref(16)
|
||||
// const scale = ref(20)
|
||||
let reportParams = ref({})
|
||||
const polyline = ref([
|
||||
{
|
||||
points: [],
|
||||
color: '#007AFF', // 高德蓝,更醒目
|
||||
width: 15, // 适当加粗
|
||||
}
|
||||
]);
|
||||
|
||||
|
||||
const markers = ref([])
|
||||
let mapContext = null;
|
||||
|
||||
onLoad((options) => {
|
||||
reportParams.value = options
|
||||
uni.setKeepScreenOn({
|
||||
keepScreenOn: true
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
onReady(() => {
|
||||
if (reportParams.value.type == 'location') {
|
||||
startWatch()
|
||||
} else if (reportParams.value.type == 'history') {
|
||||
historyLocation(reportParams.value).then(async (res) => {
|
||||
mapContext = uni.createMapContext('amap');
|
||||
let arr = []
|
||||
res.data.forEach(item => {
|
||||
arr.push({
|
||||
latitude: item.lat,
|
||||
longitude: item.lng
|
||||
})
|
||||
})
|
||||
polyline.value[0].points = JSON.parse(JSON.stringify(arr))
|
||||
console.log("!!!!!",polyline)
|
||||
// const centerPoint = await getCenterPoint(JSON.parse(JSON.stringify(arr)))
|
||||
// console.log('centerPoint', centerPoint)
|
||||
|
||||
// const autoScale = await calculateScaleByPoints(JSON.parse(JSON.stringify(arr))); // 调用计算方法
|
||||
// scale.value = autoScale.scale; // 替换固定的scale:20
|
||||
mapContext.moveToLocation({
|
||||
latitude: polyline.value[0].points[0].latitude,
|
||||
longitude: polyline.value[0].points[0].longitude
|
||||
});
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const fileWriter = () => {
|
||||
// 请求本地系统文件对象 plus.io.PRIVATE_WWW:应用运行资源目录常量
|
||||
plus.io.requestFileSystem(plus.io.PRIVATE_DOC, (fobject) => {
|
||||
// fs.root是根目录操作对象DirectoryEntry
|
||||
fobject.root.getFile(formatTime(new Date()) + '.txt', {
|
||||
create: true
|
||||
}, (fileEntry) => {
|
||||
fileEntry.file((file) => {
|
||||
console.log('file :>> ', file)
|
||||
// create a FileWriter to write to the file
|
||||
fileEntry.createWriter((writer) => {
|
||||
console.log('writer :>> ', writer)
|
||||
// Write data to file.
|
||||
// 返回页面的数据
|
||||
writer.seek(file.size)
|
||||
// 写入可以加入需要写入的数据
|
||||
let content = "{points:" + JSON.stringify(polyline.value[0].points) + ",pointArr:" + JSON.stringify(pointArr.value) + "}"
|
||||
writer.write(content)
|
||||
const fileReader = new plus.io.FileReader()
|
||||
fileReader.readAsText(file, 'utf-8')
|
||||
fileReader.onloadend = (evt) => {
|
||||
console.log('evt :>> ', evt)
|
||||
}
|
||||
console.log('file info :>> ', file)
|
||||
|
||||
// 获取对象
|
||||
plus.io.resolveLocalFileSystemURL(file.fullPath, (res) => {
|
||||
res.file((localFile) => {
|
||||
const localFilePath = localFile.fullPath; // 本地文件路径
|
||||
console.log("localFilePath :>> ", localFilePath)
|
||||
// 直接保存并打开本地文件
|
||||
uni.openDocument({
|
||||
filePath: localFilePath,
|
||||
success: () => console.log('打开成功')
|
||||
});
|
||||
});
|
||||
})
|
||||
}, (e) => {
|
||||
console.log('e :>> ', e)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 1. 计算中心点(经纬度平均值)
|
||||
// const getCenterPoint = (points) => {
|
||||
// const sumLng = points.reduce((sum, p) => sum + p.longitude, 0);
|
||||
// const sumLat = points.reduce((sum, p) => sum + p.latitude, 0);
|
||||
// return {
|
||||
// longitude: sumLng / points.length,
|
||||
// latitude: sumLat / points.length
|
||||
// };
|
||||
// };
|
||||
|
||||
// const calculateScaleByPoints = async (points) => {
|
||||
// if (points.length <= 1) return 20; // 单点默认缩放18
|
||||
|
||||
// // 1. 获取经纬度极值
|
||||
// const extremes = getLatLngExtremes(points);
|
||||
// const centerLat = (extremes.minLat + extremes.maxLat) / 2; // 轨迹中心纬度
|
||||
// // 2. 计算范围宽度(米)
|
||||
// const rangeWidth = getRangeWidth(extremes);
|
||||
// // 3. 获取地图容器宽度(像素)
|
||||
// const containerWidth = await getMapContainerWidth();
|
||||
|
||||
// // 4. 目标:让范围宽度占容器80%(留边距,避免轨迹贴边)
|
||||
// const targetMeterPerPixel = rangeWidth / (containerWidth * 0.9);
|
||||
|
||||
// const scale = calculatePreciseScale(targetMeterPerPixel, centerLat);
|
||||
|
||||
// return { centerLat, scale };
|
||||
// };
|
||||
|
||||
|
||||
// const getLatLngExtremes = (points) => {
|
||||
// if (!points.length) return null;
|
||||
|
||||
// const lats = points.map(p => p.latitude);
|
||||
// const lngs = points.map(p => p.longitude);
|
||||
|
||||
// return {
|
||||
// minLat: Math.min(...lats), // 最小纬度
|
||||
// maxLat: Math.max(...lats), // 最大纬度
|
||||
// minLng: Math.min(...lngs), // 最小经度
|
||||
// maxLng: Math.max(...lngs), // 最大经度
|
||||
// };
|
||||
// };
|
||||
|
||||
|
||||
// // 计算范围的东西向实际宽度(米)
|
||||
// const getRangeWidth = (extremes) => {
|
||||
// // 取纬度中点(减少纬度对经度距离计算的影响)
|
||||
// const centerLat = (extremes.minLat + extremes.maxLat) / 2;
|
||||
// // 计算同一纬度下,最小和最大经度的距离(米)
|
||||
// return haversineDistance(centerLat, extremes.minLng, centerLat, extremes.maxLng);
|
||||
// };
|
||||
// // 2. Haversine公式:计算两点经纬度的球面距离(单位:米)
|
||||
// const haversineDistance = (lat1, lon1, lat2, lon2) => {
|
||||
// const R = 6371000; // 地球半径(米)
|
||||
// const φ1 = (lat1 * Math.PI) / 180;
|
||||
// const φ2 = (lat2 * Math.PI) / 180;
|
||||
// const Δφ = ((lat2 - lat1) * Math.PI) / 180;
|
||||
// const Δλ = ((lon2 - lon1) * Math.PI) / 180;
|
||||
|
||||
// const a = Math.sin(Δφ / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin(Δλ / 2) ** 2;
|
||||
// const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
// return R * c;
|
||||
// };
|
||||
|
||||
|
||||
// // 获取地图容器宽度(像素)- 适配uni-app
|
||||
// const getMapContainerWidth = () => {
|
||||
// // 获取容器节点信息
|
||||
// return new Promise((resolve) => {
|
||||
// uni.createSelectorQuery().in(getCurrentInstance())
|
||||
// .select('.map-container')
|
||||
// .boundingClientRect(rect => {
|
||||
// resolve(rect.width); // 返回容器宽度(像素)
|
||||
// })
|
||||
// .exec();
|
||||
// });
|
||||
// };
|
||||
|
||||
// const calculatePreciseScale = (targetMeterPerPixel, centerLat) => {
|
||||
// const earthRadius = 6378137; // 地球半径(高德用的WGS84坐标系,半径6378137米)
|
||||
// const centerLatRad = (centerLat * Math.PI) / 180; // 中心纬度转弧度
|
||||
|
||||
// // 高德官方公式:米/像素 = (2 * π * R * cos(lat)) / (2^(scale + 8))
|
||||
// // 反向推导:scale = log2( (2 * π * R * cos(lat)) / 米/像素 ) - 8
|
||||
// const numerator = 2 * Math.PI * earthRadius * Math.cos(centerLatRad);
|
||||
// const scaleLog = Math.log2(numerator / targetMeterPerPixel);
|
||||
// let scale = scaleLog - 8;
|
||||
|
||||
// // scale必须是整数(地图scale只支持整数),同时限制范围(10-20,避免过大/过小)
|
||||
// scale = Math.round(scale);
|
||||
// console.log(scale, "scale")
|
||||
// return Math.max(10, Math.min(20, scale));
|
||||
// };
|
||||
|
||||
|
||||
function formatTime(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 补 0 为两位数(如 09)
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hour = String(date.getHours()).padStart(2, '0');
|
||||
const minute = String(date.getMinutes()).padStart(2, '0');
|
||||
const second = String(date.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let pointArr = ref([
|
||||
// { longitude: 116.478935, latitude: 39.997761 },
|
||||
// { longitude: 116.478939, latitude: 39.997825 },
|
||||
// // {longitude:116.478912,latitude: 39.998549},
|
||||
// { longitude: 116.478912, latitude: 39.998549 },
|
||||
// // {longitude:116.478998,latitude: 39.998555},
|
||||
// { longitude: 116.478998, latitude: 39.998555 },
|
||||
// { longitude: 116.479282, latitude: 39.99856 },
|
||||
// { longitude: 116.479658, latitude: 39.998528 },
|
||||
// { longitude: 116.480151, latitude: 39.998453 },
|
||||
// // {longitude:116.480784,latitude: 39.998302},
|
||||
// { longitude: 116.480784, latitude: 39.998302 },
|
||||
// { longitude: 116.481149, latitude: 39.998184 },
|
||||
// { longitude: 116.481573, latitude: 39.997997 },
|
||||
// { longitude: 116.481863, latitude: 39.997846 },
|
||||
// { longitude:116.482072,latitude: 39.997718 },
|
||||
// { longitude: 116.482362, latitude: 39.997718 },
|
||||
// { longitude: 116.483633, latitude: 39.998935 },
|
||||
// { longitude: 116.48367, latitude: 39.998968 },
|
||||
// { longitude: 116.484648, latitude: 39.999861 }
|
||||
])
|
||||
|
||||
|
||||
// 检查并请求定位权限
|
||||
const requestLocationPermission = async () => {
|
||||
if (plus.os.name === "Android") {
|
||||
// 检查后台定位权限(Android 10+)
|
||||
const hasBgPermission = await plus.android.requestPermissions(
|
||||
["android.permission.ACCESS_BACKGROUND_LOCATION"]
|
||||
);
|
||||
// if (!hasBgPermission.granted) {
|
||||
// uni.showToast({ title: "请授予后台定位权限", icon: "none" });
|
||||
// return false;
|
||||
// }
|
||||
} else if (plus.os.name === "iOS") {
|
||||
// iOS 需引导用户开启"始终允许"定位
|
||||
const authStatus = await uni.getLocationAuthStatus();
|
||||
if (authStatus.authStatus !== 3) { // 3 表示"始终允许"
|
||||
uni.showModal({
|
||||
title: "提示",
|
||||
content: "请在设置中开启始终允许定位,否则锁屏后无法跟踪",
|
||||
success: () => {
|
||||
plus.runtime.openURL("app-settings:"); // 打开应用设置页
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
let locationWatchId = null
|
||||
// 初始化地图
|
||||
const startWatch = (async () => {
|
||||
// 先请求权限
|
||||
const hasPermission = await requestLocationPermission();
|
||||
if (!hasPermission) return;
|
||||
|
||||
|
||||
// 获取地图上下文
|
||||
mapContext = uni.createMapContext('amap');
|
||||
|
||||
try {
|
||||
// 直接在组件中实现定位功能,不再依赖外部工具类
|
||||
|
||||
uni.startLocationUpdate({
|
||||
type: 'gcj02',
|
||||
// needFullAccuracy: true,
|
||||
success: res => {
|
||||
console.log('开启应用接收位置消息成功')
|
||||
locationWatchId = uni.onLocationChange(function (res) {
|
||||
|
||||
let time = formatTime(new Date())
|
||||
// 检查是否已有最后一个点,且与当前点经纬度相同
|
||||
const points = polyline.value[0].points;
|
||||
|
||||
res.time = time
|
||||
// console.log('points', formatTime(new Date()));
|
||||
let hasSameLastPoint = false
|
||||
if (points.length) {
|
||||
hasSameLastPoint = points[points.length - 1].latitude === res.latitude && points[points.length - 1].longitude === res.longitude;
|
||||
}
|
||||
if (markers.value.length == 0) {
|
||||
markers.value = [{
|
||||
id: 1,
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
iconPath: center,
|
||||
width: 10, // 小车图标宽度(rpx/px,根据需求调整)
|
||||
height: 10, // 小车图标高度
|
||||
}]
|
||||
} else {
|
||||
markers.value[0].latitude = res.latitude
|
||||
markers.value[0].longitude = res.longitude
|
||||
}
|
||||
// console.log('hasSameLastPoint', hasSameLastPoint);
|
||||
if (!hasSameLastPoint) {
|
||||
polyline.value[0].points.push({
|
||||
latitude: res.latitude,
|
||||
longitude: res.longitude,
|
||||
accuracy: res.accuracy,
|
||||
speed: res.speed,
|
||||
altitude: res.altitude,
|
||||
time: time
|
||||
});
|
||||
console.log(polyline.value[0].points)
|
||||
latitude.value = res.latitude;
|
||||
longitude.value = res.longitude;
|
||||
mapContext.moveToLocation({
|
||||
latitude: latitude.value,
|
||||
longitude: longitude.value
|
||||
});
|
||||
reportParams.value.lat = res.latitude
|
||||
reportParams.value.lng = res.longitude
|
||||
// console.log('reportParams', reportParams.value);
|
||||
locationReport(reportParams.value).then((res) => {
|
||||
console.log("页面", res)
|
||||
if (res.code == 401) {
|
||||
console.log("停止")
|
||||
stopLocationUpdate();
|
||||
}
|
||||
})
|
||||
// console.log('纬度:' + res.latitude);
|
||||
// console.log('经度:' + res.longitude);
|
||||
}
|
||||
// pointArr.value.push(res);
|
||||
// console.log(pointArr.value)
|
||||
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
fail: err => {
|
||||
console.error('开启应用接收位置消息失败:', err)
|
||||
},
|
||||
complete: msg => console.log('调用开启应用接收位置消息 API 完成')
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取位置失败:', error);
|
||||
uni.showToast({
|
||||
title: '获取位置失败,使用默认位置',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 页面卸载时清理资源
|
||||
onUnmounted(() => {
|
||||
stopLocationUpdate();
|
||||
});
|
||||
onUnload(() => {
|
||||
stopLocationUpdate();
|
||||
});
|
||||
const stopLocationUpdate = () => {
|
||||
if (locationWatchId) {
|
||||
uni.offLocationChange();
|
||||
locationWatchId = null;
|
||||
}
|
||||
uni.stopLocationUpdate({
|
||||
success: () => console.log('已停止位置更新'),
|
||||
fail: (err) => console.error('停止位置更新失败:', err)
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.map-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.center-img {
|
||||
height: 80rpx;
|
||||
left: 50%;
|
||||
margin-left: -40rpx;
|
||||
margin-top: -88rpx;
|
||||
top: 50%;
|
||||
width: 80rpx;
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.position-img {
|
||||
bottom: 32rpx;
|
||||
height: 66rpx;
|
||||
right: 16rpx;
|
||||
width: 66rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
z-index: 9999;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
283
pages/index/signature.vue
Normal file
283
pages/index/signature.vue
Normal file
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<view class="box">
|
||||
<canvas v-if="showCanvas" id="canvas" canvas-id="canvas" style="width: 100vw;background-color: green;" :style="`height:${canvasHeight}px`" @touchstart="startDraw" @touchmove="drawing" @@touchend="endDraw"></canvas>
|
||||
|
||||
<cover-view class="tipTxt">请在空白处横屏签名</cover-view>
|
||||
|
||||
<view class="btn">
|
||||
<uv-button size="large" style="width: 50%;" @tap="clearSign">重 签</uv-button>
|
||||
<uv-button type="primary" size="large" class="mainBtn" style="width: 50%;" @tap="confirm">完 成</uv-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onReady, onUnload, onLoad } from '@dcloudio/uni-app';
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { pathToBase64, base64ToPath } from './index.js'
|
||||
import { uploadDeliveryAttachment } from "@/api/index"
|
||||
|
||||
const btnHeight = ref(null)
|
||||
const BoxHeight = ref(null)
|
||||
const canvasWidth = ref(null)
|
||||
const canvasHeight = ref(null)
|
||||
const showCanvas = ref(false)
|
||||
|
||||
const signFlag = ref(false)
|
||||
|
||||
const getBtnHeight = () => {
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
// 选择要查询的元素,使用元素ID或类名
|
||||
query.select('.btn').boundingClientRect(data => {
|
||||
btnHeight.value = data.height
|
||||
}).select('.box').boundingClientRect(res => {
|
||||
// console.log('元素高度:', res);
|
||||
// BoxHeight.value = res.height
|
||||
// canvasWidth.value = res.width
|
||||
}).exec(() => {
|
||||
// 确保两个查询都完成后再初始化画布
|
||||
if (btnHeight.value && BoxHeight.value && canvasWidth.value) {
|
||||
console.log("获取",BoxHeight.value,btnHeight.value);
|
||||
|
||||
canvasHeight.value = BoxHeight.value - btnHeight.value
|
||||
showCanvas.value = true
|
||||
// 等待DOM更新完成后再初始化Canvas
|
||||
nextTick(() => {
|
||||
initCanvas();
|
||||
});
|
||||
} else {
|
||||
console.error('获取元素尺寸失败,无法初始化画布');
|
||||
}
|
||||
}); // 执行查询
|
||||
}
|
||||
|
||||
let canvasContext = null
|
||||
const initCanvas = () => {
|
||||
console.log(canvasWidth.value, canvasHeight.value)
|
||||
canvasContext = uni.createCanvasContext('canvas');
|
||||
canvasContext.setFillStyle('#F5F5F5'); // 设置填充颜色为红色
|
||||
canvasContext.fillRect(0, 0, canvasWidth.value, canvasHeight.value); // 填充整个画板
|
||||
canvasContext.draw(true)
|
||||
}
|
||||
|
||||
const startX = ref(null)
|
||||
const startY = ref(null)
|
||||
const startDraw = (e) => {
|
||||
startX.value = e.touches[0].x
|
||||
startY.value = e.touches[0].y
|
||||
canvasContext.beginPath(); // 开始创建一个路径
|
||||
signFlag.value = true
|
||||
}
|
||||
const endDraw = (e) => {
|
||||
startX.value = 0
|
||||
startY.value = 0
|
||||
}
|
||||
|
||||
const drawing = (e) => {
|
||||
let temX = e.touches[0].x;
|
||||
let temY = e.touches[0].y;
|
||||
canvasContext.moveTo(startX.value, startY.value)
|
||||
canvasContext.lineTo(temX, temY)
|
||||
canvasContext.setStrokeStyle('#000') // 文字颜色
|
||||
canvasContext.setLineWidth(4) // 文字宽度
|
||||
canvasContext.setLineCap('round') // 文字圆角
|
||||
canvasContext.stroke() // 文字绘制
|
||||
startX.value = temX
|
||||
startY.value = temY
|
||||
canvasContext.draw(true) // 文字绘制到画布中
|
||||
}
|
||||
|
||||
const clearSign = () => {
|
||||
canvasContext.setFillStyle('#F5F5F5'); // 设置填充颜色为红色
|
||||
canvasContext.fillRect(0, 0, canvasWidth.value, canvasHeight.value); // 填充整个画板
|
||||
canvasContext.draw(true) // 文字绘制到画布中
|
||||
signFlag.value = false
|
||||
}
|
||||
|
||||
const confirm = () => {
|
||||
// 确认按钮事件
|
||||
if (!signFlag.value) {
|
||||
uni.showToast({
|
||||
title: '请签名后再点击确定',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '确认',
|
||||
content: '确认签名无误吗',
|
||||
showCancel: true,
|
||||
success: async ({ confirm }) => {
|
||||
if (confirm) {
|
||||
let tempFile
|
||||
// if (this.horizontal) {
|
||||
tempFile = await saveHorizontalCanvas()
|
||||
// } else {
|
||||
// tempFile = await this.saveCanvas()
|
||||
// }
|
||||
console.log(tempFile, "tempFile")
|
||||
const base64 = await pathToBase64(tempFile)
|
||||
const path = await base64ToPath(base64)
|
||||
console.log(path, "path")
|
||||
const res = await uploadDeliveryAttachment(path, uploadParams.value);
|
||||
uni.navigateBack();
|
||||
uni.$emit('getSignImg', { base64, path, url: res.data });
|
||||
// uploadImg(path, {}).then(res=>{
|
||||
// console.log(base64, path, res.url, "打印")
|
||||
// let url = res.url
|
||||
// uni.$emit('getSignImg', { base64, path, url })
|
||||
// uni.navigateBack()
|
||||
// })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const saveHorizontalCanvas = async () => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
uni.canvasToTempFilePath(
|
||||
{
|
||||
canvasId: 'canvas',
|
||||
fileType: "png", // 只支持png和jpg
|
||||
success: (res) => {
|
||||
if (!res.tempFilePath) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '保存签名失败',
|
||||
showCancel: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// #ifdef APP
|
||||
uni.compressImage({
|
||||
src: res.tempFilePath,
|
||||
quality: 100, // 范围 0 - 100
|
||||
rotate: 270,
|
||||
success: (r) => {
|
||||
console.log('==== compressImage :', r)
|
||||
resolve(r.tempFilePath)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef APP
|
||||
// uni.getImageInfo({
|
||||
// src: res.tempFilePath,
|
||||
// success: (r) => {
|
||||
// // console.log('==== getImageInfo :', r)
|
||||
// // 将signCanvas的内容复制到hsignCanvas中
|
||||
// const hcanvasCtx = uni.createCanvasContext('hsignCanvas', this)
|
||||
// // 横屏宽高互换
|
||||
// hcanvasCtx.translate(this.canvasHeight / 2, this.canvasWidth / 2)
|
||||
// hcanvasCtx.rotate(Math.PI * (-90 / 180))
|
||||
// hcanvasCtx.drawImage(
|
||||
// r.path,
|
||||
// -this.canvasWidth / 2,
|
||||
// -this.canvasHeight / 2,
|
||||
// this.canvasWidth,
|
||||
// this.canvasHeight
|
||||
// )
|
||||
// hcanvasCtx.draw(false, async () => {
|
||||
// const hpathRes = await uni.canvasToTempFilePath(
|
||||
// {
|
||||
// canvasId: 'hsignCanvas',
|
||||
// fileType: this.expFile.fileType, // 只支持png和jpg
|
||||
// quality: this.expFile.quality // 范围 0 - 1
|
||||
// },
|
||||
// this
|
||||
// )
|
||||
// let tempFile = ''
|
||||
// if (Array.isArray(hpathRes)) {
|
||||
// hpathRes.some((item) => {
|
||||
// if (item) {
|
||||
// tempFile = item.tempFilePath
|
||||
// return
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// tempFile = hpathRes.tempFilePath
|
||||
// }
|
||||
// resolve(tempFile)
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// #endif
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('图片生成失败:' + err)
|
||||
resolve(false)
|
||||
}
|
||||
},
|
||||
this
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
onReady(() => {
|
||||
// uni.showLoading({
|
||||
// title: '正在加载,请稍后...',
|
||||
// mask: true
|
||||
// })
|
||||
// 页面加载完成后设置横屏
|
||||
// plus.screen.lockOrientation('landscape-primary')
|
||||
// nextTick(() => {
|
||||
// getBtnHeight();
|
||||
// });
|
||||
// setTimeout(() => {
|
||||
// uni.hideLoading();
|
||||
uni.getSystemInfo({
|
||||
success: function (res) {
|
||||
console.log(res)
|
||||
BoxHeight.value = res.windowHeight
|
||||
canvasWidth.value = res.windowWidth
|
||||
}
|
||||
});
|
||||
getBtnHeight();
|
||||
// }, 1500);
|
||||
// nextTick(() => {
|
||||
// getBtnHeight();
|
||||
// });
|
||||
})
|
||||
|
||||
const uploadParams = ref({})
|
||||
onLoad((options) => {
|
||||
uploadParams.value = options
|
||||
})
|
||||
|
||||
|
||||
onUnload(() => {
|
||||
// 页面卸载时恢复竖屏
|
||||
// plus.screen.lockOrientation('portrait-primary');
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.box {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
|
||||
.tipTxt{
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
left: 10rpx;
|
||||
color: #999;
|
||||
font-size: 44rpx;
|
||||
}
|
||||
|
||||
.btn {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
128
pages/login/login.vue
Normal file
128
pages/login/login.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<view class="container">
|
||||
<view class="huanying">HELLO,</view>
|
||||
<view class="huanyingText">
|
||||
欢迎登录
|
||||
<text>一公里配送</text>
|
||||
</view>
|
||||
<view class="huanyingDes">
|
||||
近距离、快响应,即时补给线
|
||||
</view>
|
||||
<uv-form labelPosition="left" :model="formModel" labelWidth="80" :rules="rules" class="formContent" ref="formRef">
|
||||
<uv-form-item label="用户名" prop="userInfo.username" borderBottom>
|
||||
<uv-input v-model="formModel.userInfo.username" border="none" placeholder="请输入用户名" />
|
||||
</uv-form-item>
|
||||
<uv-form-item label="密码" prop="userInfo.password" borderBottom>
|
||||
<uv-input v-model="formModel.userInfo.password" :type="showPass ? '' : 'password'" border="none" placeholder="请输入密码" />
|
||||
<template v-slot:right>
|
||||
<uv-icon name="eye" @tap="changePassStatus" v-show="!showPass"></uv-icon>
|
||||
<uv-icon name="eye-off-outline" @tap="changePassStatus" v-show="showPass"></uv-icon>
|
||||
</template>
|
||||
</uv-form-item>
|
||||
<uv-button type="primary" size="large" text="登 录" shape="circle" color="#199793" customStyle="margin-top: 50px" @click="submit"></uv-button>
|
||||
</uv-form>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { userLogin } from "@/api/login"
|
||||
|
||||
const formModel = ref({
|
||||
userInfo: {
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
})
|
||||
|
||||
const showPass = ref(false)
|
||||
const formRef = ref()
|
||||
const rules = ({
|
||||
'userInfo.username': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '输入用户名',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
'userInfo.password': {
|
||||
type: 'string',
|
||||
required: true,
|
||||
message: '请填写密码',
|
||||
trigger: ['blur', 'change']
|
||||
},
|
||||
})
|
||||
|
||||
const changePassStatus = () => {
|
||||
showPass.value = !showPass.value
|
||||
}
|
||||
// 提交
|
||||
const submit = () => {
|
||||
// 如果有错误,会在catch中返回报错信息数组,校验通过则在then中返回true
|
||||
formRef.value.validate().then(res => {
|
||||
userLogin(formModel.value.userInfo).then(res => {
|
||||
console.log("登录成功", res)
|
||||
uni.setStorageSync('token', res.data.token)
|
||||
uni.setStorageSync('user', res.data.user)
|
||||
uni.navigateTo({
|
||||
url: "/pages/index/index"
|
||||
})
|
||||
})
|
||||
}).catch(errors => {
|
||||
// uni.showToast({
|
||||
// icon: 'error',
|
||||
// title: '校验失败'
|
||||
// })
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container{
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// flex-direction: column;
|
||||
padding: 0 56rpx;
|
||||
.title{
|
||||
font-size: 72rpx;
|
||||
}
|
||||
.formContent{
|
||||
// width: 80%;
|
||||
margin-top: 140rpx;
|
||||
}
|
||||
.huanyingText {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: bold;
|
||||
font-size: 34rpx;
|
||||
color: #000000;
|
||||
line-height: 44rpx;
|
||||
font-style: normal;
|
||||
margin-top: 38rpx;
|
||||
|
||||
text {
|
||||
color: #199793;
|
||||
}
|
||||
}
|
||||
.huanying {
|
||||
margin-top: 130rpx;
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: bold;
|
||||
font-size: 72rpx;
|
||||
color: #000000;
|
||||
}
|
||||
.huanyingDes {
|
||||
font-family: PingFang SC, PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 28rpx;
|
||||
color: #999999;
|
||||
line-height: 44rpx;
|
||||
font-style: normal;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
}
|
||||
::v-deep .uv-input__content{
|
||||
flex-direction: column;
|
||||
align-items: unset;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user