Commit f47e6d5d authored by 贺世双's avatar 贺世双

初始化

parents
/node_modules/
/unpackage/dist
.idea
\ No newline at end of file
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"default" :
{
"launchtype" : "local"
},
"mp-weixin" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}
<script>
import { MINI, ENV_MODEL, TokenPrefix} from './publicConfig/config.js'
const loginBehavior = require('./mixins/loginBehavior.js')
// #ifdef H5
import VConsole from 'vconsole'; //引入H5调试工具
// #endif
export default {
mixins: [loginBehavior],
onLaunch() {
this.judgePlatform(); //判断运行平台,仅初始化时执行
},
globalData: {
projectMini: 'biz-trina5-app', //项目名
loginAction: '', //登录Action
cacheUserLocation: {}, //用户地址位置信息缓存,含经、纬度地址描述等
shipmentInfo: { //运单信息 跳转运单详情时设置
shipmentNo: "", //运单号
shipmentId: "", //运单id
shipmentBizName: "" //运单模式 tm.MovementShipment, tm.SimpleShipment
},
stopsInfo: { //站点信息 站点预约时修改
loadStop: '', //是否装货站点
unloadStop: '', //是否卸货站点
locationName: '', //站点名称
locationAddress: '', //站点地址
locationOrders: '', //订单数
locationQty: '', //件数数
locationWeight: '', //重量
locationVolume: '', //体积
},
allotInfo: {}, // 当前分配信息, 分配司机、车辆时赋值
backPagePath: '', //回退页面路径,含完整参数
serviceProvider: 'AMap' ,//地图服务商,默认为高德地图
MapIns: null , //地图实例, 默认在Index首页初始化, mxins下getUserLocation中赋值
},
methods: {
//判断平台
judgePlatform() {
//项目是否为H5 条件编译,代码块仅判断成功才运行
//#ifdef H5
if (process.env.NODE_ENV === 'development') { //HBuilderX中,点击运行是--开发环境,点击发行是--生产环境
this.globalData.projectMini = MINI.h5Dev
const vConsole = new VConsole(); //H5实例化调试工具,工具git地址:https://github.com/Tencent/vConsole
} else { //生产环境区分,默认生产PROD
switch(ENV_MODEL.model){
case 'UAT':
this.globalData.projectMini = MINI.h5Uat
break;
default:
this.globalData.projectMini = MINI.h5Prod
}
}
this.isRunningWxBrowser()
//#endif
//项目是否为小程序 MP代表所有小程序 MP-WEIXIN:微信小程序
//#ifdef MP
this.globalData.projectMini = MINI.wxApp
this.getWxToken()
//#endif
},
//判断是否运行于微信环境
isRunningWxBrowser() {
if (this.isWxBrowser()) { //判断是否运行在微信浏览器
const { code, time } = this.getH5Code()
if (code) {
this.login(code)
}
} else {
this.globalData.projectMini = MINI.web
this.login('')
}
},
//判断是否为微信浏览器
isWxBrowser(){
let ua = window.navigator.userAgent.toLowerCase() //判断浏览器类型, 包含浏览器类型、版本、操作系统类型、浏览器引擎类型等信息
return ua.match(/MicroMessenger/i) == 'micromessenger'
},
}
}
</script>
<style lang="scss">
@import "uview-ui/index.scss";
@import "static/iconfont/iconfont-weapp-icon.css";
/*每个页面公共css */
// 设置整个项目的背景色
page,
view {
margin: 0;
padding: 0;
font-family: PingFangSC-Regular, PingFang SC !important; //苹方简体
}
page,
view,
::after,
::before {
box-sizing: border-box
}
/*flex布局容器*/
.flex {
display: flex;
}
.flex_col {
display: flex;
flex-direction: column;
}
.flex_center {
display: flex;
align-items: center;
}
.flex_sb {
display: flex;
justify-content: space-between;
}
.flex_wrap {
flex-wrap: wrap;
}
.flex_end {
justify-content: flex-end;
}
.flex_cen {
display: flex;
justify-content: center;
}
/* 写一个CSS用于隐藏页面 */
.display_none {
display: none;
}
/* 字体加粗 */
.font_bolder {
font-weight: bolder;
}
/* 单行字体溢出省略 */
.overflowOmitRow {
width: 100%;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
/* 多行字体溢出省略 */
.overflowOmitRows {
width: 100%;
display: -webkit-box;
word-break: break-all;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}
.content_box {
color: #434343;
background: #FAFAFA;
min-height: 100vh;
font-size: 32rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
/* 统一样式类 */
.box_shadow_card {
padding: 20rpx;
border-radius: 24rpx;
background-color: #FFFFFF;
box-shadow: 1px 3px 12px 0px rgba(0, 0, 0, 0.04);
}
.routine_radius_card {
padding: 24rpx;
margin: 0 24rpx 24rpx;
background-color: #FFFFFF;
border-radius: 0 0 16rpx 16rpx;
}
/* 客户单号 */
.consumer_code {
font-size: 28rpx;
font-weight: 400;
color: #8C8C8C;
}
// 通用按钮
.common_btn {
width: 327rpx;
height: 88rpx;
font-size: 32rpx;
font-weight: bolder;
color: #2E75E6;
border-radius: 44rpx;
border: 2rpx solid;
background-image: linear-gradient(136deg, rgba(46, 117, 230, 1), rgba(46, 46, 230, 1)) 2 2;
margin-top: 20rpx;
&::after {
border: none;
}
}
.operation-btn {
width: 100%;
flex-grow: 1;
display: flex;
flex-wrap: wrap;
align-items: flex-end;
margin-bottom: 40rpx;
.submitBtn {
color: #FFFFFF;
background: linear-gradient(136deg, #2E75E6 0%, #2E2EE6 100%);
}
}
/deep/.u-form-item__body__left__content__required{
left: -16rpx !important;
line-height: 150% !important;
font-size: 28rpx !important;
top: 0 !important;
}
</style>
# biz-carrier-uniapp
承运商端
## 项目初始化
1. git clone #####项目地址
2. yarn/yarn start
3. 替换publicConfig目录下config.js 中的项目名、APPid等信息为实际项目信息。H5项目需要同步将manifest.json下H5代理地址替换为BASEURL的请求地址
4. 步骤3中项目名等信息,需要提前在bo-designer中系统配置 → 移动端配置下配置。
## 打包发布
1. HBuilderX工具中点击发布。(点击运行为开发环境,点击发布为生产环境)
2. 选择H5或网站PC将发布为H5项目,打包后文件部署至服务器即可。
3. 选择微信开发者工具则为小程序项目,将代码点击上传即可
## 添加彩色图标
1. 在iconfont官网将所需图标添加至购物车→我的项目中→下载至本地解压缩
2. 安装icon工具包
npm install -g iconfont-tools
yarn add -g iconfont-tools
3. 执行命令iconfont-tools
一路回车,将 iconfont-weapp 文件夹下iconfont-weapp-icon.css 替换static下文件即可。 更新图标只需执行步骤3即可
\ No newline at end of file
import Request from "./request";
import uploadFile from "./uploadFile";
import downloadFile from "./downloadFile";
// 登录接口
// mimi: 项目名:biz-carrier-wxapp LOGIN_ACTION: loginByUserInfo
export function userLogin(loginAction, loginCode, data) {
return Request(
`/api/auth/app-login/{mini}/miniAppDriverLogin?weixin_config_key={mini}&code=${loginCode}`,data
);
}
//登录小程序初始化时,调用auth自动登录
export function authLogin(loginAction, loginCode, data) {
return Request(
`/api/auth/app-login/{mini}/miniAppCarrierAuth?weixin_config_key={mini}&code=${loginCode}`,data
);
}
// 用户中心操作&&初始化JS-SDK action 说明: 退出登录:logout 获取用户信息: getUserInfo 修改密码:changePassword 初始化SDK: querySignature
export function userMobileOperation(action, data) {
if (action === "logout") {
//退出登录
return Request(`/logout`, data, "GET"); // data: {openId, entryName}
} else {
return Request(`/api/entry/{mini}/m-action/${action}`, data);
}
}
//获取地图key
export function getMap() {
return Request(`/api/query/*/action/getMap`);
}
//获取地图颜色
export function getMapColor(data) {
return Request(`/api/query/biz.md.Truck/action/service-getMapColor`, data);
}
// 上传图片
export function uploadImg(data, fileType = "image") {
return uploadFile("/api/doc/upload", data, fileType);
}
// 下载图片
export function downloadImg(data) {
return downloadFile("/api/doc/download", data);
}
//查询运单列表
export function findShipmentOrders(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.Shipment.tm_shipment_m_carrier.paging`,
data
);
}
//查询运单详情
export function findShipmentDetail(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.Shipment.tm_shipment_m_carrier_page.get`,
data
);
}
//查询运单途径站点
export function findShipmentStops(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.ShipmentStop.tm_shipmentstop_m_carrier.paging`,
data
);
}
// 查询货量明细
// OM模式
export function OMOrderDetail(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.OrderMovementLine.tm_ordermovementline_m_search.paging`,
data
);
}
// OR模式
export function OROrderDetail(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.OrderLine.tm_orderline_m_search.paging`,
data
);
}
//查询中转地址
export function searchTransferAddress(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.TransferAddress.md_transferaddress_m_search.paging`,
data
);
}
//查询准驾车型
export function queryDrivingType() {
return Request(`/api/query/choice.md.QuasiDrivingType/action/auto_choice`);
}
//查询车辆车型
export function queryTruckType(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.TruckMode.md_truckmode_m_search.paging`,
data
);
}
//查询司机列表
export function searchDriverList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Driver.md_driver_m_search.paging`,
data
);
}
//查询车辆列表
export function searchTruckList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Truck.md_truck_m_search.paging`,
data
);
}
//查询道口
export function searchDockList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Dock.md_dock_m_search.paging`,
data
);
}
//查询道口可预约时间
export function searchDockTime(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.DockTimeTable.md_docktimetable_m_search.paging`,
data
);
}
//中转换、卸货 分配车辆司机
//action说明: 中转换车:transferAndChangeTruck 中转卸货:transferAndUnload 分配车辆司机: assign
export function reportInfo(action, data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.Shipment.operation.${action}`,
data
);
}
//司机、车辆注册
//registerType说明: 司机注册:Driver 车辆注册:Truck
export function serviceRegister(registerType, data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.${registerType}.service.register`,
data
);
}
//司机、车辆详情
//action说明: 司机:Driver 车辆:Truck
export function searchDriverTruckDetail(action, data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.${action}.md_${action.toLocaleLowerCase()}_m_page.get`,
data
);
}
//预约站点
export function loadDockBooking(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.ShipmentStop.operation.loadDockBooking`,
data
);
}
//取消站点预约
export function dockBookingCancel(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.ShipmentStop.operation.dockBookingCancel`,
data
);
}
//调度确认、取消确认 operationType: approve approveCancel
export function shipmentDispatch(operationType, data) {
return Request(`/api/entry/{mini}/m-action/biz.tm.Shipment.operation.${operationType}`, data);
}
//签收 模式:OM:OrderMovement OR: SimpleOrder
export function signFor(shipMode, data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.${shipMode}.operation.receive`,
data
);
}
//订单列表 OM/OR/MASS模式
export function ordermovementOM(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.OrderMovement.tm_ordermovement_m_search.paging`,
data
);
}
export function ordermovementOR(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.SimpleOrder.tm_simpleorder_m_search.paging`,
data
);
}
export function ordermovementMASS(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.MassShipment.tm_massshipment_m_page.get`,
data
);
}
//修改人员详情
export function UpdateDriverInfo(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Driver.service.updateInfo`,
data
);
}
//修改车辆详情
export function UpdateTruckInfo(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Truck.service.updateInfo`,
data
);
}
//获取大宗订单
export function getMassOrderList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.MassOrderCarrier.tm_massordercarrier_m_search.paging`,
data
);
}
//获取大宗订单已分配车辆
export function getMassOrderTruckList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.MassOrderTruck.tm_massordertruck_m_search.paging`,
data
);
}
//大宗订单车辆分配 assignType: 分配单辆车:assignTruck 分配多量车:assignTrucks
export function massAssignTruck(data, assignType) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.MassOrderCarrier.${assignType}`,
data
);
}
//更新大宗订单配车
export function updateMassAssignTruck(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.MassOrderTruck.service.updateOrderTrucks`,
data
);
}
//更新大宗订单配车
export function GetSignUrl(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.OrderRelease.operation.electronicVisa`,
data
);
}
// 验证码发送
export function SendMessage(data) {
return Request(`/handler/sendMessage`, data);
}
// 订单轨迹
export function findShipmentLbs(data) {
return Request(
`/api/entry/{mini}/m-action/biz.tm.Shipment.service.lbs`,
data
);
}
//获取承运商
export function GetCarrierList(data) {
return Request(
`/api/entry/{mini}/m-action/biz.md.Company.md_company_search.paging`,
data
);
}
// 百度OCR ocrType: 身份证 idCard 驾驶证 drivingLicense 行驶证 vehicleLicense
export function OCRService(ocrType, data) {
return Request(`/api/entry/{mini}/m-action/biz.adapter.AdapterTask.service.${ocrType}`, data)
}
\ No newline at end of file
import { BASEURL, TokenPrefix, APIPrefix } from '../publicConfig/config'
let downloadFile = (api, filePath, header = {}) => {
filePath = filePath.split('=')[1] ? filePath.split('=')[1] : filePath
header = {
'Cookie': 'XSRF-TOKEN=' + uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"X-XSRF-TOKEN": uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"Authorization": uni.getStorageSync(`${TokenPrefix}_token`) || '',
}
uni.showLoading({
title: '下载中',
})
return new Promise((reslove, reject) => {
api = APIPrefix + api
uni.downloadFile({
//#ifdef MP
url: BASEURL.url + `${api}/` + filePath, //下载资源的 url
//#endif
//#ifdef H5
url: `${api}/` + filePath, //H5使用代理
//#endif
header,
success: (res) => {
uni.hideLoading()
if (res.statusCode == 401 || res.statusCode == -1) {
let app = getApp()
uni.showToast({
title: '登录已过期',
icon: 'error',
duration: 3000
})
uni.removeStorageSync(`${TokenPrefix}_token`)
uni.removeStorageSync(`${TokenPrefix}_XSRFToken`)
uni.removeStorageSync('cookies')
app.judgePlatform();
}
reslove(res.tempFilePath)
},
fail: (err) => {
uni.hideLoading()
uni.showToast({
title: '下载失败!',
icon: 'error'
})
reject(err)
}
})
})
}
export default downloadFile
import { BASEURL, TokenPrefix, APIPrefix } from '../publicConfig/config'
let serverCode = {
3: '重定向',
4: '请求包含错误或未找到资源',
5: '服务器错误'
}
let Request = (api, data, method = 'POST', header = {}, showLoading = true) => {
header = {
'Content-Type': 'application/json;charset=UTF-8',
'Cookie': 'XSRF-TOKEN=' + uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"X-XSRF-TOKEN": uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"Authorization": uni.getStorageSync(`${TokenPrefix}_token`) || '',
...header
}
if(showLoading){
uni.showLoading({
title: '加载中...',
})
}
console.log('请求参数:', data);
return new Promise((reslove, reject) => {
const { projectMini } = getApp().globalData
const newApi = APIPrefix + api.replace(/{mini}/g, projectMini)
uni.request({
//#ifdef MP
url: BASEURL.url + newApi,
//#endif
//#ifdef H5
url: newApi,
//#endif
data,
method,
header,
success: (res) => {
if(showLoading){
uni.hideLoading()
}
if (res.statusCode == '404') {
uni.showToast({
title: '无法找到资源',
icon: 'error',
duration: 3000
})
} else if (res.statusCode === 401 && !newApi.includes('/auth/app-login')) {
let app = getApp()
uni.showToast({
title: '登录已过期',
icon: 'error',
duration: 3000
})
uni.removeStorageSync(`${TokenPrefix}_token`)
uni.removeStorageSync(`${TokenPrefix}_XSRFToken`)
uni.removeStorageSync('cookies')
app.judgePlatform();
} else if (res.statusCode !== 200) {
console.log('响应日志:', res.data);
if (res.data.messageType) {
if(!newApi.includes('/auth/app-login')){ //登录接口自定义处理
uni.showToast({
title: res.data.message,
icon: 'none',
duration: 3000
})
}
reject(res.data)
} else {
uni.showToast({
title: serverCode[res.statusCode.toString()[0]],
icon: 'error',
duration: 3000
})
}
} else {
reslove(res)
}
},
fail: (err) => {
if(showLoading){
uni.hideLoading()
}
uni.showToast({
title: err.info || '请求失败!',
icon: 'error',
duration: 2000
})
reject(err)
}
})
})
}
export default Request
import {BASEURL, TokenPrefix, APIPrefix} from '../publicConfig/config'
import compress from '../utils/compress'
let uploadFile = async (api, filePath, fileType, header = {}) => {
header = {
'Cookie': 'XSRF-TOKEN=' + uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"X-XSRF-TOKEN": uni.getStorageSync(`${TokenPrefix}_XSRFToken`) || '',
"Authorization": uni.getStorageSync(`${TokenPrefix}_token`) || '',
}
uni.showLoading({
title: '上传中',
})
if(fileType === 'image'){
filePath = await compress(filePath) //图片压缩
}
return new Promise((reslove, reject) => {
api = APIPrefix + api
uni.uploadFile({
//#ifdef MP
url: BASEURL.url + `${api}`, //开发者服务器地址
//#endif
//#ifdef H5
url: api, //H5使用代理
//#endif
filePath,
name: 'file',
header,
success: (res) => {
uni.hideLoading()
if (res.data.reCode == 401 || res.data.reCode == -1) {
let app = getApp()
uni.showToast({
title: '登录已过期',
icon: 'error',
duration: 3000
})
uni.removeStorageSync(`${TokenPrefix}_token`)
uni.removeStorageSync(`${TokenPrefix}_XSRFToken`)
uni.removeStorageSync('cookies')
app.judgePlatform();
}
reslove(res.data)
},
fail: (err) => {
uni.hideLoading()
uni.showToast({
title: '上传失败!',
icon: 'error'
})
console.log(err);
reject(err)
}
})
})
}
export default uploadFile
<template>
<view class="custom_camera" v-if="visible">
<CustomCamera ref="customCamera" :coverImage="coverImage" @onCancel="onCancel" @onConfirm="onConfirm"/>
</view>
</template>
<script>
// #ifdef H5
import CustomCamera from './cameraH5.vue'
// #endif
// #ifdef MP-WEIXIN
import CustomCamera from "./cameraWeChat.vue"
// #endif
// #ifdef APP
import CustomCamera from "./cameraApp.vue"
// #endif
export default {
components:{CustomCamera},
data() {
return {
coverImage: '', //证照遮罩图
}
},
props: {
visible: {
type: Boolean,
default: false
},
cameraType: {
type: String,
default: '' //相机类型 idCardFace idCardBack
}
},
watch: {
visible: {
handler(visible) {
if(visible){
this.$nextTick(() => this.$refs.customCamera.onInitCamera()) //因v-if异步渲染,避免获取组件实例失败
const cardImges = {
idCardFace: 'static/img/camera/idCardFace.png', //身份证正面
idCardBack: 'static/img/camera/idCardBack.png', //身份证国徽面
driveCardFace: 'static/img/camera/driveCardFace.png', //驾驶证正页
driveCardBack: 'static/img/camera/driveCardBack.png', //驾驶证副页
vehicleCardFace: 'static/img/camera/driveCardBack.png', //行驶证正页
vehicleCardBack: 'static/img/camera/driveCardBack.png', //行驶证副页
}
const coverImage = cardImges[this.cameraType]
if(coverImage){
// #ifdef H5
this.coverImage = `../../${coverImage}`
// #endif
// #ifdef MP
let base64Png = uni.getFileSystemManager().readFileSync(cardImges[this.cameraType], 'base64');
this.coverImage = 'data:image/jpg;base64,' + base64Png
// #endif
}
} else {
//退出拍照 清空画布、停止视频流等操作
this.$refs.customCamera.onExitCamera()
}
}
}
},
methods: {
//退出拍照
onCancel() {
this.$emit('uploadCancel')
},
//拍照完成
onConfirm(imgSrc){
this.$emit('uploadFinish', imgSrc)
}
}
}
</script>
<style scoped lang="scss">
.custom_camera {
width: 100vw;
height: 100vh;
background-color: #000000;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
}
</style>
\ No newline at end of file
<template>
<view class="custom_camera_App">
<live-pusher id="livePusher" style="width: 100%; flex-grow: 1;position: relative" :url="url" :mode="mode" :aspect="aspect" :beauty="0" :whiteness="0"
:orientation="orientation" :auto-focus="true" :muted="true" :enable-camera="true" @error="onError">
<!-- 遮罩层 -->
<template v-if="previewImg">
<cover-image class="previewImg" :src="previewImg" />
</template>
<template v-else>
<cover-image class="coverImage" v-if="coverImage" :src="coverImage" />
<cover-view class="camera-menuTop">
<cover-image class="menuBtn" mode="aspectFill" src="/static/img/camera/back.png" @click="onCancel" />
<cover-image class="menuBtn" mode="aspectFill" src="/static/img/camera/track.png" @click="onTrack" />
</cover-view>
</template>
</live-pusher>
<view class="camera-menuBottom">
<view class="previewBtns" v-if="previewImg">
<image class="menuBtn" mode="aspectFill" src="/static/img/camera/cancel.png" @click="onRetry" />
<image class="menuBtn" mode="aspectFill" src="/static/img/camera/confirm.png" @click="onConfirm" />
</view>
<template v-else>
<image class="takeBtn" mode="aspectFill" src="/static/img/camera/shutter.png" @click="onTakePhoto" />
<image class="menuBtn reversalBtn" mode="aspectFill" src="/static/img/camera/reversal.png" @click="onReversePhoto" />
</template>
</view>
</view>
</template>
<script>
// 1.因为uniapp的camera组件只支持微信小程序,所以APP只能采用live-pusher组件来解决这个问题
// 当然live-pusher组件没有camera的性能好,而且必须是nvue文件(这些官方也有介绍[](https://uniapp.dcloud.net.cn/component/live-pusher.html))
export default {
props: {
coverImage: {
type: String,
default: '' //遮罩类型
}
},
data() {
return {
previewImg: '', //预览图
livePusherCtx: null,
//相机参数
url: '', //必填 推流地址,支持RTMP协议。
mode: 'SD', //推流视频模式,可取值:SD(标清), HD(高清), FHD(超清)
aspect: '3:2', //视频宽高比例
orientation: 'vertical', //画面方向 竖直:vertical 水平:horizontal
}
},
methods: {
//相机初始化
onInitCamera(){
this.livePusherCtx = uni.createLivePusherContext('livePusher', this);
setTimeout(() => {
this.startPreview()
}, 1000)
},
startPreview() {
this.livePusherCtx.startPreview({
success: () => {
switch (plus.os.name) {
case 'Android':
break;
case 'iOS':
this.onReversePhoto()
break
}
},
fail: (err) => {
console.log(err)
}
});
},
//退出相机
onExitCamera(){
this.previewImg = ''
this.livePusherCtx = null
},
// 返回
onCancel(){
this.$emit('onCancel')
},
//拍照
onTakePhoto(){
this.livePusherCtx.snapshot({
success: (res) => {
this.previewImg = res.message.tempImagePath
}
})
},
//翻转摄像头
onReversePhoto(){
this.livePusherCtx.switchCamera();
},
//闪光灯
onTrack(){
},
//渲染错误事件
onError(err) {
console.log(err)
},
//提交
onConfirm(){
this.$emit('onConfirm', this.previewImg)
},
//重拍
onRetry(){
this.previewImg = ''
}
}
}
</script>
<style lang="scss" scoped>
.custom_camera_App {
height: 100%;
display: flex;
flex-direction: column;
.previewImg{
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 6; //层级应高于 Vido、Canvas
}
.coverImage {
width: 65%;
height: 75%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 6; //层级应高于 Vido、Canvas
}
.menuBtn{
width: 60rpx;
height: 60rpx;
}
.camera-menuTop{
width: 100%;
padding: 30rpx 60rpx;
box-sizing: border-box;
display: flex;
justify-content: space-between;
position: absolute;
z-index: 6; //层级应高于 Vido、Canvas
}
.camera-menuBottom{
height: 320rpx;
display: flex;
justify-content: center;
align-items: center;
.previewBtns{
width: 100%;
padding: 0 60rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.takeBtn{
width: 120rpx;
height: 120rpx;
}
.reversalBtn{
position: absolute;
right: 60rpx;
}
}
}
</style>
This diff is collapsed.
<template>
<view class="custom_camera_WeChat">
<camera style="width: 100%; flex-grow: 1;position: relative" mode="normal" :resolution="resolution" :device-position="device"
:frame-size="frameSize" :flash="flash" @error="onError">
<!-- 遮罩层 -->
<template v-if="previewImg">
<cover-image class="previewImg" :src="previewImg" />
</template>
<template v-else>
<cover-image class="coverImage" v-if="coverImage" :src="coverImage" />
<cover-view class="camera-menuTop">
<cover-image class="menuBtn" mode="aspectFill" src="/static/img/camera/back.png" @click="onCancel" />
<cover-image class="menuBtn" mode="aspectFill" src="/static/img/camera/track.png" @click="onTrack" />
</cover-view>
</template>
</camera>
<view class="camera-menuBottom">
<view class="previewBtns" v-if="previewImg">
<image class="menuBtn" mode="aspectFill" src="/static/img/camera/cancel.png" @click="onRetry" />
<image class="menuBtn" mode="aspectFill" src="/static/img/camera/confirm.png" @click="onConfirm" />
</view>
<template v-else>
<image class="takeBtn" mode="aspectFill" src="/static/img/camera/shutter.png" @click="onTakePhoto" />
<image class="menuBtn reversalBtn" mode="aspectFill" src="/static/img/camera/reversal.png" @click="onReversePhoto" />
</template>
</view>
</view>
</template>
<script>
export default {
props: {
coverImage: {
type: String,
default: '' //遮罩类型
}
},
data() {
return {
previewImg: '', //预览图
cameraContext: null,
//相机参数
device: 'back', //前置或后置摄像头,前置: front 后置: back
flash: 'off', // 闪光灯 自动: auto 打开: on 关闭: off 常亮: torch
resolution: 'high', //分辨率: 有效值为low, medium, high,不支持动态修改
frameSize: 'medium', //指定期望的相机帧数据尺寸,值为small, medium, large
}
},
methods: {
//相机初始化
onInitCamera(){
uni.getSetting({ //判断用户是否授权相机权限
success: (res) => {
if (!res.authSetting['scope.camera']) {
uni.authorize({
scope: 'scope.camera',
success: () => {
this.cameraContext = uni.createCameraContext()
},
fail: () => {
uni.showModal({
title: '提示',
content: '缺失相机权限,前往系统设置开启相机权限!',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: () => {
this.onInitCamera()
}
});
}
}
});
}
});
} else {
this.cameraContext = uni.createCameraContext()
}
}
})
},
//退出相机
onExitCamera(){
this.previewImg = ''
this.cameraContext = null
},
// 返回
onCancel(){
this.$emit('onCancel')
},
//拍照
onTakePhoto(){
this.cameraContext.takePhoto({
quality: 'high', //成像质量,值为high(高质量)、normal(普通质量)、low(低质量),默认normal
selfieMirror: 'true', //是否开启镜像,默认true。仅微信小程序 2.22.0+ 支持
success: (res) => {
this.previewImg = res.tempImagePath
}
})
},
//翻转摄像头
onReversePhoto(){
this.device = this.device === 'back' ? 'front' : 'back'
},
//闪光灯
onTrack(){
this.flash = this.flash === 'off' ? 'torch' : 'off'
},
//用户不允许使用摄像头时触发
onError(err) {
console.log(err)
},
//提交
onConfirm(){
this.$emit('onConfirm', this.previewImg)
},
//重拍
onRetry(){
this.previewImg = ''
}
},
}
</script>
<style lang="scss" scoped>
.custom_camera_WeChat {
height: 100%;
display: flex;
flex-direction: column;
.previewImg{
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 6; //层级应高于 Vido、Canvas
}
.coverImage {
width: 65%;
height: 75%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 6; //层级应高于 Vido、Canvas
}
.menuBtn{
width: 60rpx;
height: 60rpx;
}
.camera-menuTop{
width: 100%;
padding: 30rpx 60rpx;
box-sizing: border-box;
display: flex;
justify-content: space-between;
position: absolute;
z-index: 6; //层级应高于 Vido、Canvas
}
.camera-menuBottom{
height: 320rpx;
display: flex;
justify-content: center;
align-items: center;
.previewBtns{
width: 100%;
padding: 0 60rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.takeBtn{
width: 120rpx;
height: 120rpx;
}
.reversalBtn{
position: absolute;
right: 60rpx;
}
}
}
</style>
\ No newline at end of file
<template>
<view class="custom-cell-row">
<u-row>
<u-col :span="labelCol" @click.stop>
<view class="custom-cell-label">
<slot name="label"></slot>
<text class="custom-cell-col" v-if="label">{{label}}</text>
</view>
</u-col>
<u-col :span="wrapCol">
<view class="custom-cell-value">
<slot name="value"></slot>
<text class="custom-cell-col" v-if="value">{{value}}</text>
</view>
</u-col>
<slot name="right-icon"></slot>
</u-row>
</view>
</template>
<script>
export default {
name: 'bs-customCell',
props: {
label: {
type: String,
default: "" //默认值为空
},
value: {
type: String,
default: "" //默认值为空
},
labelCol: {
type: String,
default: '6'
},
wrapCol: {
type: String,
default: '6'
},
}
}
</script>
<style lang="scss">
.custom-cell-row {
font-size: 28rpx;
margin-top: 24rpx;
color: #434343;
.custom-cell-col {
width: 100%;
}
}
</style>
<template>
<view class="customRowCell">
<u-cell-group>
<u-cell center :url="linkUrl" :link-type="linkType" arrow="false">
<view slot="icon" :class="['t-icon cell-leftIcon', 't-icon-' + leftIcon]" />
<view slot="title" class="customRow-title">
<slot name="title"></slot>
<text :class="{ overflowOmitRows: isOmits}" v-if="title">{{title}}</text>
</view>
<view class="customRow-value" slot="value">
<slot name="value"></slot>
<text v-if="value"> {{value}}</text>
</view>
<view slot="right-icon" class="flex_sb icon-min-width">
<text class="right-separator" v-if="separator" />
<slot name="rightIcon"></slot>
<view slot="icon" :class="['t-icon cell-rightIcon', 't-icon-' + rightIcon]" v-if="rightIcon" />
</view>
</u-cell>
</u-cell-group>
</view>
</template>
<script>
export default {
name: 'bs-customRowCell',
options: { styleIsolation: 'shared' },
props: {
title: { //标题
type: String,
default: ''
},
value: { //右侧内容
type: String,
default: ''
},
leftIcon: { //标题左侧图标
type: String,
default: ""
},
rightIcon: { //右侧图标
type: String,
default: ""
},
linkUrl: { //跳转页面地址
type: String,
default: ""
},
linkType: { //跳转类型
type: String,
default: 'navigateTo'
},
isOmits: { //是否多行省略
type: Boolean,
default: false
},
separator: { //是否显示分隔符
type: Boolean,
default: false
}
}
}
</script>
<style lang="scss">
.customRowCell {
&/deep/.u-cell-group {
.u-line {
border: none !important;
}
.u-cell__body {
padding: 20rpx 0;
border: none !important;
}
}
.cell-leftIcon,
.cell-rightIcon {
width: 44rpx;
height: 45rpx;
}
.right-separator {
border-left: 2rpx solid #E5E5E5;
min-height: 30rpx;
}
.customRow-title {
margin-left: 20rpx;
}
.customRow-value {
color: #333333;
white-space: nowrap;
}
.icon-min-width {
min-width: 75rpx;
}
}
</style>
<template>
<view class="datetimePicker" @touchmove.stop.prevent="moveHandle">
<u-datetime-picker ref="datetimePicker" itemHeight="120" :visibleItemCount="visibleItem" :show="visible"
v-model="timestamp" :mode="dateType" @confirm="onSelsectDate" @cancel="onVisible" />
</view>
</template>
<script>
export default {
name: "bs-datetimePicker",
options: { styleIsolation: 'shared' },
props: {
visible: {
type: Boolean,
default: false
},
dateType: {
type: String,
default: 'date'
}
},
onReady() {
// 微信小程序需要用此写法
this.$refs.datetimePicker.setFormatter(this.formatter)
},
data() {
return {
//时间选择配置
showPopup: false,
timestamp: new Date().getTime(),
dateValue: "", //时间戳转换后 年-月-日
visibleItem: 4,
};
},
methods: {
moveHandle(){
},
//自定义时间格式
formatter(type, value) {
if (type === 'year') {
return `${value}年`;
}
if (type === 'month') {
return `${value}月`;
}
if (type === 'day') {
return `${value}日`;
}
if (type === 'hour') {
return `${value}时`;
}
return `${value}分`;
},
onVisible() {
this.$emit('onVisible')
},
// 时间选择
onSelsectDate(dateInfo) {
const date = this.formatDate(dateInfo.value)
this.dateValue = date
this.timestamp = dateInfo.value
this.$emit('onSelsectDate', { dateValue: date, timestamp: dateInfo.value, })
},
//时间戳转换
formatDate(timestamp) {
const nowDate = new Date(timestamp)
var year = nowDate.getFullYear();
var month = nowDate.getMonth() + 1;
var date = nowDate.getDate();
const hour = nowDate.getHours()
const minute = nowDate.getMinutes()
if (this.dateType === 'date') {
return `${year}-${ month}-${date}`
} else {
return `${year}-${ month}-${date} ${hour}-${ minute}`
}
},
}
}
</script>
<style lang="scss">
.datetimePicker{
z-index: 1000;
/* #ifdef MP */
/deep/.u-popup__content {
height: 40vh !important;
.u-picker__view__column {
.u-picker__view__column__item {
line-height: 88rpx !important;
}
}
}
/* #endif */
}
</style>
<template>
<view class="page-header">
<view class="page-header-orderNo flex_center">
运单号: {{shipmentNo}}
</view>
</view>
</template>
<script>
export default {
name: "bs-pageHeader-orderNo",
props: {
shipmentNo: {
type: String,
default: "" //运单号
},
}
}
</script>
<style lang="scss">
.page-header {
padding: 40rpx 24rpx 0;
background: #2E75E6;
border-radius: 0 0 32rpx 32rpx;
.page-header-orderNo {
color: #2E75E6;
padding: 30rpx 24rpx;
background: linear-gradient(90deg, rgba(46, 117, 230, .1), rgba(46, 117, 230, .1));
background-color: #EDEFFD;
border-radius: 16rpx 16rpx 0 0;
}
}
</style>
<template>
<view class="selectPicker" @touchmove.stop.prevent="moveHandle">
<u-picker ref="uPicker" keyName="label" immediateChange confirmText="确定" :show="visible" :title="PanelTitle" :closeOnClickOverlay="true" :itemHeight="68"
:columns="[pickerEnums]" :singleIndex="singleIndex" :visibleItemCount="visibleNum" @cancel="onCancel" @confirm="onConfirm" @close="onCancel"/>
</view>
</template>
<script>
export default {
name: "bs-selectPicker",
options: { styleIsolation: 'shared' },
data() {
return {
singleIndex: 0 //默认选中项
};
},
props: {
visible: {
type: Boolean,
default: false
},
pickerEnums: { //选择项枚举
type: Array,
default: []
},
PanelTitle: { //面板标题
type: String,
default: ""
},
visibleNum: {
type: Number,
default: 3
}
},
methods: {
moveHandle(){
},
//确定
onConfirm(e) {
const value = e.value[0].value
this.$emit('onConfirm', value)
},
//设置默认值
setDefaultValue(index) {
this.singleIndex = index
},
//取消
onCancel() {
this.$emit('onVisible')
}
}
}
</script>
<style lang="scss">
.selectPicker{
/deep/.u-toolbar{
height: 80rpx !important;
}
/deep/.u-slide-up-enter-active {
height: 240rpx;
}
}
</style>
<template>
<!-- 选择弹窗 -->
<view class="selectPopup" @touchmove.stop.prevent="onTouchMove">
<u-popup :show="visible" round="32" mode="bottom" closeable @close="onCancel" duration="1">
<view class="select-container">
<view class="popTitle"> <text>{{popTitle}}</text> </view>
<scroll-view scroll-y class="select-list">
<u-radio-group v-model="currentChecked" placement="column" iconPlacement="right">
<u-radio :customStyle="{margin: '22rpx 40rpx'}" :size="40" :iconSize="32" :labelSize="32"
labelColor="#262626" v-for="(item, index) in selectEnum" :key="index" :label="item.label"
:name="item.value" @change="onChange">
</u-radio>
</u-radio-group>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
name: "bs-selectPopup",
options: { styleIsolation: 'shared' },
props: {
visible: { //弹窗显隐
type: Boolean,
default: false
},
selectEnum: { //选择项枚举
type: Array,
default: []
},
popTitle: { //弹窗标题
type: String,
default: ""
}
},
data() {
return {
currentChecked: '', //当前选中类型
};
},
methods: {
setSelect(value) {
this.currentChecked = value
},
onChange(value) {
this.setSelect(value)
setTimeout(() => {
this.onConfirm()
}, 350)
},
onCancel() {
this.$emit('onVisible')
},
//确认选择
onConfirm() {
this.$emit('onConfirm', this.currentChecked)
},
//防止滚动穿透
onTouchMove() {}
}
}
</script>
<style lang="scss">
.selectPopup {
.select-container {
min-width: 70vw;
height: 50vh;
padding: 30rpx;
font-weight: 400;
.popTitle {
color: #8C8C8C;
font-size: 32rpx;
text-align: center;
margin-bottom: 44rpx;
}
.select-list{
height: 460rpx;
}
}
/deep/.uicon-close {
font-size: 40rpx !important;
width: 24rpx;
height: 24rpx;
margin-right: 20rpx;
}
}
</style>
<template>
<view class="uploade-file">
<view class="uploade-title" v-if="uploadTitle">
<text style="color: #EB2F48;position: absolute;left: 18rpx;" v-if="required">*</text>
<text>{{uploadTitle}}{{fileList.length}}/{{maxCount}}</text>
</view>
<view class="fileList flex flex_wrap">
<view class="previewImages" v-for="(item,index) in fileDetailList" :key="index">
<template v-if="item.type === 'image'">
<image mode="aspectFill" :src="item.url" :data-index="index" @click="previewImage" class="previewImg" />
</template>
<template v-if="item.type === 'video'">
<video class="previewImg" :src="item.url" :page-gesture="true" :show-mute-btn="true" :enable-play-gesture="true"/>
</template>
<u-icon v-if="isShowDelete" name="close-circle-fill" class="clearIcon" size="30" @click="deleteFile" :index='index' />
</view>
<template v-if="isIos || cameraType">
<view class="uploadeBtn flex_col" @click="onUpLoad">
<view class="t-icon t-icon-picture" />
<text style="margin-top:10rpx">{{btnTitle}}</text>
<text class="font_bolder orderTotal" v-if="!uploadTitle">{{fileList.length}}/{{maxCount}}</text>
</view>
</template>
<template v-else>
<u-upload :fileList="fileList" :maxCount="maxCount" :accept="uploadAccept" :maxDuration="maxDuration"
:sizeType="['compressed']" multiple @afterRead="uploadAfterRead" :previewImage="false">
<!-- 自定义上传按钮 -->
<view class="uploadeBtn flex_col" v-if="isShowBtn">
<view class="t-icon t-icon-picture" />
<text style="margin-top:10rpx">{{btnTitle}}</text>
<text class="font_bolder orderTotal" v-if="!uploadTitle">{{fileList.length}}/{{maxCount}}</text>
</view>
<view v-else style=" height: 140rpx"></view>
</u-upload>
</template>
</view>
<!-- 自定义相机 -->
<bs-customCamera :visible="visible" :cameraType="cameraType" @uploadFinish="customCameraFinish" @uploadCancel="visible=false"/>
</view>
</template>
<script>
const uploadBehavior = require('../../../mixins/uploadBehavior')
export default {
name: "bs-uploader",
mixins: [uploadBehavior],
options: { styleIsolation: 'shared' },
props: {
uploadTitle: {
type: String,
default: ""
},
uploadAccept: {
type: String, //接受的文件类型: image | video | file | all | media; file只支持H5(all、media仅小程序支持)
default: "image"
},
maxDuration: {
type: [String, Number], //当accept为video时生效,拍摄视频最长拍摄时间,单位秒
default: 60
},
maxCount: {
type: [String, Number], //最大上传数量
default: 6
},
btnTitle: { //按钮名字
type: String,
default: "上传图片"
},
isShowBtn: {
type: Boolean,
default: true //是否显示上传按钮
},
required: {
type: Boolean,
default: false //图片是否必传
},
isShowDelete: {
type: Boolean,
default: true //是否显示删除按钮
},
cameraType: {
type: String,
default: '' //自定义拍照类型 如:身份证、驾驶证等
},
},
data() {
return {
}
}
}
</script>
<style lang="scss">
.uploade-file {
.uploade-title {
font-size: 28rpx;
color: #8C8C8C;
}
.fileList {
width: 100%;
.previewImages {
position: relative;
overflow: hidden;
.previewImg {
width: 180rpx;
height: 140rpx;
margin: 24rpx 24rpx 0 0;
}
/deep/.uicon-close-circle-fill {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
background-color: #fff;
color: #D81E06 !important;
position: absolute;
top: 4rpx;
right: 4rpx;
}
}
.uploadeBtn {
position: relative;
color: #BFBFBF;
width: 180rpx;
height: 140rpx;
margin-top: 24rpx;
border: 2rpx dashed #D9D9D9;
background-color: #FAFAFA;
justify-content: center;
align-items: center;
font-size: 24rpx;
.t-icon-picture{
width: 50rpx;
height: 50rpx;
}
.orderTotal {
color: #333333;
position: absolute;
right: -100rpx;
bottom: 0;
z-index: 999;
}
}
}
}
</style>
<template>
<view class="user-agreement flex_center flex_cen">
<u-checkbox-group>
<u-checkbox name="userAuth" shape="circle" size="44" iconSize='44' @change="onChange" />
</u-checkbox-group>
<text>请您详细阅读并授权</text>
<text class="special-text" @click="onVisible(true)">运匠用户协议</text>
<text></text>
<text class="special-text" @click="onVisible(true)">隐私政策</text>
<!-- 协议详情 -->
<view @touchmove.stop.prevent="onTouchMove">
<u-popup :show="visible" mode="right" duration="300" :overlay="false">
<view class="agreement-contentBox" style="width: 100%;">
<view class="agreement-title flex_center">
<u-icon name="arrow-left" size="48" style="margin-right: 25rpx;" @click="onVisible(false)" />
<view class="flex_cen" style="flex-grow: 1;font-size: 38rpx; color: #2c3e50;">用户隐私协议</view>
</view>
<scroll-view scroll-y class="agreement-container">
<view class="detail-title">一、位置及第三方授权</view>
<view class="detail-content">
您明确知悉并同意,您授权我平台通过中交兴路及关联方、第三方机构对您在平台认证的车辆进行当前位置、轨迹跟踪等定位信息查询并授权我平台及其关联方予以使用。授权时间为长期有效。
</view>
<view class="detail-title">二、其它</view>
<view class="detail-content">本应用仅公司内部用户和POC演示使用</view>
</scroll-view>
</view>
</u-popup>
</view>
</view>
</template>
<script>
export default {
name: 'bs-userPrivacy',
data() {
return {
userAuth: false, //用户是否授权
visible: false,
}
},
methods: {
onTouchMove() {
//防止滚动穿透
},
onVisible(value) {
this.visible = value
},
onChange(value) {
this.userAuth = value
this.$emit('onAuthChange', value)
}
}
}
</script>
<style lang="scss">
.user-agreement {
font-size: 26rpx;
font-weight: 400;
color: #8C8C8C;
line-height: 37rpx;
letter-spacing: 2rpx;
margin: 40rpx 0 95rpx;
.special-text {
font-size: 28rpx;
font-weight: bolder;
color: #2E75E6;
margin: 0 4rpx
}
.agreement-title {
padding: 50rpx 25rpx;
}
.agreement-container {
padding: 0 12rpx;
height: calc(100vh - 148rpx);
.detail-title {
color: #2c3e50;
font-weight: 400;
padding: 10rpx 14rpx;
}
.detail-content {
text-indent: 50rpx; //首行缩进
letter-spacing: 4rpx; //字符间距
}
}
}
</style>
\ No newline at end of file
<template>
<view class="orderCard">
<bs-customCell :label="propData.orderNo" labelCol="9.4" wrapCol="2">
<text slot="value" @click="navToOrderDetail">查看详情</text>
<view slot="right-icon" class="t-icon t-icon-rightArrow" @click="navToOrderDetail" />
</bs-customCell>
<!-- 起始目的地 -->
<u-line color="#E5E5E5" margin="0" />
<view class="orderCard-container">
<view class="stations">
<view class="start-station flex">
<view class="start-icon flex_center"></view>{{propData['originalAddress.name']}}
</view>
<view class="end-station flex">
<view class="end-icon flex_center"></view>{{propData['destinationAddress.name']}}
</view>
</view>
<bs-customCell label="货量 :" labelCol="2" wrapCol="10"
:value="`${propData.qty}件 / ${propData.weight}千克 / ${propData.volume}方`" />
</view>
</view>
</template>
<script>
export default {
name: "orderCard",
options: { styleIsolation: 'shared' },
props: {
propData: {
type: Object,
default: {}
},
},
data() {
return {
};
},
methods: {
navToOrderDetail() {
wx.navigateTo({
url: `/subpkg/orderDetail/orderDetail?data=${JSON.stringify(this.propData)}`,
})
},
}
}
</script>
<style lang="scss">
.orderCard {
margin-bottom: 24rpx;
background-color: #FFFFFF;
.start-station,
.end-station {
margin: 20rpx 0;
font-size: 30rpx;
font-weight: bolder;
.start-icon,
.end-icon {
width: 40rpx;
height: 40rpx;
font-size: 24rpx;
padding: 8rpx;
border-radius: 50%;
color: #FFFFFF;
background-color: #2E75E6;
margin-right: 20rpx;
}
.end-icon {
background-color: #F7A64A;
}
}
.custom-cell-row {
color: #8C8C8C;
margin: 0;
padding: 28rpx 30rpx 24rpx 32rpx;
}
.orderCard-container {
padding: 8rpx 32rpx;
.custom-cell-row {
color: #434343;
padding: 0;
margin: 49rpx 0 32rpx;
}
}
}
</style>
<template>
<view class="shipmentItem box_shadow_card">
<view class="shipment-header flex_sb" style="padding: 28rpx 24rpx 0;">
<view class="consumer_code flex_center">
<text selectable> 订单号:{{propData['massOrder.orderNo'] || ''}}</text>
</view>
<view class="consumer_code flex_center">
<text selectable>
剩余货量:{{propData['massOrder.splitMode'] === 'Weight'? propData.weight : propData.qty || '0'}}{{['massOrder.splitMode'] === 'Weight'? "kg": '件'}}</text>
</view>
</view>
<u-line color="#E5E5E5" margin="20rpx 0" />
<view style="padding: 0 24rpx 32rpx;">
<view class="stations">
<view class="start-station flex_center">
<view class="t-icon-start-sddress t-icon" style="margin-right: 8rpx;"></view>
{{propData['massOrder.originalAddress.name'] || ''}}
</view>
<view class="end-station flex_center">
<view class="t-icon-end-sddress t-icon" style="margin-right: 8rpx;"></view>
{{propData['massOrder.destinationAddress.name'] || ''}}
</view>
</view>
<bs-customCell label="接单日期 :" :value="orderDate" labelCol="3" wrapCol="6" />
</view>
<view class="flex_cen flex_center orderItemButtonGroup">
<button class="flex_cen flex_center" @click="onAllotCar('unMulti')">分配车辆</button>
<button class="flex_cen flex_center" @click="onAllotCar('multi')">分配多辆车</button>
<button class="flex_cen flex_center" style="margin-top: 24rpx;" @click="onAllotCar('record')">分配记录</button>
</view>
</view>
</template>
<script>
import { formatGMT } from '../../utils/util'
export default {
name: 'shipmentItem',
options: { styleIsolation: 'shared' },
props: {
propData: {
type: Object,
default: {}
},
},
methods: {
onAllotCar(allotType) {
const orderNo = this.propData['massOrder.orderNo']
const orderId = this.propData['id']
uni.navigateTo({
url: `/subpkg/allotCars/allotCars?orderId=${orderId}&orderNo=${orderNo}&allotType=${allotType}`,
})
},
},
computed: {
orderDate() {
return formatGMT(this.propData['massOrder.orderDate'], 'D') || ''
}
}
}
</script>
<style lang="scss">
.shipmentItem {
position: relative;
margin: 24rpx 24rpx 0;
padding: 0;
padding-bottom: 32rpx;
&/deep/.custom-cell-row {
margin-top: 20rpx;
}
.consumer_code {
line-height: 40rpx;
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipment-state {
font-size: 24rpx;
color: #F7A64A;
line-height: 33rpx;
padding: 6rpx 24rpx;
border-radius: 8rpx;
background: rgba(247, 166, 74, .1);
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipmenting {
color: #66CCCC;
background: rgba(61, 204, 185, .1);
}
.start-station,
.end-station {
margin: 20rpx 0;
font-size: 30rpx;
font-weight: bolder;
.start-icon,
.end-icon {
width: 40rpx;
height: 40rpx;
font-size: 24rpx;
padding: 8rpx;
border-radius: 50%;
color: #FFFFFF;
background-color: #2E75E6;
margin-right: 20rpx;
}
.end-icon {
background-color: #F7A64A;
}
}
.orderItemButtonGroup {
justify-content: space-between;
padding: 0 24rpx;
flex-wrap: wrap;
button {
font-size: 32rpx;
font-weight: 500;
color: #FFFFFF;
width: 315rpx;
height: 80rpx;
margin: 0;
border-radius: 44rpx;
background: linear-gradient(90deg, #2E75E6 0%, #5E58EE 100%);
box-shadow: 1rpx 3rpx 12rpx 0rpx rgba(0, 0, 0, 0.04);
}
}
}
</style>
\ No newline at end of file
<template>
<view class="orderTruckCard">
<view class="flex_center flex_sb">
<view class="orderTruckCardTruckNo">
<template v-if="isNewAllot">
{{(propData['name'] || '') + '&nbsp;&nbsp;&nbsp;'+(propData['facilityMode.name'] || '')}}
</template>
<template v-else>
{{(propData['truck.name'] || '') + '&nbsp;&nbsp;&nbsp;'+(propData['truck.specification'] || '')}}
</template>
</view>
<image style="width: 40rpx;height: 40rpx;" src="../../static/img/icons/deleteIcon.png" @click="onDeleteTruck(propData.id)"/>
</view>
<u-line customStyle="position:absolute;left:0;"></u-line>
<view class="flex_center flex_sb">
<view class="orderTruckCardContenxt flex">
<view>{{isNewAllot ? propData['driver1.name'] || '' : propData['driver.name'] || ''}}</view>
<view style="margin-left: 80rpx">
{{isNewAllot ? propData['driver1.tel'] || '' : propData['driver.tel'] || ''}}
</view>
</view>
<!-- <u-icon name="arrow-right" v-if="isNewAllot"/> -->
</view>
<u-line></u-line>
<view class="flex_center flex_sb">
<view class="orderTruckCardContenxt"> 最小趟次 </view>
<u-number-box v-model="minFrequency" integer>
<view class="minus" slot="minus"> - </view>
<view slot="input" class="flex_cen flex_center"
style="width: 100rpx; height:56rpx; text-align: center;">
<input v-model="minFrequency" type='number'>
</view>
<view class="plus" slot="plus"> + </view>
</u-number-box>
</view>
<u-line></u-line>
<view class="flex_center flex_sb">
<view class="orderTruckCardContenxt">最大趟次</view>
<u-number-box v-model="maxFrequency" integer>
<view class="minus" slot="minus"> - </view>
<view slot="input" class="flex_cen flex_center"
style="width: 100rpx; height:56rpx; text-align: center;">
<input v-model="maxFrequency" type='number'>
</view>
<view class="plus" slot="plus"> + </view>
</u-number-box>
</view>
<u-line></u-line>
<view class="flex_center flex_sb">
<view class="orderTruckCardContenxt" style="margin-right: 32rpx;">分配量</view>
<u-input type='number' border="none" v-model="alloutQty" placeholder="请填写分配量"/>
</view>
</view>
</template>
<script>
export default {
name: "orderTruckCard",
props: {
propData: {
type: Object,
default: {}
},
isNewAllot: {
type: Boolean,
default: false
}
},
data() {
return {
minFrequency: this.propData.minShipments || 1, //最小趟次
maxFrequency: this.propData.maxShipments || 1, //最大趟次
alloutQty: this.propData.splitMode === 'Weight' ? this.propData.loadWeight : this.propData.loadQty || 0, //分配量
};
},
methods: {
//删除订单已分配车型
onDeleteTruck(truckId) {
uni.$emit('onRemoveMassAssignTruck', truckId)
}
}
}
</script>
<style lang="scss">
.orderTruckCard {
padding: 0 24rpx;
background-color: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 24rpx;
position: relative;
.orderTruckCardTruckNo {
height: 114rpx;
color: #2E75E6;
line-height: 114rpx;
font-size: 32rpx;
font-weight: bolder;
}
.orderTruckCardContenxt {
height: 120rpx;
color: #434343;
line-height: 120rpx;
font-size: 28rpx;
}
.minus {
width: 56rpx;
height: 56rpx;
border: 1rpx solid #BFBFBF;
border-radius: 8rpx;
line-height: 48rpx;
text-align: center;
color: #BFBFBF;
font-weight: bolder;
}
.plus {
width: 56rpx;
height: 56rpx;
border: 1rpx solid #2E75E6;
background-color: #2E75E6;
border-radius: 8rpx;
line-height: 48rpx;
text-align: center;
color: #FFFFFF;
font-weight: bolder;
}
}
</style>
\ No newline at end of file
<template>
<scroll-view class="virtual-list" :style="{ height: `${height}px` }" @scroll="onScroll" :scroll-y="true">
<div class="virtual-list-placeholder" :style="{ height: `${placeholderHeight}px` }"></div>
<div class="virtual-list-viewport" :style="{ transform: `translate3d(0, ${viewportOffset}px, 0)` }">
<slot :items="visibleItems"></slot>
</div>
</scroll-view>
</template>
<script>
export default {
name: 'VirtualList',
props: {
height: {
type: Number,
required: true,
},
itemHeight: {
type: Number,
required: true,
},
items: {
type: Array,
required: true,
},
},
data() {
return {
scrollTop: 0,
viewportOffset: 0,
placeholderHeight: 0,
visibleRange: [0, 0],
visibleItems: [],
};
},
computed: {
viewportHeight() {
return this.height;
},
contentHeight() {
return this.items.length * this.itemHeight;
},
overscanCount() {
return Math.ceil(this.viewportHeight / this.itemHeight) + 1;
},
},
watch: {
items() {
this.updateViewport();
},
scrollTop(newVal) {
this.updateViewport();
this.viewportOffset = newVal;
},
},
mounted() {
this.updateViewport();
},
methods: {
onScroll(e) {
this.scrollTop = e.detail.scrollTop;
},
updateViewport() {
const { scrollTop, overscanCount } = this;
const visibleCount = Math.ceil(this.viewportHeight / this.itemHeight);
const contentCount = this.items.length;
const visibleTop = Math.max(Math.floor(scrollTop / this.itemHeight) - overscanCount, 0);
const visibleBottom = Math.min(visibleTop + visibleCount + overscanCount * 2, contentCount);
this.visibleRange = [visibleTop, visibleBottom];
this.visibleItems = this.items.slice(visibleTop, visibleBottom);
this.placeholderHeight = visibleTop * this.itemHeight;
},
},
};
</script>
<style scoped>
.virtual-list {
position: relative;
overflow: hidden;
}
.virtual-list-placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
pointer-events: none;
}
.virtual-list-viewport {
position: relative;
pointer-events: auto;
}
</style>
<template>
<view class="shipmentBriefInfo">
<bs-pageHeader-orderNo :shipmentNo="shipmentNo" />
<view class="primary-info routine_radius_card flex_center">
<view class="flex">
<view class="left-icon">
<view :class="['t-icon', stopsInfo.loadStop ? 't-icon-a-zhuang2x1' : 't-icon-a-xie2x1']" />
</view>
<view class="primary-desc flex_col">
<view style="margin-bottom: 8rpx;" class="font_bolder">{{stopsInfo.locationName}}</view>
<view>{{stopsInfo.locationAddress}}</view>
</view>
</view>
<view style="width: 100%;">
<bs-customCell label="货量 :" labelCol="2" wrapCol="10"
:value="stopsInfo.locationOrders + '单/' +stopsInfo.locationQty+ '件/' + stopsInfo.locationWeight+'千克/'+ stopsInfo.locationVolume+'方'" />
</view>
</view>
</view>
</template>
<script>
export default {
name: "shipmentBriefInfo",
options: { styleIsolation: 'shared' },
created: function() { //定义组件的生命周期
this.initData()
},
data() {
return {
stopsInfo: {}, //站点信息,
shipmentNo: '' //运单号
};
},
methods: {
initData() {
const appInstance = getApp()
const { stopsInfo, shipmentInfo } = appInstance.globalData
this.stopsInfo = stopsInfo
this.shipmentNo = shipmentInfo.shipmentNo
}
}
}
</script>
<style lang="scss">
.primary-info {
flex-wrap: wrap;
.left-icon {
position: relative;
margin-right: 20rpx;
.leftIcon-text {
font-size: 28rpx;
display: inline-block;
position: absolute;
top: 5px;
left: 12px;
}
.t-icon {
width: 70rpx;
height: 70rpx;
}
}
/deep/.custom-cell-row {
width: 100%;
margin-top: 32rpx;
color: #8C8C8C;
}
}
</style>
<template>
<view class="shipmentCard" v-if="isShow">
<view class="shipment-header flex_sb">
<view class="consumer_code flex_center">
<text selectable> 运单号:{{data.header.shipmentNo}}</text>
</view>
<view :class="data.header.shipmentStatus ==='Approved' ? 'shipment-state' : 'shipment-state shipmenting'">
{{data.header.shipmentStatusLabel}}
</view>
</view>
<!-- 主要运单信息 -->
<bs-customCell :label="'发运日期:'" :value="data.header.shipmentDate" labelCol="3" wrapCol="8" />
<!-- <bs-customCell label="运输时效 :" :value="data.header.shipTime + '天'" labelCol="3" wrapCol="8" /> -->
<!-- 司机信息 -->
<bs-customCell label="司机姓名:" :value="data.header['driver1.name']" labelCol="3" wrapCol="8" v-if="data.header['driver1.name']" />
<bs-customCell label="司机手机:" :value="data.header['driver1.tel']" labelCol="3" wrapCol="8" v-if="data.header['driver1.tel']" />
<bs-customCell label="车牌号:" :value="data.header['truck.name']" labelCol="3" wrapCol="8" v-if="data.header['truck.name']" />
<bs-customCell label="车型:" :value="data.header['truck.facilityMode.name']" labelCol="3" wrapCol="8" v-if="data.header['truck.facilityMode.name']" />
<bs-customCell label="装载率:" :value="data.header['loadRate'] + '%'" labelCol="3" wrapCol="8" v-if="!data.header['loadRate'] === 0" />
<bs-customCell label="货量:" labelCol="2" wrapCol="9"
:value="`${data.header.shipOrders}单 / ${data.header.shipQty}件 / ${data.header.shipWeight}千克 / ${data.header.shipVolume}方`" />
</view>
</template>
<script>
export default {
name: "shipmentCard",
options: { styleIsolation: 'shared' },
props: {
propData: {
type: String,
default: ""
},
},
created() {
if(this.propData){
this.data = JSON.parse(this.propData);
}
},
watch: {
propData: {
handler(newValue) {
this.data = JSON.parse(newValue);
this.$nextTick(() => {
this.isShow = false
this.isShow = true
})
}
},
},
data() {
return {
data: {},
isShow: true
}
}
}
</script>
<style lang="scss">
.shipmentCard {
background-color: #fff;
padding: 32rpx;
.consumer_code {
line-height: 40rpx;
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipment-state {
font-size: 24rpx;
color: #F7A64A;
line-height: 33rpx;
padding: 6rpx 24rpx;
border-radius: 8rpx;
background: rgba(247, 166, 74, .1);
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipmenting {
color: #66CCCC;
background: rgba(61, 204, 185, .1);
}
}
</style>
<template>
<view class="shipmentItem box_shadow_card" @click="navToShipmentDetail">
<view class="shipment-header flex_sb" style="padding: 28rpx 24rpx 0;">
<view class="consumer_code flex_center">
<text selectable> 运单号:{{propData.shipmentNo}}</text>
</view>
<view :class="propData.shipmentStatus ==='Approved' ? 'shipment-state' : 'shipment-state shipmenting'">
{{propData.shipmentStatusLabel}}
</view>
</view>
<u-line color="#E5E5E5" margin="20rpx 0" />
<view style="padding: 0 24rpx 32rpx;">
<view class="stations">
<view class="start-station flex_center">
<view class="t-icon-start-sddress t-icon" style="margin-right: 8rpx;"></view>
{{propData['original.name'] || ''}}
</view>
<view class="end-station flex_center">
<view class="t-icon-end-sddress t-icon" style="margin-right: 8rpx;"></view>
{{propData['destination.name'] || ''}}
</view>
</view>
<bs-customCell label="发运日期 :" :value="propData.shipmentDate" labelCol="3" wrapCol="6" />
<!-- <customCell label="运输时效" :value="propData.shipTime || '0天'" labelCol="4" wrapCol="6" /> -->
<template v-if="0">
<bs-customCell label="司机名" :value="propData['driver1.tel']" labelCol="2" wrapCol="10" />
<bs-customCell label="车牌号" :value="propData['truck.name']" labelCol="2" wrapCol="10" />
</template>
<bs-customCell label="货量 :" labelCol="2" wrapCol="10" :value="`${propData.shipOrders}单 / ${propData.shipQty}件 / ${propData.shipWeight}千克 / ${propData.shipVolume}方`" />
<template v-if="propData.shipmentStatus ==='Approving'">
<view class="operationBtn flex_center flex_cen" @click.stop="onClick(propData.id, 'approve')">确认</view>
</template>
<template v-if="propData.shipmentStatus ==='Approved'">
<view class="operationBtn flex_center flex_cen" @click.stop="onClick(propData.id, 'approveCancel')">取消确认</view>
</template>
</view>
</view>
</template>
<script>
import {shipmentDispatch} from '../../api/apiList.js'
export default {
name: 'shipmentItem',
options: { styleIsolation: 'shared' },
props: {
propData: {
type: Object,
default: {}
},
},
methods: {
navToShipmentDetail() {
this.onSetGlobalData()
const shipmentId = this.propData.id
wx.navigateTo({
url: `/subpkg/shipmentDetail/shipmentDetail?id=${shipmentId}`,
})
},
//设置全局变量
onSetGlobalData() {
const appInstance = getApp()
appInstance.globalData.shipmentInfo = { //设置全局变量
shipmentNo: this.propData.shipmentNo,
shipmentId: this.propData.id,
shipmentBizName: this.propData.bizName,
}
},
onClick(shipmentId, operationType){
const reqData = {
"args": {
"selectedIds": [shipmentId]
}
}
shipmentDispatch(operationType, reqData).then(res => {
if (res.data.messageType === 'success') {
uni.showToast({
title: '操作成功',
duration: 1500
})
setTimeout(() => {
this.$emit('onReload')
}, 1500)
}
})
}
}
}
</script>
<style lang="scss">
.shipmentItem {
position: relative;
margin: 24rpx 24rpx 0;
padding: 0;
&/deep/.custom-cell-row {
margin-top: 20rpx;
}
.consumer_code {
line-height: 40rpx;
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipment-state {
font-size: 24rpx;
color: #F7A64A;
line-height: 33rpx;
padding: 6rpx 24rpx;
border-radius: 8rpx;
background: rgba(247, 166, 74, .1);
text-shadow: 1rpx 3rpx 12rpx rgba(0, 0, 0, 0.04);
}
.shipmenting {
color: #66CCCC;
background: rgba(61, 204, 185, .1);
}
.start-station,
.end-station {
margin: 20rpx 0;
font-size: 30rpx;
font-weight: bolder;
.start-icon,
.end-icon {
width: 40rpx;
height: 40rpx;
font-size: 24rpx;
padding: 8rpx;
border-radius: 50%;
color: #FFFFFF;
background-color: #2E75E6;
margin-right: 20rpx;
}
.end-icon {
background-color: #F7A64A;
}
}
.operationBtn {
color: #FFFFFF;
padding: 20rpx;
margin-top: 32rpx;
border-radius: 44rpx;
box-shadow: 1rpx 3rpx 12rpx 0px rgba(0, 0, 0, 0.04);
background: linear-gradient(90deg, #2E75E6 0%, #5E58EE 100%);
width: 100%;
}
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>
import App from './App'
// #ifndef VUE3
import Vue from 'vue'
// #ifdef H5
import jwx from 'jweixin-module' //H5页面引入微信JSSDK,并全局挂载
Vue.prototype.$jwx = jwx
// #endif
// 引入uView并配置
import uView from 'uview-ui'
Vue.use(uView)
// 修改uView内置配置方案: 需要在Vue.use(uView)之后执行,目前只建议修改config、props属性, 除非您清楚知道自己的修改所带来的影响。
uni.$u.setConfig({
// 修改$u.config对象的属性
config: {
// 修改默认单位为rpx,相当于执行 uni.$u.config.unit = 'rpx'
unit: 'rpx'
},
// 修改$u.props对象的属性
props: {
// 修改radio组件的size参数的默认值,相当于执行 uni.$u.props.radio.size = 30
radio: {
size: 15
}
// 其他组件属性配置
// ......
}
})
Vue.prototype.getLocationCity = (cityInfo)=>{
const addressIsEmpty = (add)=>{
if (add) return add
return ""
}
let cityStr = ""
cityStr += addressIsEmpty(cityInfo['location.province'])
if (cityInfo['location.province'] !== cityInfo['location.city']){
cityStr += addressIsEmpty(cityInfo['location.city'])
}
cityStr += addressIsEmpty(cityInfo['location.district']) + addressIsEmpty(cityInfo['location.detail'])
return cityStr
}
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import {
createSSRApp
} from 'vue'
export function createApp() {
const app = createSSRApp(App)
return {
app
}
}
// #endif
{
"name" : "运匠运输管理系统",
"appid" : "__UNI__2878006",
"description" : "TMS-承运商",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* 模块配置 */
"modules" : {},
/* 应用发布信息 */
"distribute" : {
/* android打包配置 */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "wxd7383e6db760127c",
"setting" : {
"urlCheck" : false,
"es6" : true,
"postcss" : false
},
"usingComponents" : true,
"lazyCodeLoading" : "requiredComponents",
"requiredPrivateInfos" : [ "getLocation" ],
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于中转换车等上报操作"
},
"scope.camera" : {
"desc" : "该操作仅用于OCR识别时拍照辅助定位"
}
}
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"devServer" : {
"https" : false,
"disableHostCheck" : true,
"port" : 8080,
"proxy" : {
"/api" : {
"target" : "https://miniapp.logwirecloud.com",
// "target": "http://192.168.1.199:7080",
"changeOrigin" : true,
"secure" : false
},
"/handler" : {
"target" : "https://miniapp.logwirecloud.com",
// "target": "http://192.168.1.199:7080",
"changeOrigin" : true,
"secure" : false
},
"/sandtable" : {
"target" : "https://miniapp.logwirecloud.com",
// "target": "http://192.168.1.199:7080",
"changeOrigin" : true,
"secure" : false
},
"/tmsca" : {
"target" : "https://miniapp.logwirecloud.com",
// "target": "http://192.168.1.199:7080",
"changeOrigin" : true,
"secure" : false
}
}
},
"router" : {
"mode" : "hash",
"base" : "./"
},
"template" : "template.h5.html",
"sdkConfigs" : {
"maps" : {}
}
}
}
import AMap from '../utils/AMapUtil'; //高德工具类
import BMap from '../utils/BMapUtil'; //百度工具类
import { formatTime } from './../utils/util.js';
import { getMap, userMobileOperation } from '../api/apiList.js'
// #ifdef MP
import AMapSDK from "../utils/MapSDK/amap-wx.130"
import BMapSDK from '../utils/MapSDK/bmap-wx.min.js'
// #endif
// #ifdef H5
import AMapLoader from "@amap/amap-jsapi-loader"; //高德
// #endif
module.exports = {
data() {
return {
locationInfo: {}, //用户当前位置信息
isLocation: true, //是否获取位置中
};
},
//事件处理函数
methods: {
//初始化地图服务
async initMapService() {
await this.initMapInstance()
// #ifdef H5
const isWxBrowser = getApp().isWxBrowser()
if (isWxBrowser) {
await this.initWXH5SDK() //H5时初始化微信JSSDK, 以调用扫码、获取经纬度等功能
}
// #endif
return Promise.resolve('done');
},
//获取地图密钥并初始化地图实例, 用于转换接口获取的经纬度为文字描述, 小程序返回MapInstance H5返回MapContext
initMapInstance() {
const _this = this
return new Promise((resolve, reject) => {
getMap().then(res => {
//参数说明: 微信小程序KEY: wechatKey WEB地图KEY: jsKey WEB地图密钥: securityJsCode 地图服务商:BaiduMap/AMap(默认使用AMap高德服务)
const { wechatKey, jsKey, securityJsCode, serviceProvider } = res.data.data || {}
getApp().globalData.serviceProvider = serviceProvider
// #ifdef MP
if (!wechatKey) {
_this.showMapTost('请在BO系统集成中配置地图的Wechat Key')
} else {
let newMap = null
if(serviceProvider === 'AMap'){
newMap = new AMapSDK.AMapWX({ key: wechatKey }) //高德地图Key
} else {
newMap = new BMapSDK.BMapWX({ ak: wechatKey }); //百度地图key
}
getApp().globalData.MapIns = newMap
resolve(newMap);
}
// #endif
// #ifdef H5
if (!jsKey) {
_this.showMapTost('请在BO系统集成中配置地图的JS API Key')
} else if (!securityJsCode && serviceProvider === 'AMap') { //高德无需Security秘钥
_this.showMapTost('请在BO系统集成中配置地图的JS Security')
} else {
if(serviceProvider === 'AMap'){
window._AMapSecurityConfig = { securityJsCode}
AMapLoader.load({
"key": jsKey, // 申请好的Web端开发者Key,首次调用 load 时必填
"version": "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
"plugins": ["AMap.Marker", "AMap.Pixel","AMap.Geocoder","AMap.Geolocation"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then(async (AMapCtx) => {
getApp().globalData.MapIns = AMapCtx
resolve(AMapCtx);
}).catch(err => {
_this.showMapTost(err, 'errrr')
});
} else {
const BMap_URL = `https://api.map.baidu.com/api?v=2.0&ak=${jsKey}&callback=onBMapCallback`;
// 插入script脚本
let scriptNode = document.createElement("script");
scriptNode.type="text/javascript";
scriptNode.setAttribute("src", BMap_URL);
document.body.appendChild(scriptNode);
// 百度地图异步加载回调处理
window.onBMapCallback = function() {
getApp().globalData.MapIns = window.BMap
resolve(window.BMap);
};
}
}
// #endif
// APP && PDA端需引入指定定位SDK, 暂未兼容地图及定位服务
// #ifdef APP-PLUS
// #endif
})
})
},
//弹窗提示
showMapTost(tostText) {
uni.showToast({
title: tostText,
icon: 'none',
duration: 3000
})
},
//初始化微信JSSDK
async initWXH5SDK() {
const { appId, timestamp, nonceStr, sign } = await this.getJwxConfig()
await this.setJwxConfig(appId, timestamp, nonceStr, sign)
return Promise.resolve('done');
},
//获取JSSDK配置签名
getJwxConfig() {
return new Promise(resolve => {
let url = window.location.href.split('#')[0] //当前页面域名 botms.logwirecloud.com, 相同url无需重复获取
const { projectMini } = getApp().globalData
const data = { args: { entryName: projectMini, url } }
userMobileOperation('querySignature', data).then(res => { //获取签名
if (res.data.data) {
const { appId, timestamp, nonceStr, sign } = res.data.data
resolve({ appId, timestamp, nonceStr, sign })
}
})
})
},
//配置JS-SDK
setJwxConfig(appId, timestamp, nonceStr, signature) {
return new Promise((resolve, reject) => {
this.$jwx.config({ //JWX 已在main.js 中全局挂载
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature: signature, // 必填,签名
jsApiList: ['scanQRCode', 'getLocation', 'openLocation','chooseImage'] // 必填,需要使用的JS接口列表
});
this.$jwx.ready(() => {
this.$jwx = this.$jwx //ready 完成后供全局调用
resolve(this.$jwx)
});
this.$jwx.error(err => {
console.log('微信JSSDK实例化失败: ' + err.errMsg)
reject(err)
})
})
},
//获取用户授权和当前位置 关于uni.getLocation(): 使用该接口需要在Manifest.json中配置相关地图Key,由于为明文故弃用
getUserSetting() {
this.isLocation = false
const { serviceProvider } = getApp().globalData
// #ifdef H5
const callBack = (res) => {
const { latitude, longitude } = res
getApp().globalData.userLocation = { latitude, longitude } //H5转换坐标时使用
this.getSiteName() //H5授权后手动设置一次当前位置信息
}
const isWxBrowser = getApp().isWxBrowser() //判断是否为微信H5
if (isWxBrowser && this.$jwx) {
this.$jwx.getLocation({ //H5时使用JSSDK获取经纬度,自动弹出授权对话框,真机仅弹出一次
type: 'gcj02',
success: (res) => {
callBack(res)
}
})
} else { //WEB浏览器
if(serviceProvider === 'AMap'){
AMap.AMapGetLocation().then(res => callBack(res))
} else {
BMap.BMapGetLocation().then(res => callBack(res))
}
}
// #endif
// #ifdef MP
uni.getSetting({ //小程序中判断用户是否授权位置权限
success: (res) => {
if (!res.authSetting['scope.userLocation']) {
uni.authorize({
scope: 'scope.userLocation',
success: () => {
this.getSiteName();
},
fail: () => {
this.isLocation = true
uni.showModal({
title: '提示',
content: '缺失定位信息,前往系统设置开启定位权限!',
success: (res) => {
if (res.confirm) {
uni.openSetting({
success: () => {
this.getUserSetting()
}
});
}
}
});
}
});
} else {
this.getSiteName();
}
}
});
// #endif
},
//获取当前位置信息
getSiteName() {
const { serviceProvider } = getApp().globalData
const newMapUtil = serviceProvider === 'AMap' ? AMap : BMap
newMapUtil.reverseGeocoder()
.then((res) => {
const address = res.textData.desc;
const time = formatTime(new Date());
const info = { ...res, address, time };
getApp().globalData.userLocation = info;
this.locationInfo = info
this.isLocation = true
})
.catch((err) => {
this.isLocation = true
uni.showToast({
title: err,
icon: 'none',
duration: 3000
});
});
}
}
};
\ No newline at end of file
import { userLogin,authLogin, updateLocation } from "../api/apiList";
import { APP_ID, ENV_MODEL, TokenPrefix } from "../publicConfig/config";
module.exports = {
methods: {
//获取微信token
getWxToken() {
uni.login({
success: (res) => {
this.login(res.code);
},
});
},
// 获取H5 授权Code
getH5Code() {
//是否重新获取授权
let code = this.getQueryString("code");
let time = new Date().getTime();
if (code) {
return {
code,
time,
};
} else {
this.onRedirect(); //重定向获取Code
}
},
//重定向授权 用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。
onRedirect() {
let href = window.location.href;
if (href.includes("?")) {
//防止重定向地址重复拼接
href = href.split("?")[0];
}
let url = encodeURIComponent(href);
let appId = "";
if (process.env.NODE_ENV === "development") {
appId = APP_ID.h5Dev;
} else {
switch (ENV_MODEL.model) {
case "UAT":
appId = APP_ID.h5Uat;
break;
default:
appId = APP_ID.h5Prod;
}
}
let HREF = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${url}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`;
window.location.href = HREF;
},
//用户登录
login(code) {
const _this = this;
if (!uni.getStorageSync("SearchHistory")) {
uni.setStorageSync("SearchHistory", []);
}
const { projectMini } = getApp().globalData;
let { pagePath, route } = this.getPathRoute(1); //获取跳转前当前页面完整路径及路由
try {
// #ifdef H5
pagePath = window.location.href.split("#")[1]; //获取跳转前当前页面完整路径及路由
route = pagePath.split("?")[0];
// #endif
//登录Action: loginByUserInfo已与手机号验证码登录实现互通, 故自动认证时默认为loginByUserInfo
let reqData = { entryName: projectMini }
authLogin("loginByUserInfo", code, reqData).then(res => {
let XSRFToken = "";
//#ifdef MP
if (res.header["Set-Cookie"]) {
XSRFToken = res.header["Set-Cookie"].split(";")[0].split("=")[1];
uni.setStorageSync(`${TokenPrefix}_XSRFToken`, XSRFToken);
}
//#endif
// #ifdef H5
XSRFToken = _this.getCookies("XSRF-TOKEN");
uni.setStorageSync(`${TokenPrefix}_XSRFToken`, XSRFToken);
// #endif
const messageType = res.data.messageType;
if (messageType === "success") {
let token = res.data.renewedToken;
uni.setStorageSync(`${TokenPrefix}_token`, token);
// #ifdef MP
uni.setStorageSync("cookies", res.cookies);
// #endif
if (route && route !== "pages/login/login" && route !== "/") {
uni.reLaunch({
url: pagePath,
}); //可跳转至任意页面
} else {
uni.switchTab({
url: "/pages/index/index",
});
}
// #ifdef MP
// _this.onUpdateLocation()
// #endif
}
}).catch((err) => {
if (route && route !== "pages/login/login" && route !== "/") {
//非登录页认证,则跳转至登录页面
getApp().globalData.backPagePath = pagePath; //当需要跳转登录页面,跳转前记录当前页面,作为回退页面
uni.navigateTo({
url: "/pages/login/login",
});
}
uni.showToast({
title: err.message,
icon: "none",
duration: 3000,
});
});
} catch (err) {
//非登录页认证,则跳转至登录页面
getApp().globalData.backPagePath = pagePath; //当需要跳转登录页面,跳转前记录当前页面,作为回退页面
uni.navigateTo({
url: "/pages/login/login",
});
}
},
//获取页面完整路径及路由, pageIndex为页面索引,
getPathRoute(pageIndex) {
let pagePath = "";
let route = ""; //当前页面完整路径及路由
const pages = getCurrentPages(); // 获取所有页面实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。在onLaunch时获取为空,因为页面还未加载
const currentPage = pages[pages.length - pageIndex]; //获取当前页面实例
if (currentPage) {
pagePath = currentPage.$page.fullPath;
route = currentPage.route;
}
return {
pagePath,
route,
};
},
// 开始地理位置监听
onUpdateLocation() {
uni.startLocationUpdateBackground({
success: (res) => {
console.log("开启小程序接收位置消息成功");
uni.onLocationChange(function (res) {
const { longitude, latitude, speed } = res;
const timestamp = new Date().getTime();
const data = {
aux: {
map: {
gpsDevice: {
deviceId: "沪TESTM01",
deviceType: "MiniApp",
},
gpsDataList: [
{
longitude,
latitude,
speed,
timestamp,
},
],
},
},
};
updateLocation(data);
});
},
fail: (err) => console.log("开启小程序接收位置消息失败:", err),
});
},
getCookies(cName) {
// 调用函数let cookie_val = getCookie(cookie的名字);
var name = cName + "=";
var ca = document.cookie.split(";");
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
}
return "";
},
getQueryString(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
let r = window.location.search.substr(1).match(reg); //获取url中"?"符后的字符串并正则匹配
let context = "";
if (r != null) context = decodeURIComponent(r[2]);
reg = null;
r = null;
return context == null || context == "" || context == "undefined"
? ""
: context;
},
},
};
import { uploadImg } from '../api/apiList';
module.exports = {
data() {
return {
fileList: [], //文件列表
fileDetailList: [], //文件列表, 含文件名等
isIos: false,
visible: false ,//是否显示自定义相机
};
},
created() {
// #ifdef H5
const platform = uni.getSystemInfoSync().platform
if (platform === 'ios') {
// this.isIos = true
}
// #endif
},
//事件处理函数
methods: {
//图片上传 IOS兼容
onUpLoad() {
if (this.cameraType) {
this.visible = true //跳转自定义相机
} else if (this.$jwx) {
const _this = this
const sourceType = this.isIos ? ['album'] : ['album', 'camera'] // 可以指定来源是相册还是相机, ios指定为仅相册
this.$jwx.chooseImage({
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: sourceType,
success: function(res) {
_this.onConvert(res.localIds)
}
})
}
},
async onConvert(localIds) {
for (var i = 0; i < localIds.length; i++) {
await this.onReadImage(localIds[i]);
}
},
onReadImage(localId) {
const _this = this
return new Promise((resolve, reject) => {
_this.$jwx.getLocalImgData({
localId: localId, // 图片的localID
success: function(res) {
const localData = res.localData
_this.fileList = _this.fileList.concat(localData)
resolve('done!')
}
});
})
},
//上传图片
uploadImage(callback) {
let { fileList } = this;
if (!fileList.length && !this.required) return callback('');
if (!fileList.length) {
uni.showToast({
title: `请选择${this.uploadTitle}`,
icon: 'error',
duration: 3000
});
return;
}
let images = [];
fileList.forEach(item => {
const file = this.fileDetailList.find(file => file.url === item)
uploadImg(item, file.type).then(res => { //微信小程序仅支持单文件上传,传多个文件需要反复调用本API。所以跨端的写法就是循环调用本API。
uni.hideLoading();
let data = JSON.parse(res).data;
images.push(data.path);
if (images.length === fileList.length) {
return callback(images.join('|'));
}
})
});
},
// 上传文件回调
uploadAfterRead(info) {
let { file } = info;
const newFile = file.map(item => item.url);
this.fileList = [...this.fileList, ...newFile]
this.fileDetailList = [...this.fileDetailList, ...file]
},
//拍照确认回调
customCameraFinish(imgSrc){
this.visible = false
this.fileList = [...this.fileList, imgSrc]
this.fileDetailList = [...this.fileDetailList, {url: imgSrc, type: 'image'}]
this.$emit('customUploadAfter', {url: imgSrc, type: 'image', cameraType: this.cameraType}) //自定义上传回调
},
// 预览图片回调
previewImage(e) {
let { index } = e.currentTarget.dataset;
uni.previewImage({
current: index,
urls: this.fileList
})
},
// 删除当前选中文件
deleteFile(index) {
uni.showModal({
title: '删除提示',
content: '是否确认删除?',
success: (res) => {
if (res.confirm) {
this.fileList = this.fileList.filter((item,idx) => idx !== index)
this.fileDetailList = this.fileDetailList.filter((item,idx) => idx !== index)
} else if (res.cancel) {}
}
});
},
}
};
{
"name": "biz-trina5-app",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"uview-ui": {
"version": "2.0.31",
"resolved": "https://registry.npmjs.org/uview-ui/-/uview-ui-2.0.31.tgz",
"integrity": "sha512-I/0fGuvtiKHH/mBb864SGYk+SJ7WaF32tsBgYgeBOsxlUp+Th+Ac2tgz2cTvsQJl6eZYWsKZ3ixiSXCAcxZ8Sw=="
}
}
}
{
"name": "biz-trina5-app",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"image-tools": "^1.4.0",
"jweixin-module": "^1.6.0",
"uview-ui": "^2.0.31",
"vconsole": "^3.15.0"
}
}
{
"easycom": {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"bs-(.*)": "@/components/baseComponents/bs-$1/bs-$1.vue"
}
},
"pages": [{
"path": "pages/login/login",
"style": {
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"disableScroll": true
}
}, {
"path": "pages/index/index",
"style": {
"enablePullDownRefresh": true
}
}, {
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "个人中心",
"enablePullDownRefresh": false,
"disableScroll": true
}
}, {
"path": "pages/order/order",
"style": {
"enablePullDownRefresh": false
}
}],
"subPackages": [{
"root": "subpkg",
"pages": [{
"path": "editPwd/editPwd",
"style": {
"navigationBarTitleText": "修改密码",
"enablePullDownRefresh": false
}
}, {
"path": "shipmentDetail/shipmentDetail",
"style": {
"navigationBarTitleText": "运单详情",
"enablePullDownRefresh": false
}
}, {
"path": "allotDetail/allotDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "allotTruck/allotTruck",
"style": {
"navigationBarTitleText": "配车",
"enablePullDownRefresh": false
}
}, {
"path": "orderDetail/orderDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "transferChange/transferChange",
"style": {
"navigationBarTitleText": "中转换车",
"enablePullDownRefresh": false
}
}, {
"path": "transferAddressDetail/transferAddressDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "viewDriverTruck/viewDriverTruck",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "reserveDock/reserveDock",
"style": {
"navigationBarTitleText": "预约站点",
"enablePullDownRefresh": false
}
}, {
"path": "reserveDockTime/reserveDockTime",
"style": {
"navigationBarTitleText": "预约到站时间",
"enablePullDownRefresh": false
}
}, {
"path": "registerDriver/registerDriver",
"style": {
"navigationBarTitleText": "司机注册",
"enablePullDownRefresh": false
}
}, {
"path": "registerTruck/registerTruck",
"style": {
"navigationBarTitleText": "车辆注册",
"enablePullDownRefresh": false
}
}, {
"path": "viewDriverTruckDetail/viewDriverTruckDetail",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "signFor/signFor",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"disableScroll": false
}
}, {
"path": "checkTruck/checkTruck",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "allotCars/allotCars",
"style": {
"navigationBarTitleText": "配车确认",
"enablePullDownRefresh": false
}
}, {
"path": "choiceTruck/choiceTruck",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}, {
"path": "shipmentLbs/shipmentLbs",
"style": {
"navigationBarTitleText": "运单轨迹",
"enablePullDownRefresh": false,
"disableScroll": true
}
}]
}],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "天合光能",
"navigationBarBackgroundColor": "#2E75E6",
"backgroundColor": "#fff",
"app-plus": {
"background": "#efeff4"
},
"h5": {
"navigationStyle": "custom"
}
},
"tabBar": {
"color": "#999999",
"selectedColor": "#2E75E6",
"backgroundColor": "#fff",
"list": [{
"pagePath": "pages/index/index",
"text": "运单",
"iconPath": "static/img/tabbar/shipment.png",
"selectedIconPath": "static/img/tabbar/shipment-active.png"
}, {
"pagePath": "pages/order/order",
"text": "订单",
"iconPath": "static/img/tabbar/order.png",
"selectedIconPath": "static/img/tabbar/order-active.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "static/img/tabbar/user.png",
"selectedIconPath": "static/img/tabbar/user-active.png"
}
]
}
}
<template>
<view class="shipment-order-list content_box">
<shipmentItem v-for="item in shipmentOrders" :key="item.id" :propData="item" @onReload="onReload"/>
<!-- 空状态 -->
<view class="emptyImg flex_col" v-if="!shipmentOrders.length">
<image mode="aspectFit" src="../../static/img/empty/shipmentEmpty.png" />
<text class="empty-text">暂时没有运单</text>
</view>
</view>
</template>
<script>
import { findShipmentOrders } from '@/api/apiList.js'
import { formatGMT } from '@/utils/util.js'
import { TokenPrefix } from '../../publicConfig/config.js'
const getUserLocation = require('../../mixins/getUserLocation')
export default {
mixins: [getUserLocation],
data() {
return {
shipmentOrders: [], //运输订单
pageNum: 1, //页码
pageSize: 10, //每页条数
loadMore: true, //加载更多
}
},
onLoad(options) {
const time = setInterval(() => {
let XSRFToken = uni.getStorageSync(`${TokenPrefix}_XSRFToken`)
if (XSRFToken) {
this.initData()
this.initMapService() //初始化地图
clearInterval(time)
}
}, 1000);
},
//下拉刷新
onPullDownRefresh() {
this.onReload()
},
// 触底加载更多
onReachBottom() {
if (this.loadMore) {
this.initData();
this.loadMore = false
}
},
methods: {
//初始化数据
initData() {
const { pageNum, pageSize } = this
let data = {
"args": { pageNum, pageSize }
}
findShipmentOrders(data).then(res => {
uni.stopPullDownRefresh()
const data = res.data.data.datas
if (data.length > 0) {
data.map(item => { //格式化时间
if (item.shipmentDate) {
item.shipmentDate = formatGMT(item.shipmentDate, 'D')
}
})
this.shipmentOrders = this.shipmentOrders.concat(data)
this.pageNum += 1
this.loadMore = true
}
})
},
// 刷新页面
onReload() {
this.pageNum = 1
this.shipmentOrders = []
this.initData()
},
}
}
</script>
<style lang="scss">
.shipment-order-list {
.emptyImg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.empty-text {
font-size: 28rpx;
text-align: center;
}
}
}
</style>
This diff is collapsed.
<template>
<view class="orderList content_box">
<orderItem v-for="( order, index ) in orderList" :propData="order" :key="index"/>
<!-- 空状态 -->
<view class="emptyImg flex_col" v-if="!orderList.length">
<image mode="aspectFit" src="../../static/img/empty/shipmentEmpty.png" />
<text class="empty-text">暂时没有订单</text>
</view>
</view>
</template>
<script>
import { getMassOrderList} from '../../api/apiList.js'
export default {
data() {
return {
pageNum: 1, //页码
pageSize: 10, //每页条数
orderList: [],
loadMore: true, //加载更多
};
},
onLoad() {
this.initData()
},
//下拉刷新
onPullDownRefresh() {
this.pageNum = 1
this.orderList = []
this.initData()
},
// 触底加载更多
onReachBottom() {
if (this.loadMore) {
this.initData()
this.loadMore = false
}
},
methods:{
initData(){
const { pageNum, pageSize } = this
let data = {
"args": { pageNum, pageSize }
}
getMassOrderList(data).then(res=>{
uni.stopPullDownRefresh()
const data = res.data.data.datas
if (data.length > 0) {
this.orderList = this.orderList.concat(data)
this.pageNum += 1
this.loadMore = true
}
})
}
}
}
</script>
<style lang="scss">
.orderList {
.emptyImg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
.empty-text {
font-size: 28rpx;
text-align: center;
}
}
}
</style>
<template>
<view class="user-content content_box">
<view class="user-header">
<image class="header-bg" src="../../static/img/bg/userBg.png" />
<view class="user-info flex">
<view class="user_photo">
<image src="../../static/img/empty/userPhoto.png"></image>
</view>
<view class="flex_col flex_cen" style="margin-left: 30rpx;">
<text class="user-name" user-select>{{userInfo.name}}</text>
<text class="user-phone" user-select>{{userInfo.mobile}}</text>
</view>
</view>
</view>
<!-- 修改密码 -->
<view class="edit-password ">
<u-cell-group :border="false">
<u-cell title-style="font-weight: bolder" rightIconStyle="font-size: 40rpx" isLink :border="false"
title="我的司机" url="/subpkg/viewDriverTruck/viewDriverTruck?action=driver">
<view slot="icon" class="t-icon t-icon-driver" />
</u-cell>
<u-line color="#F0F0F0" />
<u-cell title-style="font-weight: bolder" rightIconStyle="font-size: 40rpx" isLink :border="false"
title="我的车辆" url="/subpkg/viewDriverTruck/viewDriverTruck?action=truck">
<view slot="icon" class="t-icon t-icon-truck" />
</u-cell>
<u-line color="#F0F0F0" />
<u-cell title-style="font-weight: bolder" rightIconStyle="font-size: 40rpx" isLink :border="false"
title="修改密码" :url="'/subpkg/editPwd/editPwd?userName=' + userInfo.name">
<view slot="icon" class="t-icon t-icon-lock" />
</u-cell>
</u-cell-group>
</view>
<button class="exit-login flex_cen" @click="exitLog">退出登录</button>
</view>
</template>
<script>
import { userMobileOperation } from '../../api/apiList.js'
import { TokenPrefix } from '../../publicConfig/config.js'
export default {
options: { styleIsolation: 'shared' },
data() {
return {
userInfo: {}
}
},
onLoad: function(options) {
this.getUserInfo()
},
methods: {
//获取用户信息
getUserInfo() {
userMobileOperation('getUserInfo').then(res => {
let data = res.data.data
data.mobile = data.userExt.mobile || '暂无'
this.userInfo = { ...data }
})
},
//退出登录
exitLog() {
const _this = this
uni.showModal({
content: '确定退出当前用户吗?',
success: (res) => {
if (res.confirm) {
// #ifdef H5
const isWxBrowser = getApp().isWxBrowser()
if(!isWxBrowser){ //非微信环境跳出
_this.customTost()
return
}
// #endif
const { projectMini } = getApp().globalData
const openId = this.userInfo.userExt?.openid
const data = {openId, entryName: projectMini}
userMobileOperation('logout', data).then(res => {
if (res.data.messageType === 'success') {
_this.customTost()
}
})
} else if (res.cancel) {}
}
})
},
customTost(){
uni.showToast({
title: '退出成功!',
duration: 1000
})
uni.removeStorageSync(`${TokenPrefix}_token`)
uni.removeStorageSync(`${TokenPrefix}_XSRFToken`)
uni.removeStorageSync('cookies')
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login',
})
}, 1500)
}
}
}
</script>
<style lang="scss" scoped>
.user-content {
.user-header {
height: 288rpx;
.header-bg {
width: 100%;
height: 100%;
}
.user-info {
position: absolute;
top: 5%;
left: 4%;
font-size: 36rpx;
color: #FFFFFF;
.user_photo{
width: 160rpx;
height: 160rpx;
border: 2rpx solid #EDF5FF;
border-radius: 50%;
background-color: #DBE2FB;
image{
width: 158rpx;
height: 158rpx;
background-size: 100%;
background-position: center;
background-repeat: repeat;
}
}
.user-name {
margin-bottom: 10rpx;
}
}
}
/deep/.edit-password {
background: #FFFFFF;
padding: 40rpx;
.u-cell__body {
padding: 0;
background-color: #FFFFFF !important;
}
.u-line{
margin: 16rpx 0 !important;
}
.t-icon {
width: 60rpx;
height: 60rpx;
margin-right: 20rpx;
}
}
.exit-login {
color: #8C8C8C;
position: fixed;
/* #ifdef H5 */
bottom: 140rpx;
/* #endif */
/* #ifdef MP */
bottom: 40rpx;
/* #endif */
width: 686rpx;
height: 96rpx;
left: 50%;
transform: translateX(-50%);
border-radius: 48rpx;
border: 2rpx solid #8C8C8C;
line-height: 96rpx;
}
}
</style>
/*
BASEURL: 说明
1. 本地开发调试: 小程序修改该url即可;H5则无需关心,H5修改manifest.json中h5相关Proxy配置即可
2. 打包生产模式: 小程序切换url; H5切换ENV_MODEL
*/
let BASEURL = {
url: "http://127.0.0.1:8080", //TMS
}
//当使用多域名代理服务时生效,用于统一添加API前缀以区分服务代理地址。本地开发H5注意修改manifest.json中Proxy代理规则
// const APIPrefix = '' //TMS项目
const APIPrefix = '' //体验版测试环境: Test Master
// const APIPrefix = '/tmsdemo' //正式版环境: TMS Demo
// const APIPrefix = '/sandtable' //沙盘演示项目
// const APIPrefix = '/tmsca' //三方物流项目
//H5打包环境区分:打包时手动切换,添加新的环境变量时,注意同步修改APP.vue 和loginBehavior.js中环境判断逻辑
const ENV_MODEL = {
model: 'UAT', //测试环境
//model: 'PROD', //生产环境
}
//用于数据请求时替换所有API接口的{mini}
let MINI = {
'h5Prod': 'biz-trina5-app', //生产项目名
'h5Uat': 'biz-trina5-app', //测试项目名
'h5Dev': 'biz-trina5-app', //开发项目名
'web': 'biz-trina5-app',
'wxApp': 'biz-trina5-app', //小程序项目名
}
//仅用于H5换取用户授权Code使用,后端可用此Code换取用户OpenId等信息
let APP_ID = {
'h5Prod': 'wxd7383e6db760127c' ,//生产公众号appid
'h5Uat': 'wxd7383e6db760127c' ,//测试公众号appid
'h5Dev': 'wxd7383e6db760127c' //开发公众号appid
}
const TokenPrefix = 'carrier' // Token && XSRFToken 前缀, 避免运行于WEB端时,打开多端Token覆盖、混淆问题
module.exports = {
BASEURL,
MINI,
APP_ID,
ENV_MODEL,
TokenPrefix,
APIPrefix
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment