1100 lines
33 KiB
Vue
1100 lines
33 KiB
Vue
<template>
|
||
<!-- 数量出库 -->
|
||
<view class="page-container">
|
||
<uni-row :gutter="24" class="search-form">
|
||
<uni-col :span="6">
|
||
<view class="search" @click="saveNumAll">
|
||
定损完成
|
||
</view>
|
||
</uni-col>
|
||
</uni-row>
|
||
<view class="table-list-item">
|
||
<uni-row :gutter="24">
|
||
<uni-col :span="6">物资名称:</uni-col>
|
||
<uni-col :span="18"
|
||
><view class="cont">{{ queryParams.typeName }}</view>
|
||
</uni-col>
|
||
</uni-row>
|
||
<uni-row :gutter="24">
|
||
<uni-col :span="6">物资类型:</uni-col>
|
||
<uni-col :span="18"
|
||
><view class="cont">{{ queryParams.type }}</view>
|
||
</uni-col>
|
||
</uni-row>
|
||
<uni-row :gutter="24">
|
||
<uni-col :span="6">退料数量:</uni-col>
|
||
<uni-col :span="18"
|
||
><view class="cont">{{
|
||
queryParams.typeRepairNum
|
||
}}</view>
|
||
</uni-col>
|
||
</uni-row>
|
||
</view>
|
||
|
||
<scroll-view scroll-y style="padding-bottom: 20rpx">
|
||
|
||
|
||
<!-- 定损 -->
|
||
<view class="table-list-item">
|
||
<h2 style="padding: 4rpx 0; font-weight: bold">定损</h2>
|
||
<uni-forms ref="baseForm" label-align="right">
|
||
<div v-for="(item, index) in partItems" :key="index">
|
||
<uni-forms-item label="定损数量">
|
||
<uni-easyinput
|
||
placeholder="请填写定损数量"
|
||
v-model="item.repairNum"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="repairCheckNum1(index)"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="配件类型">
|
||
<treeSelect style="width: 100%;height: 90rpx;" ref="treeSelectRef"
|
||
:options="partTreeData" @change="partTreeChange" :index="index" @clear="clearPart" :defaultProps="defaultProps" ></treeSelect>
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item label="配件数量">
|
||
<uni-easyinput
|
||
placeholder="配件数量"
|
||
v-model="item.partNum"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="partCheckNum(item)"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="配件单价">
|
||
<div style="width: 100%; display: flex">
|
||
<uni-easyinput
|
||
v-model="item.partPrice"
|
||
placeholder="请输入配件单价"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="costCheckNum(item)"
|
||
/>
|
||
<div class="operation-btns">
|
||
<span class="add" @click="addPart">增加</span>
|
||
<span class="remove" @click="delPart(index)">删除</span>
|
||
</div>
|
||
</div>
|
||
</uni-forms-item>
|
||
</div>
|
||
</uni-forms>
|
||
</view>
|
||
|
||
<!-- -->
|
||
<view class="table-list-item">
|
||
<!-- 维修人员 -->
|
||
<h2 style="padding: 4rpx 0; font-weight: bold">定损信息</h2>
|
||
<uni-forms ref="baseForm" label-align="right">
|
||
<uni-forms-item label="维修人员">
|
||
<uni-data-select :localdata="repairPersonData" v-model="repairPerson" :clear="false" placeholder="请选择维修人员" />
|
||
</uni-forms-item>
|
||
<uni-forms-item label="费用合计">
|
||
<uni-easyinput placeholder="请填写费用合计" maxlength="50" v-model="costAll" disabled/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="备注">
|
||
<uni-easyinput placeholder="请填写备注" maxlength="50" v-model="remark"/>
|
||
</uni-forms-item>
|
||
<!-- <uni-forms-item label="附件">
|
||
<div class="upload" @click="uploadImg" v-if="imgBeseUrl==''">+</div>
|
||
<div class="upload" @click="uploadImg" v-else>
|
||
<image :src="imgBeseUrl" style="width: 160rpx;height: 160rpx;" mode=""></image>
|
||
</div>
|
||
</uni-forms-item> -->
|
||
<uni-forms-item label="附件">
|
||
<div class="upload-container">
|
||
<div class="upload" @click="uploadImg2" v-if="imgList2.length < 3">+</div>
|
||
<div class="image-preview" v-for="(img, index) in imgList2" :key="index">
|
||
<image :src="img.url" mode="aspectFill"></image>
|
||
<view class="delete-btn" @click.stop="deleteImage2(index)">×</view>
|
||
<PreviewImg :imgUrl="img.url" />
|
||
</div>
|
||
</div>
|
||
</uni-forms-item>
|
||
</uni-forms>
|
||
</view>
|
||
</scroll-view>
|
||
<!-- 返厂维修 -->
|
||
<!-- <view class="table-list-item">
|
||
<h2 style="padding: 4rpx 0; font-weight: bold">返厂维修</h2>
|
||
<uni-forms ref="baseForm" label-align="right">
|
||
<uni-forms-item label="维修数量">
|
||
<uni-easyinput
|
||
placeholder="请输入维修数量"
|
||
v-model="formMiddle.repairNum"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="repairCheckNum2"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="物资厂家">
|
||
<uni-data-select
|
||
:localdata="partsMillList"
|
||
v-model="formMiddle.supplierId"
|
||
placeholder="请选择物资厂家"
|
||
/>
|
||
</uni-forms-item>
|
||
<div v-for="(item, index) in partItemsMiddle" :key="index">
|
||
<uni-forms-item label="配件名称">
|
||
<uni-easyinput placeholder="配件名称" v-model="item.partName" />
|
||
</uni-forms-item>
|
||
<uni-forms-item label="是否收费">
|
||
<uni-data-select
|
||
:localdata="isChargeList"
|
||
v-model="item.partType"
|
||
placeholder="请选择是否收费"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="配件数量">
|
||
<uni-easyinput
|
||
v-model="item.partNum"
|
||
placeholder="请输入配件数量"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="partCheckNum(item)"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="金额">
|
||
<div style="width: 100%; display: flex">
|
||
<uni-easyinput
|
||
v-model="item.partPrice"
|
||
placeholder="请输入金额"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="costCheckNum(item)"
|
||
/>
|
||
<div class="operation-btns">
|
||
<span class="add" @click="addMidPart">+</span>
|
||
<span class="remove" @click="delMidPart(index)">-</span>
|
||
</div>
|
||
</div>
|
||
</uni-forms-item>
|
||
</div>
|
||
</uni-forms>
|
||
</view> -->
|
||
|
||
<!-- 维修报废 -->
|
||
<!-- <view class="table-list-item">
|
||
<h2 style="padding: 4rpx 0; font-weight: bold">维修报废</h2>
|
||
<uni-forms ref="baseForm" label-align="right">
|
||
<uni-forms-item label="报废数量">
|
||
<uni-easyinput
|
||
placeholder="请填写报废数量"
|
||
v-model="formRight.scrapNum"
|
||
type="number"
|
||
:clearable="false"
|
||
@input="repairCheckNum3"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="报废原因">
|
||
<uni-easyinput
|
||
placeholder="请填写报废原因" maxlength="50"
|
||
v-model="formRight.scrapReason"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="损坏原因">
|
||
<uni-data-select v-model="formRight.scrapType"
|
||
:localdata="damageReasonList"
|
||
placeholder="请选择损坏原因"
|
||
/>
|
||
</uni-forms-item>
|
||
<uni-forms-item label="附件">
|
||
<div class="upload-container">
|
||
<div class="upload" @click="uploadImg2" v-if="imgList2.length < 3">+</div>
|
||
<div class="image-preview" v-for="(img, index) in imgList2" :key="index">
|
||
<image :src="img.url" mode="aspectFill"></image>
|
||
<view class="delete-btn" @click.stop="deleteImage2(index)">×</view>
|
||
</div>
|
||
</div>
|
||
</uni-forms-item>
|
||
</uni-forms>
|
||
</view>
|
||
</scroll-view> -->
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from 'vue'
|
||
import { partTypeTreeList, getSupplierList, getRepairerListApi, saveRepairRow } from '@/services/repair/repair.js'
|
||
import {
|
||
saveLossAssessmentRow
|
||
} from '@/services/repair/equipAssessment.js'
|
||
import { baseURL } from '@/utils/http'
|
||
import treeSelect from '../tree-select/tselectTwo2.vue';
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { decryptWithSM4, encryptWithSM4, hashWithSM3AndSalt } from '@/utils/sm'
|
||
import PreviewImg from '@/components/PreviewImg/index.vue'
|
||
// const query = defineProps() // 获取上级页面传递的路由参数
|
||
// const queryParams = JSON.parse(query.queryParams)
|
||
|
||
// 浮点数运算辅助函数
|
||
const floatUtils = {
|
||
// 加法,避免精度问题
|
||
add: (...nums) => {
|
||
return parseFloat(nums.reduce((sum, num) => {
|
||
const value = Number(num || 0);
|
||
if (isNaN(value)) return sum;
|
||
return parseFloat((sum + value).toFixed(6));
|
||
}, 0).toFixed(6));
|
||
},
|
||
|
||
// 减法,避免精度问题
|
||
subtract: (a, b) => {
|
||
const aVal = Number(a || 0);
|
||
const bVal = Number(b || 0);
|
||
if (isNaN(aVal) || isNaN(bVal)) return 0;
|
||
return parseFloat((aVal - bVal).toFixed(6));
|
||
},
|
||
|
||
// 比较是否相等(考虑浮点数误差)
|
||
isEqual: (a, b) => {
|
||
const aVal = Number(a);
|
||
const bVal = Number(b);
|
||
if (isNaN(aVal) || isNaN(bVal)) return false;
|
||
return Math.abs(aVal - bVal) < 1e-10;
|
||
},
|
||
|
||
// 格式化数字,避免极小值
|
||
format: (num) => {
|
||
const value = Number(num);
|
||
if (isNaN(value)) return 0;
|
||
const formatted = parseFloat(value.toFixed(6));
|
||
return Math.abs(formatted) < 1e-10 ? 0 : formatted;
|
||
},
|
||
|
||
// 乘法,避免精度问题
|
||
multiply: (a, b) => {
|
||
const aVal = Number(a || 0);
|
||
const bVal = Number(b || 0);
|
||
if (isNaN(aVal) || isNaN(bVal)) return 0;
|
||
return parseFloat((aVal * bVal).toFixed(6));
|
||
},
|
||
|
||
// 检查数值是否为空或0
|
||
isEmptyOrZero: (num) => {
|
||
const value = Number(num);
|
||
if (isNaN(value)) return true;
|
||
return Math.abs(value) < 1e-10;
|
||
},
|
||
|
||
// 检查数值是否为空
|
||
isEmpty: (num) => {
|
||
// 检查是否为 null 或 undefined
|
||
if (num === null || num === undefined) {
|
||
return true;
|
||
}
|
||
|
||
// 检查是否为空字符串
|
||
if (typeof num === 'string' && num.trim() === '') {
|
||
return true;
|
||
}
|
||
|
||
// 转换为数字
|
||
const value = Number(num);
|
||
|
||
// 检查是否为 NaN
|
||
if (isNaN(value)) {
|
||
return true;
|
||
}
|
||
|
||
// 检查是否为 0(根据需求,0 可能视为空或非空)
|
||
// 这里返回 false,因为 0 是一个有效的数字值
|
||
return false;
|
||
},
|
||
};
|
||
|
||
const queryParams = ref({})
|
||
const rowData = ref({})
|
||
onLoad((options) => {
|
||
queryParams.value = JSON.parse(options.queryParams)
|
||
rowData.value = JSON.parse(options.queryParams)
|
||
})
|
||
|
||
|
||
const { userInfo } = JSON.parse(uni.getStorageSync('member'))
|
||
|
||
//获取维修人员
|
||
const repairPersonData = ref([])
|
||
const repairPerson = ref("")
|
||
const getRepairerListData = async () => {
|
||
const res = await getRepairerListApi({})
|
||
console.log(res)
|
||
repairPersonData.value = res.data.map((item) => {
|
||
let obj = {
|
||
value: item.userId,
|
||
text: item.repairer,
|
||
}
|
||
return obj
|
||
})
|
||
// 如果 userInfo.userId 在repairPersonData.value 数组中说明是维修人员,则默认选中, 否则默认选中第一个
|
||
if (
|
||
repairPersonData.value &&
|
||
repairPersonData.value.findIndex((v) => v.value === userInfo.userId) > -1
|
||
) {
|
||
repairPerson.value = userInfo.userId
|
||
} else {
|
||
repairPerson.value = repairPersonData.value[0].value
|
||
}
|
||
}
|
||
getRepairerListData()
|
||
|
||
const remark = ref("") //备注
|
||
|
||
const costAll = ref("") //费用合计
|
||
|
||
const imgBeseUrl = ref("") //图片展示
|
||
const bmFileInfos = ref([])//图片数组
|
||
|
||
//配件树
|
||
const partTreeData = ref([])
|
||
const defaultProps = ref({
|
||
id: 'id',
|
||
children: 'children',
|
||
label: 'label'
|
||
})
|
||
const getPartTreeData = async () => {
|
||
const res = await partTypeTreeList({})
|
||
console.log("yyyyyyyyy",res)
|
||
partTreeData.value = res.data;
|
||
}
|
||
getPartTreeData()
|
||
// 配件清除
|
||
const clearPart = (index) =>{
|
||
partItems.value[index].partId=""
|
||
}
|
||
|
||
const treeSelectRef = ref([])
|
||
|
||
|
||
//配件选中
|
||
const partTreeChange = (val, index) => {
|
||
console.log(val);
|
||
const isHas = partItems.value.findIndex(v => v.partId === val.id);
|
||
if (isHas > -1) { // 不可选择相同的配件
|
||
console.log(isHas);
|
||
treeSelectRef.value[index].clearInput();
|
||
partItems.value[index].partId = "";
|
||
partItems.value[index].partNum = 0;
|
||
partItems.value[index].partPrice = 0;
|
||
} else {
|
||
partItems.value[index].partId = val.id;
|
||
partItems.value[index].partNum = 0;
|
||
partItems.value[index].partPrice = val.buyPrice
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
//内部维修
|
||
const formLeft = ref({
|
||
repairNum: 0,
|
||
})
|
||
const partItems = ref([{ partNum: '', partType: 0, partId: '',partPrice:'',repairNum:0}])
|
||
const addPart = () => {
|
||
partItems.value.push({ partNum: '', partType: 0, partId: '', partPrice:'',repairNum:0})
|
||
calculateCostAll();
|
||
}
|
||
const delPart = (index) => {
|
||
if (partItems.value.length > 1) {
|
||
partItems.value.splice(index, 1)
|
||
calculateCostAll();
|
||
}
|
||
}
|
||
const partIds = ref([])
|
||
|
||
|
||
|
||
const imgBeseUrl2 = ref("") //图片展示
|
||
const imgList2 = ref([]) // 图片列表,用于回显
|
||
const fileData = ref({fileList:[]})
|
||
//上传
|
||
const uploadImg2 = () => {
|
||
uni.showActionSheet({
|
||
itemList: ['拍照', '从相册选择'],
|
||
success: (res) => {
|
||
if (res.tapIndex === 0) {
|
||
getCameraFj()
|
||
} else if (res.tapIndex === 1) {
|
||
// 从相册选择
|
||
getPhotoFj()
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('操作菜单选择失败:', err)
|
||
},
|
||
})
|
||
}
|
||
|
||
const generateRandomString = (length) => {
|
||
let result = '';
|
||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||
const charactersLength = characters.length;
|
||
for (let i = 0; i < length; i++) {
|
||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 附件拍照
|
||
const getCameraFj = () => {
|
||
navigator.camera.getPicture(onCameraSuccessFj, onCameraErrorFj, {
|
||
quality: 50,
|
||
destinationType: window.Camera.DestinationType.DATA_URL,
|
||
sourceType: window.Camera.PictureSourceType.CAMERA,
|
||
})
|
||
}
|
||
// 附件从相册选择
|
||
const getPhotoFj = () => {
|
||
navigator.camera.getPicture(onCameraSuccessFj, onCameraErrorFj, {
|
||
quality: 50,
|
||
destinationType: window.Camera.DestinationType.DATA_URL,
|
||
sourceType: window.Camera.PictureSourceType.SAVEDPHOTOALBUM,
|
||
})
|
||
}
|
||
|
||
const onCameraErrorFj = (message) => {
|
||
console.log(message)
|
||
}
|
||
|
||
const onCameraSuccessFj = (file) => {
|
||
uploadSignUrlFj(file)
|
||
}
|
||
|
||
const uploadSignUrlFj = (file) => {
|
||
const base64Data = file
|
||
uni.request({
|
||
url: '/file/uploadBase64',
|
||
method: 'POST',
|
||
data: {
|
||
base64File:base64Data,
|
||
fileName: `${generateRandomString(10)}_${Date.now()}.png`,
|
||
fileType: 'image/png'
|
||
},
|
||
success: (uploadRes) => {
|
||
if(!uploadRes.data.code){
|
||
uploadRes = JSON.parse(decryptWithSM4(uploadRes.data))
|
||
}else{
|
||
uploadRes = uploadRes.data
|
||
}
|
||
if (uploadRes.code && uploadRes.code == 200) {
|
||
imgList2.value.push({
|
||
url: uploadRes.data.url, // Show local path first
|
||
serverUrl: uploadRes.data.url // Store server URL
|
||
})
|
||
// bmFileInfos.value.push({
|
||
// name: uploadRes.data.name,
|
||
// url: uploadRes.data.url,
|
||
// taskType: '10'
|
||
// })
|
||
uni.showToast({ title: '上传成功', icon: 'none' })
|
||
} else {
|
||
uni.showToast({ title: '上传失败', icon: 'none' })
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.error('上传失败', err)
|
||
uni.showToast({ title: '上传失败', icon: 'none' })
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
//删除图片
|
||
const deleteImage2 = (index) => {
|
||
imgList2.value.splice(index, 1)
|
||
fileData.value.fileList.splice(index, 1)
|
||
}
|
||
|
||
// 验证配件数据
|
||
const validateParts = () => {
|
||
let isValid = true;
|
||
let errorMessage = "";
|
||
|
||
for (let i = 0; i < partItems.value.length; i++) {
|
||
const item = partItems.value[i];
|
||
|
||
// 检查配件单价不为空时,配件数量不能为空或0
|
||
const partPrice = floatUtils.format(item.partPrice);
|
||
const partNum = floatUtils.format(item.partNum);
|
||
|
||
// 如果配件单价有值(不为0)
|
||
if (!floatUtils.isEmptyOrZero(partPrice)) {
|
||
// 配件数量为空或0
|
||
if (floatUtils.isEmptyOrZero(partNum)) {
|
||
isValid = false;
|
||
errorMessage = `第${i + 1}行配件单价已填写,请填写配件数量!`;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果配件数量有值(不为0)
|
||
if (!floatUtils.isEmptyOrZero(partNum)) {
|
||
// 配件单价为空或0
|
||
if (floatUtils.isEmpty(partPrice)) {
|
||
isValid = false;
|
||
errorMessage = `第${i + 1}行配件数量已填写,请填写配件单价!`;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// // 检查配件类型是否选择(如果配件数量或单价有值)
|
||
// if ((!floatUtils.isEmptyOrZero(partNum) || !floatUtils.isEmptyOrZero(partPrice)) && !item.partId) {
|
||
// isValid = false;
|
||
// errorMessage = `第${i + 1}行请选择配件类型!`;
|
||
// break;
|
||
// }
|
||
}
|
||
|
||
if (!isValid) {
|
||
uni.showToast({ title: errorMessage, icon: 'none' });
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
|
||
|
||
//数量维修保存
|
||
const saveNumAll = async () => {
|
||
console.log(rowData.value)
|
||
if(repairPerson.value==""){
|
||
uni.showToast({ title: '请先选择维修人员!', icon: 'none' })
|
||
}
|
||
// else if(partItems.value.some(item => Number(item.repairNum) === 0)){
|
||
// uni.showToast({ title: '存在定损数量为 0 的项,请检查!', icon: 'none' })
|
||
// }
|
||
// else if(partItems.value.findIndex(v => v.partId == '')>-1){
|
||
// uni.showToast({ title: '请选择配件!', icon: 'none' })
|
||
// }else if(partItems.value.some(item => Number(item.partNum) === 0)){
|
||
// uni.showToast({ title: '存在配件数量为 0 的项,请检查!', icon: 'none' })
|
||
// }
|
||
else {
|
||
// 验证配件数据
|
||
if (!validateParts()) {
|
||
return;
|
||
}
|
||
|
||
uni.showLoading({
|
||
title: '提交中...',
|
||
mask: true
|
||
})
|
||
try {
|
||
//维修人员
|
||
rowData.value.repairDeviceList[0].repairList=[
|
||
{
|
||
repairer:repairPerson.value,
|
||
remark:remark.value,
|
||
fileList:fileData.value.fileList
|
||
}
|
||
]
|
||
//提交
|
||
for (let i = 0; i < partItems.value.length; i++) {
|
||
rowData.value.repairDeviceList[0].numberInRepairPartList.push({
|
||
partType: partItems.value[i].partType,
|
||
partId: partItems.value[i].partId,
|
||
partNum: partItems.value[i].partNum,
|
||
partPrice: partItems.value[i].partPrice,
|
||
dsNum:partItems.value[i].repairNum,
|
||
})
|
||
}
|
||
console.log("yyyyyyyyyy",rowData.value)
|
||
const response = await saveLossAssessmentRow(rowData.value.repairDeviceList)
|
||
// 隐藏Loading
|
||
uni.hideLoading()
|
||
if (response.code == 200) {
|
||
uni.showToast({ title: '定损成功', icon: 'none' })
|
||
setTimeout(() => {
|
||
uni.navigateBack({
|
||
delta: 1, // 返回到已存在的页面
|
||
})
|
||
}, 1000)
|
||
} else {
|
||
uni.showToast({
|
||
title: response.msg || '提交失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
// saveLossAssessmentRow(rowData.value.repairDeviceList).then(async (response) => {
|
||
// console.log("yxxxxxyyyy",response)
|
||
// if (response.code === 200) {
|
||
// uni.showToast({ title: '定损成功', icon: 'none' })
|
||
// setTimeout(() => {
|
||
// uni.navigateBack({
|
||
// delta: 1, // 返回到已存在的页面
|
||
// })
|
||
// }, 1000)
|
||
// }
|
||
// })
|
||
} catch (error) {
|
||
// 隐藏Loading
|
||
uni.hideLoading()
|
||
|
||
console.error('定损提交失败:', error)
|
||
uni.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算可使用的最大定损数量
|
||
// const maxRepairNum = computed(() => {
|
||
// return queryParams.value.typeRepairNum - queryParams.value.typeRepairedNum - queryParams.value.typeScrapNum;
|
||
// });
|
||
|
||
const maxRepairNum = computed(() => {
|
||
const total = floatUtils.subtract(
|
||
queryParams.value.typeRepairNum,
|
||
floatUtils.add(queryParams.value.typeRepairedNum, queryParams.value.typeScrapNum)
|
||
);
|
||
return floatUtils.format(total);
|
||
});
|
||
|
||
// 计算当前所有定损数量之和
|
||
// const currentTotalRepairNum = computed(() => {
|
||
// let total = 0;
|
||
// partItems.value.forEach(item => {
|
||
// total += Number(item.repairNum) || 0;
|
||
// });
|
||
// return total;
|
||
// });
|
||
|
||
const currentTotalRepairNum = computed(() => {
|
||
return partItems.value.reduce((sum, item) => {
|
||
return floatUtils.add(sum, item.repairNum || 0);
|
||
}, 0);
|
||
});
|
||
|
||
|
||
// 数量框change事件
|
||
const repairCheckNum1 = (index) => {
|
||
setTimeout(() => {
|
||
const item = partItems.value[index];
|
||
|
||
// 使用辅助函数格式化输入值
|
||
const originalValue = item.repairNum;
|
||
const cleanedValue = Number(String(originalValue).replace(/[^\d.]/g, ''));
|
||
const formattedValue = floatUtils.format(cleanedValue);
|
||
|
||
item.repairNum = formattedValue < 0 ? 0 : formattedValue;
|
||
|
||
// 计算当前所有项的总和
|
||
const currentTotal = partItems.value.reduce((sum, it, idx) => {
|
||
const value = idx === index ? item.repairNum : (it.repairNum || 0);
|
||
return floatUtils.add(sum, value);
|
||
}, 0);
|
||
|
||
const maxNum = maxRepairNum.value;
|
||
if (currentTotal > maxNum && !floatUtils.isEqual(currentTotal, maxNum)) {
|
||
uni.showToast({
|
||
title: '已达到当前物资最大定损数量!',
|
||
icon: 'none',
|
||
});
|
||
|
||
// 计算超出的部分
|
||
const excess = floatUtils.subtract(currentTotal, maxNum);
|
||
|
||
// 从当前项中减去超出的部分
|
||
const newValue = floatUtils.format(
|
||
floatUtils.subtract(item.repairNum, excess)
|
||
);
|
||
|
||
// 确保新值不会变成负数
|
||
item.repairNum = Math.max(0, newValue);
|
||
}
|
||
|
||
// 重新计算费用合计
|
||
calculateCostAll();
|
||
|
||
|
||
|
||
// item.repairNum = Number(String(item.repairNum).replace(/[^\d.]/g, ''));
|
||
// if (Number(item.repairNum) < 0) {
|
||
// item.repairNum = 0;
|
||
// }
|
||
//
|
||
// if (currentTotalRepairNum.value > maxRepairNum.value) {
|
||
// uni.showToast({
|
||
// title: '已达到当前物资最大定损数量!',
|
||
// icon: 'none',
|
||
// });
|
||
// // 计算需要减少的数量
|
||
// const excess = currentTotalRepairNum.value - maxRepairNum.value;
|
||
// // 从当前修改的项中减去超出的部分
|
||
// item.repairNum = Math.max(0, item.repairNum - excess);
|
||
// }
|
||
//
|
||
// // 重新计算费用合计
|
||
// calculateCostAll();
|
||
}, 500);
|
||
};
|
||
|
||
// 配件框change事件
|
||
const partCheckNum = (item) => {
|
||
setTimeout(() => {
|
||
item.partNum = Number(String(item.partNum).replace(/[^\d.]/g, ''))
|
||
// 重新计算费用合计
|
||
calculateCostAll();
|
||
}, 500)
|
||
}
|
||
// 金额框change事件
|
||
const costCheckNum = (item) => {
|
||
setTimeout(() => {
|
||
item.partPrice = Number(String(item.partPrice).replace(/[^\d.]/g, ''))
|
||
// 重新计算费用合计
|
||
calculateCostAll();
|
||
}, 500)
|
||
}
|
||
|
||
// 计算费用合计
|
||
const calculateCostAll = () => {
|
||
let totalCost = 0;
|
||
partItems.value.forEach(item => {
|
||
totalCost += item.partNum * item.partPrice;
|
||
});
|
||
costAll.value = totalCost.toFixed(2); // 保留两位小数
|
||
}
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.upload-container {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
|
||
.upload {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
background-color: #f7f8fa;
|
||
border: 2rpx dashed #d9d9d9;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 48rpx;
|
||
color: #bfbfbf;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.image-preview {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
position: relative;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.image-preview image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.delete-btn {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
color: white;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
border-bottom-left-radius: 12rpx;
|
||
font-size: 32rpx;
|
||
line-height: 32rpx;
|
||
}
|
||
|
||
.uploading-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.3);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
color: white;
|
||
font-size: 24rpx;
|
||
}
|
||
.page-container {
|
||
display: flex;
|
||
height: 100vh;
|
||
padding: 24rpx;
|
||
flex-direction: column;
|
||
background-color: #f7f8fa;
|
||
|
||
// 基本信息卡片
|
||
.table-list-item {
|
||
background-color: #fff;
|
||
border-radius: 20rpx;
|
||
padding: 24rpx;
|
||
margin-bottom: 24rpx;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
|
||
// 标题样式
|
||
h2 {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #262626;
|
||
margin-bottom: 24rpx;
|
||
position: relative;
|
||
padding-left: 24rpx;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 6rpx;
|
||
height: 28rpx;
|
||
background: #3784fb;
|
||
border-radius: 6rpx;
|
||
}
|
||
}
|
||
|
||
// 信息行
|
||
.uni-row {
|
||
padding: 16rpx 0;
|
||
font-size: 28rpx;
|
||
border-bottom: 2rpx solid #f5f5f5;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.uni-col-6 {
|
||
color: #8c8c8c;
|
||
}
|
||
|
||
.cont {
|
||
color: #262626;
|
||
}
|
||
}
|
||
|
||
// 表单样式
|
||
:deep(.uni-forms-item) {
|
||
padding: 24rpx 0;
|
||
margin-bottom: 0;
|
||
border-bottom: 2rpx solid #f5f5f5;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.uni-forms-item__label {
|
||
color: #8c8c8c;
|
||
}
|
||
|
||
.uni-easyinput__content {
|
||
background-color: #f7f8fa;
|
||
border: 2rpx solid #e8e8e8;
|
||
border-radius: 12rpx;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
transition: all 0.3s ease;
|
||
|
||
&:focus-within {
|
||
border-color: #3784fb;
|
||
box-shadow: 0 0 0 2rpx rgba(55, 132, 251, 0.1);
|
||
}
|
||
}
|
||
|
||
// 下拉选择框样式
|
||
.uni-data-select {
|
||
.uni-select {
|
||
border: 2rpx solid #e8e8e8;
|
||
border-radius: 12rpx;
|
||
height: 88rpx;
|
||
padding: 0 24rpx;
|
||
transition: all 0.3s ease;
|
||
|
||
&:focus-within {
|
||
border-color: #3784fb;
|
||
box-shadow: 0 0 0 2rpx rgba(55, 132, 251, 0.1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 操作按钮组
|
||
.search-form {
|
||
margin-bottom: 24rpx;
|
||
background: #fff !important;
|
||
border-radius: 20rpx !important;
|
||
padding: 24rpx !important;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||
|
||
.search {
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
text-align: center;
|
||
color: #fff;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
border-radius: 12rpx;
|
||
transition: all 0.3s ease;
|
||
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
|
||
box-shadow: 0 6rpx 20rpx rgba(46, 204, 113, 0.2);
|
||
|
||
&:active {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 加减按钮
|
||
.operation-btns {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
margin-left: 24rpx;
|
||
|
||
span {
|
||
width: 90rpx;
|
||
height: 48rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 8rpx;
|
||
font-size: 32rpx;
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
transform: scale(0.95);
|
||
opacity: 0.8;
|
||
}
|
||
|
||
// 添加按钮
|
||
&.add {
|
||
color: #2ecc71;
|
||
background-color: rgba(46, 204, 113, 0.1);
|
||
}
|
||
|
||
// 删除按钮
|
||
&.remove {
|
||
color: #e74c3c;
|
||
background-color: rgba(231, 76, 60, 0.1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 加载提示文字
|
||
.loading-text {
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
padding: 20rpx 0;
|
||
}
|
||
|
||
.outbound-btn {
|
||
width: 70%;
|
||
margin: 25rpx auto;
|
||
height: 65rpx;
|
||
line-height: 65rpx;
|
||
text-align: center;
|
||
background-color: #19be6b;
|
||
border-radius: 12rpx;
|
||
color: #fff;
|
||
}
|
||
|
||
// 上传区域样式
|
||
.upload {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
background-color: #f7f8fa;
|
||
border: 2rpx dashed #d9d9d9;
|
||
border-radius: 12rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 48rpx;
|
||
color: #bfbfbf;
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
background-color: #f0f0f0;
|
||
border-color: #3784fb;
|
||
}
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 12rpx;
|
||
object-fit: cover;
|
||
}
|
||
}
|
||
|
||
.my-confirm-notice {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
z-index: 998;
|
||
/* 这里防止当用户长按屏幕,出现的黑色背景色块,以及 iPhone 横平时字体的缩放问题 */
|
||
-webkit-text-size-adjust: 100%;
|
||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.confirm-content-wrap1 {
|
||
position: relative;
|
||
left: 0;
|
||
right: 0;
|
||
// width: 60%;
|
||
width: 90%;
|
||
height: 247px;
|
||
margin: 0 auto;
|
||
background-color: #FFFFFF;
|
||
border-radius: 10px;
|
||
z-index: 999;
|
||
user-select: none;
|
||
}
|
||
|
||
.content_margin {
|
||
padding: 15px 0px 15px 0px;
|
||
}
|
||
|
||
.flex_row_center {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.label_text {
|
||
font-size: 28rpx;
|
||
color: #555555;
|
||
}
|
||
|
||
.inputStyle {
|
||
border: #bababa 0.5px solid;
|
||
width: 169px;
|
||
height: 34px;
|
||
box-sizing: border-box;
|
||
font-size: 13px;
|
||
padding: 3px 2px;
|
||
}
|
||
|
||
.top_level {
|
||
position: fixed;
|
||
z-index: 100;
|
||
width: 90%;
|
||
}
|
||
|
||
.ly_tree_style {
|
||
border: #F5F5F5 1px solid;
|
||
width: 169px;
|
||
}
|
||
</style>
|