Files
gjt_mini/pageSub/failure/detail.vue
2025-12-30 09:44:46 +08:00

1833 lines
53 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="container">
<u-collapse :value="['basic']" :border="false">
<u-collapse-item title="故障上报" ref="uploadCollapse" name="basic">
<view class="item">
<view class="title">序号</view>
<u--input
border="surround"
disabled
placeholder="保存后自动生成"
v-model="datas.failureNo"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title plateNumber required">车牌号</view>
<u-picker
:show="showPlateNumberPicker"
:columns="[truckList]"
keyName="plateNumber"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmPlateNumber"
@cancel="showPlateNumberPicker = false"
>
</u-picker>
<u-input
border="surround"
:value="datas.plateNumber"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
@tap="!disabled && searchPlatNumber()"
>
</u-input>
</view>
<view class="item">
<view class="title">车辆型号</view>
<u--input
border="surround"
disabled
v-model="datas.modelName"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title">车辆品牌</view>
<u--input
border="surround"
disabled
v-model="datas.brandName"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title required">故障来源</view>
<u-picker
:show="showFaultSourcePicker"
:columns="[faultSource]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmFaultSource"
@cancel="showFaultSourcePicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getFaultSourceName(datas.faultSource)"
@tap="!disabled && (showFaultSourcePicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title required">故障状态</view>
<u-picker
:show="showIsFailureStatusPicker"
:columns="[failureStatus]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmFailureStatus"
@cancel="showIsFailureStatusPicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getFailureStatusName(datas.failureStatus)"
@tap="!disabled && (showIsFailureStatusPicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">故障描述</view>
<u--textarea
border="surround"
fontSize="26"
:disabled="disabled"
:confirmType="null"
v-model="datas.failureDesc"
:placeholder="failureDescPlaceHolder"
>
</u--textarea>
</view>
<!-- <view class="item">
<view class="title required">故障类型</view>
<u-picker :show="showTypePicker" :columns="[type]" keyName="dicName" :disabled="disabled"
@confirm="confirmIsDrivingType" @cancel="showTypePicker = false" >
</u-picker>
<u--input border="surround" :disabledColor="disabled ? '#f5f7fa': '#ffffff'" disabled fontSize="26"
:value="getTypeName(datas.type)" @tap="!disabled && (showTypePicker = true)">
</u--input>
</view> -->
<view class="item">
<view class="title required">故障类型</view>
<!-- 使用 v-model 绑定 -->
<multiple-picker
title="请选择故障类型"
:show="selectMultiple.show"
:columns="selectMultiple.columns"
:defaultIndex="defaultIndex"
@confirm="confirmMultiple"
@cancel="selectMultiple.show = false"
@change="changeMultiple"
/>
<!-- 显示选中的故障类型 -->
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
fontSize="26"
disabled
:value="datas.typeName"
@tap="!disabled && handleMultiple()"
>
</u--input>
</view>
<view class="item">
<view class="title">解决方案 (详细)</view>
<u--textarea
border="surround"
v-model="datas.solution"
:disabled="disabled"
fontSize="26"
:confirmType="null"
height="180"
></u--textarea>
</view>
<view class="item">
<view class="title">公里数 (km)</view>
<u--input
border="surround"
:disabled="disabled"
v-model="datas.mileage"
fontSize="26"
type="digit"
></u--input>
</view>
<view class="item">
<view class="title">保修开始时间</view>
<u-datetime-picker
placeholder="请选择日期"
:show="showWarrantyDateBeginPicker"
mode="date"
@confirm="confirmWarrantyDateBegin"
@cancel="showWarrantyDateBeginPicker = false"
></u-datetime-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
fontSize="26"
disabled
:value="formatDateTime(datas.warrantyDateBegin, 'date')"
@tap="!disabled && (showWarrantyDateBeginPicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">保修结束时间</view>
<u-datetime-picker
placeholder="请选择日期"
:show="showWarrantyDateEndPicker"
mode="date"
@confirm="confirmWarrantyDateEnd"
@cancel="showWarrantyDateEndPicker = false"
></u-datetime-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
fontSize="26"
disabled
:value="formatDateTime(datas.warrantyDateEnd, 'date')"
@tap="!disabled && (showWarrantyDateEndPicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">费用类型</view>
<u-picker
:show="showCostTypePicker"
:columns="[costType]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmCostType"
@cancel="showCostTypePicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getCostTypeName(datas.costType)"
@tap="!disabled && (showCostTypePicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">工时费</view>
<u--input
border="surround"
:disabled="disabled"
v-model="datas.hourlyRates"
fontSize="26"
type="digit"
></u--input>
</view>
<view class="item">
<view class="title">配件费</view>
<u--input
border="surround"
:disabled="disabled"
v-model="datas.accessoriesCost"
fontSize="26"
type="digit"
></u--input>
</view>
<view class="item">
<view class="title">付款方</view>
<u-picker
:show="showPayerPicker"
:columns="[payer]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmPayer"
@cancel="showPayerPicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getPayerName(datas.payer)"
@tap="!disabled && (showPayerPicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">收款方</view>
<u-picker
:show="showPayeePicker"
:columns="[maintainanceList]"
keyName="name"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmPayee"
@cancel="showPayeePicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="datas.payeeName"
@tap="!disabled && (showPayeePicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title required">故障上报时间</view>
<u-datetime-picker
placeholder="请选择日期"
:disabled="disabled"
:show="showFailureTimePicker"
mode="datetime"
@confirm="confirmFailureTime"
@cancel="showFailureTimePicker = false"
></u-datetime-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
fontSize="26"
disabled
:value="formatDateTime(datas.failureTime, 'minute')"
@tap="!disabled && (showFailureTimePicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title required">故障解决时间</view>
<u-datetime-picker
placeholder="请选择日期"
:disabled="disabled"
:show="showSolutionTimePicker"
mode="datetime"
@confirm="confirmSolutionTime"
@cancel="showSolutionTimePicker = false"
></u-datetime-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
fontSize="26"
disabled
:value="formatDateTime(datas.solutionTime, 'minute')"
@tap="!disabled && (showSolutionTimePicker = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">维修效率 (小时)</view>
<u--input
border="surround"
disabled
v-model="datas.timeConsuming"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title">解决路径</view>
<u--textarea
border="surround"
:disabled="disabled"
fontSize="26"
height="180"
:confirmType="null"
:placeholder="resolutionPathPlaceHolder"
v-model="datas.resolutionPath"
>
</u--textarea>
</view>
<view class="item">
<view class="title">是否停运</view>
<u-picker
:show="showIsSuspensionPicker"
:columns="[yesNoDic]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmIsSuspension"
@cancel="showIsSuspensionPicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getYesNoName(datas.isSuspension)"
@tap="!disabled && (showIsSuspensionPicker = true)"
>
</u--input>
</view>
<view class="item" v-if="datas.isSuspension == '1'">
<view class="title">停运时长 (小时)</view>
<u--input
border="surround"
disabled
v-model="datas.suspensionDuration"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title">客户需求</view>
<u--textarea
border="surround"
:disabled="disabled"
v-model="datas.customerDemand"
fontSize="26"
></u--textarea>
</view>
<view class="item">
<view class="title">是否拖车</view>
<u-picker
:show="showIsTrailerPicker"
:immediateChange="true"
:columns="[yesNoDic]"
keyName="dicName"
:disabled="disabled"
@confirm="confirmTrailer"
@cancel="showIsTrailerPicker = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getYesNoName(datas.isTrailer)"
@tap="!disabled && (showIsTrailerPicker = true)"
>
</u--input>
</view>
<view class="item" v-if="datas.isTrailer == '1'">
<view class="title">拖车费用 ()</view>
<u--input
border="surround"
:disabled="disabled"
v-model="datas.towingFee"
fontSize="26"
type="digit"
></u--input>
</view>
<view class="item">
<view class="title">客户名称</view>
<u--input
border="surround"
disabled
v-model="datas.customerName"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title">付款状态</view>
<u-picker
:show="showPaymentStatus"
:columns="[paymentStatusDic]"
keyName="dicName"
:disabled="disabled"
:immediateChange="true"
@confirm="confirmPaymentStatus"
@cancel="showPaymentStatus = false"
>
</u-picker>
<u--input
border="surround"
:disabledColor="disabled ? '#f5f7fa' : '#ffffff'"
disabled
fontSize="26"
:value="getPaymentStatusName(datas.paymentStatus)"
@tap="!disabled && (showPaymentStatus = true)"
>
</u--input>
</view>
<view class="item">
<view class="title">执行人</view>
<u--input
border="surround"
disabled
v-model="datas.implementerName"
fontSize="26"
></u--input>
</view>
<view class="item">
<view class="title">附件上传</view>
<u-upload
:fileList="fileList12"
@afterRead="afterRead2"
@delete="deletePic"
accept="all"
uploadIcon="plus"
@clickPreview="previewFile"
:previewFullImage="true"
:maxCount="10"
:width="155"
:height="155"
:disabled="disabled"
:deletable="!disabled"
name="12"
>
</u-upload>
</view>
<view class="item">
<view class="title">零部件更换照片</view>
<u-upload
:fileList="fileList13"
@afterRead="afterRead"
@delete="deletePic"
:maxCount="10"
:width="200"
:height="200"
:disabled="disabled"
:deletable="!disabled"
name="13"
></u-upload>
</view>
</u-collapse-item>
</u-collapse>
<u-loading-page
bg-color="#ffffff"
color="#7ba746"
font-size="24"
:loading="loading"
:loading-text="loadingText"
></u-loading-page>
<view class="btns">
<button
class="done"
:class="{ disabled: isRead }"
:disabled="isRead"
@tap="submitConfirm"
>
提交
</button>
<button
class="done"
:class="{ disabled: isRead }"
:disabled="isRead"
@tap="save"
>
保存
</button>
<button class="cancel" @tap="navigateBack">取消</button>
</view>
<view style="width: 0px; height: 0px; overflow: hidden">
<canvas
:style="
'width: ' +
canvasWidth +
'px; height:' +
canvasHeight +
'px;left:8888px'
"
canvas-id="myCanvas"
></canvas>
</view>
</view>
</template>
<script>
import { getUser } from "@/utils/auth.js";
import UniUploadFile from "uni-uploadfile";
export default {
options: {
styleIsolation: "shared", // 解除样式隔离
},
data() {
return {
datas: {
typeName: "",
mileage: "",
type: [],
selectValue: "",
selectIndex: [], // 选中的索引(多选)
isSuspension: "0", // 是否挂起
},
selectMultiple: {
show: false, // 是否显示多选框
index: [], // 当前选中的索引
columns: [], // 选择项的数据列
},
failureDescPlaceHolder: `1、何种状态下\r\n2、产生何种现象\r\n3、导致何种故障`,
resolutionPathPlaceHolder: `1、运维电话指导\n2、运维现场排故\n3、服务站名字现场排故\n4、自行行驶到服务站名字\n5、厂家名字电话指导\n6、厂家名字现场排故`,
address: "",
id: null,
brands: [], // 车辆品牌字典列表
truckType: [], // 车辆型号字典列表,
salesManagerDic: [], // 销售经理字典列表
yesNoDic: [],
type: [], // 故障类型
costType: [], // 费用类型
payer: [], // 付款方
paymentStatusDic: [], // 付款状态列表
failureStatus: [], //故障状态
faultSource: [], // 故障来源
userInfo: {}, // 用户信息
contractAuthorizerInformationList: [], // 授权人列表
truckList: [], // 车辆列表
maintainanceList: [], // 维修站列表
showAuthorizerPicker: false,
showTypePicker: false, //故障类型是否弹出
showIsTrailerPicker: false, // 是否托车是否弹出
showIsSuspensionPicker: false, // 是否停运是否弹出
showWarrantyDatePicker: false, // 保修时间是否弹出
showWarrantyDateBeginPicker: false, // 保修开始时间
showWarrantyDateEndPicker: false, // 保修结束时间
showFailureTimePicker: false, // 故障上报时间是否弹出
showSolutionTimePicker: false, // 故障解决时间是否弹出
showIsFailureStatusPicker: false, // 故障状态是否弹出
showFaultSourcePicker: false, // 故障来源是否弹出
showCostTypePicker: false, // 费用类型
showPayerPicker: false, // 付款方
showPayeePicker: false, // 收款方
showHandoverDatePicker: false,
platNumberDisabled: true,
showPlateNumberPicker: false,
showPaymentStatus: false, // 付款状态
fileList12: [], // 附件上传
fileList13: [], // 事故照片
canvasWidth: 0,
canvasHeight: 0,
isRead: false, // 页面是否处于阅读模式
loading: false,
loadingText: "加载中...",
defaultIndex: [], // 故障类型多选框默认选中值
};
},
methods: {
getData() {
// 没有id说明是要新增故障不请求接口
if (!this.id) {
this.datas = {
solutionTime: null,
failureTime: null,
timeConsuming: null,
};
this.datas.implementerName = this.userInfo.userName;
this.datas.implementer = this.userInfo.id;
this.datas.attachmentList = [];
this.datas.failurePics = [];
return;
}
this.$api.failure.getTakeId({ id: this.id }).then((res) => {
console.log(res);
this.datas = res;
if (!this.isRead) {
this.datas.implementerName = this.userInfo.userName;
this.datas.implementer = this.userInfo.id;
this.updateSuspensionDuration();
}
this.defaultIndex = this.datas.type.split(",") ?? [];
// 根据后端相应的相关图片字段重新得到fileList用于前端图片预览
this.parseFileListBack();
});
},
goto(e) {
uni.navigateTo({ url: e.target.dataset.url });
},
navigateBack() {
uni.navigateBack();
},
formatDateTime(obj, type = "date") {
if (obj == null) {
return "";
}
const date = new Date(obj);
if (type === "date") {
let y = 1900 + date.getYear();
let m = "0" + (date.getMonth() + 1);
let d = "0" + date.getDate();
return (
y +
"-" +
m.substring(m.length - 2, m.length) +
"-" +
d.substring(d.length - 2, d.length)
);
} else if (type === "hour") {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:00`;
} else if (type === "minute") {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份从0开始
const day = String(date.getDate()).padStart(2, "0");
const hour = String(date.getHours()).padStart(2, "0");
const minute = String(date.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hour}:${minute}`;
} else {
throw new Error(
"Invalid type. Valid types are 'date', 'hour', and 'minute'."
);
}
},
getNowDateTime() {
let currentTime = new Date();
let year = currentTime.getFullYear();
let month = currentTime.getMonth() + 1;
let day = currentTime.getDate();
let hours = currentTime.getHours();
let minutes = currentTime.getMinutes();
month = month < 10 ? "0" + month : month;
day = day < 10 ? "0" + day : day;
hours = hours < 10 ? "0" + hours : hours;
minutes = minutes < 10 ? "0" + minutes : minutes;
return `${year}-${month}-${day} ${hours}:${minutes}`;
},
getDic() {
this.$api.standbyVehicle.getVehicleBrand().then((res) => {
this.brands = res;
});
this.$api.standbyVehicle.getTruckType().then((res) => {
this.truckType = res;
});
this.$api.truckRent.getSalesManagerDic().then((res) => {
this.salesManagerDic = res;
});
this.$api.standbyVehicle.getYesNoDic().then((res) => {
this.yesNoDic = res;
});
this.$api.standbyVehicle.getTypeName().then((res) => {
this.type = res;
console.log(res);
this.type = res;
// 设置多选picker的数据
this.selectMultiple.columns = this.type.map((item) => ({
label: item.dicName,
value: item.dicCode,
}));
// 假设后端返回的 type 是一个字符串 "0, 1, 2"
if (this.datas.type) {
// 将后端返回的 type 字符串拆分成数组
this.datas.type = this.datas.type
.split(",")
.map((item) => item.trim());
}
// 在数据加载完成后,回显默认选中项
this.setDefaultSelection();
});
this.$api.standbyVehicle.getFailureStatusName().then((res) => {
this.failureStatus = res;
});
this.$api.standbyVehicle.getCostTypeName().then((res) => {
this.costType = res;
});
this.$api.standbyVehicle.getPayerName().then((res) => {
this.payer = res;
});
this.$api.standbyVehicle.getFaultSourceName().then((res) => {
this.faultSource = res;
});
this.$api.standbyVehicle.getPaymentStatusDic().then((res) => {
this.paymentStatusDic = res;
});
},
getBrandName(dicCode) {
return this.getDicValue(this.brands, dicCode);
},
getModelName(dicCode) {
return this.getDicValue(this.truckType, dicCode);
},
getSalesManagerName(dicCode) {
return this.getDicValue(this.salesManagerDic, dicCode);
},
getYesNoName(dicCode) {
return this.getDicValue(this.yesNoDic, dicCode);
},
getTypeName(dicCode) {
return this.getDicValue(this.type, dicCode);
},
getFailureStatusName(dicCode) {
return this.getDicValue(this.failureStatus, dicCode);
},
getCostTypeName(dicCode) {
return this.getDicValue(this.costType, dicCode);
},
getPayerName(dicCode) {
return this.getDicValue(this.payer, dicCode);
},
getFaultSourceName(dicCode) {
return this.getDicValue(this.faultSource, dicCode);
},
getPaymentStatusName(dicCode) {
return this.getDicValue(this.paymentStatusDic, dicCode);
},
getDicValue(dic, dicCode) {
if (dic.length == 0) {
return "";
}
return dic.find((x) => x.dicCode == dicCode)?.dicName ?? "";
},
queryAuthListByPlateNumber(plateNumber) {
this.$api.truck.queryAuthList({ plateNumber }).then((res) => {
this.truckList = res;
this.showPlateNumberPicker = true;
});
},
getMaintainanceList() {
this.$api.failure.getMaintainanceList({ pageSize: 999 }).then((res) => {
console.log(`maintainanceList:\n`, res);
if (res.records) {
this.maintainanceList = res.records;
}
});
},
confirmAuthorizer(e) {
this.showAuthorizerPicker = false;
this.datas.takeName = e.value[0].authorizer;
this.datas.takeIdNo = e.value[0].authorizerIdentityCard;
this.datas.takePhone = e.value[0].authorizerTelephone;
},
// 故障类型
confirmIsDrivingType(e) {
this.datas.typeName = e.value[0].dicName;
this.datas.type = e.value[0].dicCode;
this.showTypePicker = false;
},
// 是否拖车
confirmTrailer(e) {
this.datas.isTrailer = e.indexs[0];
this.showIsTrailerPicker = false;
this.$nextTick(() => {
this.$refs.uploadCollapse.init();
});
},
// 是否停运
confirmIsSuspension(e) {
this.$set(this.datas, "isSuspension", e.indexs[0]);
// 有watch监听isSuspension更改后停运时长会自动更新
this.showIsSuspensionPicker = false;
this.$nextTick(() => {
this.$refs.uploadCollapse.init();
});
},
// 故障解决时间
confirmHandoverDate(e) {
this.datas.solutionTime = e.value;
this.showHandoverDatePicker = false;
},
// 保修时间
confirmWarrantyDate(e) {
this.datas.warrantyDate = e.value;
this.showWarrantyDatePicker = false;
},
// 保修开始时间confirmWarrantyDateBegin
confirmWarrantyDateBegin(e) {
const end = new Date(this.datas.warrantyDateEnd ?? 32503593600000); // 如果还未填写默认为2999年12月31日
const start = new Date(e.value);
if (end < start) {
this.alert(`保修开始时间必须早于保修结束时间`);
return;
}
this.datas.warrantyDateBegin = e.value;
this.showWarrantyDateBeginPicker = false;
},
// 保修结束时间
confirmWarrantyDateEnd(e) {
const end = new Date(e.value);
const start = new Date(this.datas.warrantyDateBegin ?? 0);
if (end < start) {
this.alert(`保修结束时间必须晚于保修开始时间`);
return;
}
this.datas.warrantyDateEnd = e.value;
this.showWarrantyDateEndPicker = false;
},
// 故障上报时间
confirmFailureTime(e) {
const end = new Date(this.datas.solutionTime ?? 32503593600000); // 如果还未填写默认为2999年12月31日
const start = new Date(e.value);
if (end < start) {
this.alert(`故障上报时间必须早于故障解决时间`);
return;
}
this.datas.failureTime = e.value;
this.showFailureTimePicker = false;
},
// 故障解决时间
confirmSolutionTime(e) {
const end = new Date(e.value);
const start = new Date(this.datas.failureTime ?? 0);
if (end < start) {
// 显示错误提示
this.alert(`故障解决时间必须晚于故障上报时间`);
return;
}
this.datas.solutionTime = e.value;
this.showSolutionTimePicker = false;
},
// 故障状态
confirmFailureStatus(e) {
this.datas.failureStatusName = e.value[0].dicName;
this.datas.failureStatus = e.value[0].dicCode;
this.showIsFailureStatusPicker = false;
},
// 故障来源 confirmFaultSource
confirmFaultSource(e) {
this.datas.faultSourceName = e.value[0].dicName;
this.datas.faultSource = e.value[0].dicCode;
this.showFaultSourcePicker = false;
},
// 费用类型 showPayerPicker
confirmCostType(e) {
this.datas.costTypeName = e.value[0].dicName;
this.datas.costType = e.value[0].dicCode;
this.showCostTypePicker = false;
},
// 付款方 confirmPayer
confirmPayer(e) {
this.datas.payerName = e.value[0].dicName;
this.datas.payer = e.value[0].dicCode;
this.showPayerPicker = false;
},
confirmPayee(e) {
this.datas.payeeName = e.value[0].name;
this.datas.payee = e.value[0].id;
this.showPayeePicker = false;
},
// 付款状态
confirmPaymentStatus(e) {
this.datas.paymentStatusName = e.value[0].dicName;
this.datas.paymentStatus = e.value[0].dicCode;
this.showPaymentStatus = false;
},
confirmPlateNumber(e) {
console.log("truckInfo:\n", e.value[0]);
this.datas.truckId = e.value[0].id;
this.datas.vin = e.value[0].vin;
this.datas.plateNumber = e.value[0].plateNumber;
this.datas.modelName = e.value[0].modelName;
this.datas.brandName = e.value[0].brandName;
this.showPlateNumberPicker = false;
this.queryTruckRentInfo();
this.getActualDataByVin();
},
getActualDataByVin() {
this.$api.truck
.getActualDataByVin({ vin: this.datas.vin })
.then((res) => {
const miles = res.data?.totalMileage
? (res.data?.totalMileage / 1000).toFixed(1)
: 0;
this.$set(this.datas, "mileage", miles);
});
},
queryTruckRentInfo() {
const params = {};
if (!this.datas.truckId) {
this.datas.customerName = null;
this.datas.customerId = null;
return;
}
params.truckId = this.datas.truckId;
if (this.datas.failureTime) {
params.date = this.datas.failureTime;
}
this.$api.failure.queryTruckRentInfo(params).then((res) => {
if (!res) {
this.datas.customerName = null;
this.datas.customerId = null;
return;
}
this.datas.customerName = res.customerName;
this.datas.customerId = res.customerId;
});
},
enbalbeChangeTruck() {
this.platNumberDisabled = false;
this.searchPlatNumber();
},
searchPlatNumber() {
uni.showModal({
title: "车牌号关键字",
confirmText: "搜索",
editable: true,
placeholderText: "请输入车牌号关键字用于检索",
success: (res) => {
res.confirm && this.queryAuthListByPlateNumber(res.content);
},
});
},
// 进入页面后更新停运时长
updateSuspensionDuration() {
const hasSettled =
this.getFailureStatusName(this.datas.failureStatus) == "已解决";
if (!this.datas.isSuspension) {
this.datas.suspensionDuration = 0;
return;
}
if (hasSettled) {
this.datas.suspensionDuration = this.datas.timeConsuming;
} else {
const timeConsuming = this.datas.failureTime
? (Date.now() - this.datas.failureTime) / (1000 * 3600)
: 0;
this.datas.suspensionDuration =
timeConsuming && timeConsuming > 0 ? timeConsuming.toFixed(2) : 0;
}
},
getLocation() {
uni.getLocation({
type: "wgs84",
success: (res) => {
const longitude = res.longitude;
const latitude = res.latitude;
this.$api.truckRent
.getAddress({ longitude, latitude })
.then((res) => {
this.address = res?.data ?? "浙江省嘉兴市";
});
},
fail: (res) => {
console.log(res);
},
});
},
checkCameraPermission() {
console.log("checkCameraPermission");
uni.getSetting({
success(res) {
if (!res.authSetting["scope.camera"]) {
console.log(`有相机权限`);
return;
} else {
console.log(`没有相机权限! 开始申请获取`);
this.requestCameraPermission();
}
},
fail(e) {
console.log(`获取setting失败:\n`, e);
},
});
},
requestCameraPermission() {
uni.authorize({
scope: "scope.camera",
success() {
console.log(`相机权限授权成功`);
},
fail() {
console.log(`相机权限授权失败`);
},
});
},
// 删除图片
deletePic(event) {
const fileList = this[`fileList${event.name}`];
if (fileList.some((x) => x.status == "uploading")) {
this.alert(`请等待现有图片上传完成后再操作`);
return;
}
this[`fileList${event.name}`].splice(event.index, 1);
if (event.name == "12") {
this.datas.attachmentList.splice(event.index, 1);
} else if (event.name == "13") {
this.datas.failurePics.splice(event.index, 1);
}
},
// 新增图片
async afterRead(event) {
this.loading = true;
this.loadingText = "添加水印中...";
let lists = [].concat(event.file);
let fileListLen = this[`fileList${event.name}`].length;
lists.map((item) => {
this[`fileList${event.name}`].push({
...item,
status: "uploading",
message: "上传中",
});
});
const fileSize = event.file.size / 1024 ** 2; // 图片多少MB
console.log(`event.file:\n`, event.file);
for (let i = 0; i < lists.length; i++) {
let waterUrl = "";
try {
waterUrl = await this.addWatermark(lists[i].url, fileSize);
} catch (e) {
console.log("添加水印报错\n", e);
this[`fileList${event.name}`].splice(fileListLen, 1);
return;
}
const result = await this.uploadFilePromise(waterUrl);
let item = this[`fileList${event.name}`][fileListLen];
this[`fileList${event.name}`].splice(
fileListLen,
1,
Object.assign(item, {
status: "success",
message: "",
url: result.data?.url ?? "",
thumb: waterUrl,
})
);
fileListLen++;
const imgUrl = result.data?.url ?? "";
if (event.name == "13") {
this.datas.failurePics.push(imgUrl);
}
}
this.$nextTick(() => {
this.$refs.uploadCollapse.init();
});
},
// 新增文件
async afterRead2(event) {
console.log(event);
let lists = [].concat(event.file);
let fileListLen = this[`fileList${event.name}`].length;
lists.map((item) => {
this[`fileList${event.name}`].push({
...item,
status: "uploading",
message: "上传中",
});
});
console.log(`event.file:\n`, event.file);
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise2(event.file);
let item = this[`fileList${event.name}`][fileListLen];
this[`fileList${event.name}`].splice(
fileListLen,
1,
Object.assign(item, {
status: "success",
message: "",
url: result.data?.url ?? "",
name: event.file.name,
})
);
fileListLen++;
const url = result.data?.url ?? "";
if (event.name == "12") {
this.datas.attachmentList.push({
path: url,
fileName: event.file.name,
});
}
}
this.$nextTick(() => {
this.$refs.uploadCollapse.init();
});
},
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: "/attachment/upload",
filePath: url,
name: "file",
success: (res) => {
resolve(JSON.parse(res.data || {}));
},
fail: (e) => {
reject(e);
},
});
});
},
// 微信原生wx.uploadFile不支持原文件名上传故手动构造formData请求接口
// 参考https://github.com/guoyanshuo/uni-uploadfile
uploadFilePromise2(file) {
return new Promise((resolve, reject) => {
let a = UniUploadFile({
url: "/attachment/upload",
files: [file],
name: "file",
success: (res) => {
resolve(res.data || {});
},
fail: (e) => {
reject(e);
},
});
});
},
// 图片添加水印
async addWatermark(img, fileSize) {
const image = img;
let res1 = await new Promise((resolve, reject) => {
uni.getImageInfo({
src: img,
success: (res) => {
// 设置canvas宽高等于原图片宽高
this.canvasWidth = res.width;
this.canvasHeight = res.height;
// 创建 canvas 的绘图上下文
const ctx = uni.createCanvasContext("myCanvas", this);
// 绘制图片
ctx.drawImage(image, 0, 0, res.width, res.height);
//先加logo
// const watermarkImg = this.$refs.logo;
ctx.drawImage("/static/logo3.png", 10, res.height - 150, 200, 40.8);
//再加时间
ctx.font = "25px 黑体";
ctx.fillStyle = "#fff";
ctx.textAlign = "left";
ctx.fillText(this.getNowDateTime(), 10, res.height - 70);
//最后加地址
ctx.font = "25px 黑体";
ctx.fillStyle = "#fff";
ctx.textAlign = "left";
ctx.fillText(this.address, 10, res.height - 30);
// 画到 canvas 中
ctx.draw(false, () => {
// 将画布转化为图片
// 设置延时,延时的秒数和图片体积正相关,越大的图片,延时越长
// 如果不延时,导出的图片可能存在有黑边、只显示部分 等问题
let timeout = 0;
if (fileSize <= 3) {
timeout = 1000;
} else {
timeout = 1000 * (fileSize / 3);
}
setTimeout(() => {
uni.canvasToTempFilePath(
{
canvasId: "myCanvas",
fileType: "jpg",
quality: fileSize <= 3 ? 1 : 0.75, // 如果不到3MB则不压缩否则压缩
success: (res) => {
console.log(res);
this.loading = false;
resolve(res.tempFilePath);
},
fail: (err) => {
this.loading = false;
reject(err);
},
},
this
);
}, timeout);
});
},
});
});
return res1;
},
parseFileListBack() {
// 事故照片
this.fileList13 = this.datas.failurePics.map((x) => ({ url: x }));
this.fileList12 = this.datas.attachmentList.map((x) => ({
url: x.path,
name: x.fileName,
}));
this.$nextTick(() => {
this.$refs.uploadCollapse.init();
});
},
// 保存
save() {
this.datas.isSubmit = 0;
// 有id调用编辑接口
if (this.id) {
this.$api.failure.edit(this.datas).then((res) => {
if (res) {
uni.showToast({
title: "保存成功",
icon: "success",
duration: 1500,
success() {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
}
});
return;
}
// 没有id调用添加接口
this.$api.failure.add(this.datas).then((res) => {
if (res) {
uni.showToast({
title: "保存成功",
icon: "success",
duration: 1500,
success() {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
}
});
},
submitConfirm() {
uni.showModal({
title: "提示",
content: "确认提交?",
success: (res) => {
if (res.confirm) {
this.submit();
}
},
});
},
submit() {
if (!this.check()) {
return;
}
this.datas.isSubmit = 1;
// 有id调用编辑接口
if (this.id) {
this.$api.failure.edit(this.datas).then((res) => {
if (res) {
uni.showToast({
title: "提交成功",
icon: "success",
duration: 1500,
success() {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
}
});
return;
}
// 没有id调用添加接口
this.$api.failure.add(this.datas).then((res) => {
if (res) {
uni.showToast({
title: "提交成功",
icon: "success",
duration: 1500,
success() {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
}
});
},
// 在提交前检查,是否有必填字段未填写
check() {
console.log(`datas:\n`, this.datas);
if (!this.datas.plateNumber) {
this.alert("请填写车牌号");
return false;
}
if (!this.datas.faultSource && this.datas.faultSource !== 0) {
this.alert("请选择故障来源");
return false;
}
if (!this.datas.failureStatus && this.datas.failureStatus !== 0) {
this.alert("请选择故障状态");
return false;
}
if (!this.datas.type && this.datas.type !== 0) {
this.alert("请选择故障类型");
return false;
}
if (!this.datas.failureTime) {
this.alert("请填写故障上报时间");
return false;
}
if (!this.datas.solutionTime) {
this.alert("请填写故障解决时间");
return false;
}
// if (!this.datas.failurePics) {
// this.alert("请填写零部件更换照片");
// return false;
// }
// if (this.datas.failurePics.length < 3) {
// this.alert("请至少上传3张零部件更换照片");
// return false;
// }
return true;
},
alert(msg) {
uni.showToast({
title: msg,
icon: "none",
duration: 1500,
});
},
// fileDownloadUrl() {
// if (!this.datas.signFlowId) {
// uni.showToast({
// icon: "none",
// title: "请先点击签名",
// duration: 2000,
// });
// return;
// }
// this.loading = true;
// this.loadingText = "下载中...";
// this.$api.truckRent
// .fileDownloadUrl({
// signFlowId: this.datas.signFlowId,
// })
// .then((res) => {
// this.loading = false;
// if (!res) {
// this.alert(`下载失败`);
// return;
// }
// if (res.msg != "OK") {
// this.alert(res.msg);
// return;
// }
// this.datas.esignatureAttachment = res.data;
// this.fileList11 = res.data.map((x) => ({
// url: x.path,
// thumb: "/static/pdf.png",
// }));
// this.hasSign = true;
// });
// },
previewFile(event) {
const path = event.url;
this.loading = true;
this.loadingText = "加载中...";
const that = this;
uni.downloadFile({
url: path,
success: function (res) {
var filePath = res.tempFilePath;
console.log(filePath);
that.loading = false;
uni.openDocument({
filePath: filePath,
success() {
that.loading = false;
},
fail(e) {
console.log(e);
if (e.errMsg == "openDocument:fail filetype not supported") {
that.alert("微信不支持预览此类文件");
} else {
that.alert(e.errMsg);
}
that.loading = false;
},
});
},
fail(e) {
console.log(e);
that.alert("预览文件失败");
that.loading = false;
},
});
},
// 打开多选框
handleMultiple() {
this.selectMultiple.index = this.datas.type || [];
this.selectMultiple.show = true;
},
confirmMultiple(e) {
console.log("Selected Items:", e.selected);
this.defaultIndex = e.value;
let selectedLabels = [];
let selectedValues = [];
// 遍历选中的项
e.selected.forEach((item) => {
selectedLabels.push(item.label); // 保存选中的标签(显示用)
selectedValues.push(item.value); // 保存选中的实际值(实际提交用)
});
// 以逗号分隔显示选中的标签
this.datas.typeName = selectedLabels.join(",");
// 保存实际值到 type后端需要的字段
this.datas.type = selectedValues.join(","); // 拼接成字符串形式
// 关闭多选框
this.selectMultiple.show = false;
},
// 多选变化
changeMultiple(e) {
console.log(e);
},
// 回显默认选中的值
setDefaultSelection() {
// 如果datas.type有默认值例如后端返回的 "0, 1, 2"
if (this.datas.type && this.datas.type.length > 0) {
const selectedLabels = [];
// 遍历type中的每个索引找到对应的label
this.datas.type.forEach((index) => {
const column = this.selectMultiple.columns.find(
(item) => item.value == index
); // 根据 value 查找
if (column) {
selectedLabels.push(column.label); // 获取对应索引的标签
}
});
// 将所有选中的标签拼接成一个字符串赋值给 typeName
this.datas.typeName = selectedLabels.join(",");
// 设置回显时的选中索引(用于显示选中的项)
this.selectMultiple.index = this.datas.type; // 这里直接使用 type 数组作为索引
}
},
},
computed: {
disabled() {
return this.isRead;
},
},
onLoad(options) {
if (options.id) {
this.id = options.id;
}
if (options.isRead) {
this.isRead = Number(options.isRead);
}
this.userInfo = getUser() || {};
this.checkCameraPermission(); // 检测拍照权限
this.getDic();
this.getData();
this.getMaintainanceList();
this.getLocation();
},
onPullDownRefresh() {
uni.stopPullDownRefresh(); //刷新数据之后停止刷新效果
},
onShow() {
if (this.loadingText != "添加水印中...") {
this.loading = false;
}
},
onHide() {},
destroyed() {},
watch: {
"datas.solutionTime": function (val, oldval) {
if (this.datas.failureTime && val) {
const timeConsuming =
(this.datas.solutionTime - this.datas.failureTime) / (1000 * 3600);
this.datas.timeConsuming = timeConsuming.toFixed(2);
}
this.updateSuspensionDuration();
},
"datas.failureTime": function (val, oldval) {
if (this.datas.timeConsuming && val) {
const timeConsuming =
(this.datas.solutionTime - this.datas.failureTime) / (1000 * 3600);
this.datas.timeConsuming = timeConsuming.toFixed(2);
}
this.updateSuspensionDuration();
},
"datas.isSuspension": function (val, oldVal) {
this.updateSuspensionDuration();
},
"datas.failureStatus": function (val, oldVal) {
this.updateSuspensionDuration();
},
},
};
</script>
<style lang="less" scoped>
.container {
// background-color: #d7d7d7;
padding: 30rpx;
.item {
margin-top: 30rpx;
.title {
font-size: 26rpx;
margin-bottom: 15rpx;
&.required::before {
content: "*";
color: red;
margin-right: 3rpx;
}
}
.tip {
font-size: 26rpx;
margin-bottom: 15rpx;
color: #aaaaaa;
position: relative;
}
.btn.checkList {
width: 200rpx;
height: 60rpx;
font-size: 26rpx;
line-height: 26rpx;
padding: 17rpx 0;
text-align: center;
background-color: #7ba746;
color: white;
margin-left: 0;
&.required::before {
content: "*";
color: red;
margin-right: 3rpx;
}
}
.unit {
font-size: 26rpx;
}
.btn {
width: 200rpx;
height: 80rpx;
margin-left: 0;
font-size: 26rpx;
line-height: 26rpx;
padding: 27rpx 0;
text-align: center;
color: white;
&.sign {
background-color: #7ba746;
}
&.genDocument {
background-color: #1e98d7;
}
&.disabled {
opacity: 0.6;
}
}
}
.btns {
display: flex;
align-items: center;
justify-content: space-around;
margin-top: 20rpx;
button {
width: 200rpx;
height: 60rpx;
font-size: 26rpx;
line-height: 26rpx;
padding: 17rpx 0;
text-align: center;
}
.cancel {
background-color: white;
color: #7ba746;
border: 1rpx #7ba746 solid;
}
.save {
background-color: #7ba746;
color: white;
}
.done {
background-color: #7ba746;
color: white;
}
.disabled {
opacity: 0.6;
}
}
}
/deep/ .u-textarea textarea {
min-height: 100rpx !important;
font-size: 26rpx !important;
white-space: pre-line !important;
}
/deep/ .u-border {
border-width: 1rpx !important;
border-color: gray !important;
border-style: solid;
}
/deep/ .u-collapse-item__content__text {
padding: unset !important;
color: unset !important;
font-size: unset !important;
}
/deep/ .u-cell__title-text {
font-size: unset !important;
line-height: unset !important;
color: unset !important;
}
/deep/ .u-cell__body {
padding: unset !important;
font-size: unset !important;
color: unset !important;
}
/deep/ .u-cell__right-icon-wrap text {
font-size: 32rpx !important;
}
/deep/ .u-cell--clickable {
background-color: unset !important;
}
/deep/ .u-collapse-item {
margin-bottom: 30rpx;
}
/deep/ .uni-select {
width: 100rpx !important;
}
/deep/ .u-upload__button text {
font-size: 48rpx !important;
}
/deep/ .u-upload__deletable {
height: 42rpx !important;
width: 42rpx !important;
}
/deep/ .u-upload__deletable text {
font-size: 32rpx !important;
line-height: 32rpx !important;
}
/deep/ picker-view {
height: 450rpx !important;
}
/deep/ .uni-select__input-text {
font-size: 26rpx !important;
text-align: right !important;
}
/deep/ .uni-select__selector-item text {
font-size: 26rpx !important;
}
/deep/ .uni-select {
padding-right: 18rpx !important;
border-width: 1rpx !important;
border-color: gray !important;
border-style: solid !important;
border-top-width: 0 !important;
border-right-width: 0 !important;
border-bottom-width: 0 !important;
}
// /deep/ picker-view picker-view-column:nth-of-type(5) {
// display: none;
// }
.changeCarBtn {
height: 76rpx;
font-size: 26rpx;
line-height: 76rpx;
border-width: 1rpx !important;
border-color: gray !important;
border-style: solid !important;
border-top-width: 0 !important;
border-right-width: 0 !important;
border-bottom-width: 0 !important;
}
/* 检查单样式开始 */
/deep/ .td_wrap {
height: auto !important;
}
/deep/ checkbox-group view.td {
width: 186rpx !important;
}
/deep/ checkbox-group view.td_wrap {
width: 186rpx !important;
}
/deep/ .no-bad-table-wrap .td.rowspan {
display: flex;
align-items: center;
justify-content: center;
}
/deep/ .div-table-head .thead .tr view {
width: 186rpx !important;
}
/deep/ no-bad-table {
overflow-x: hidden;
}
/deep/ .no-bad-table-wrap .thead .tr .td {
background-color: #7ba746;
color: white;
}
/deep/ .no-bad-table-wrap .thead .tr .td .td_wrap {
background-color: #7ba746;
color: white;
}
/* 检查单样式结束 */
/* loading 置于顶层 */
/deep/ .u-fade-enter-to.u-fade-enter-active {
z-index: 10074 !important;
}
/deep/ .selectMultiple .multipleBody .list .item {
font-size: 26rpx;
}
</style>