bonus-material-app/src/pages/repair/repairManage/batch-repair.vue

834 lines
26 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="page-container">
<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">{{ selectedCodesDisplay }}</view>
</uni-col>
</uni-row>
<uni-row :gutter="24" style="display: flex;align-items: center">
<uni-col :span="6">维修人员</uni-col>
<uni-col :span="18">
<uni-data-select :localdata="repairPersonData" v-model="repairPerson" placeholder="请选择维修人员" />
</uni-col>
</uni-row>
<uni-row
:gutter="24"
class="search-form"
style="background: #fff; padding: 10px; margin: 5px; border-radius: 5px"
>
<uni-col :span="24">
<view class="btnBox complete" @click="saveCode"> 提交 </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">
<uni-forms-item label="维修人员">
<uni-data-select
:localdata="repairPersonData"
v-model="repairPerson"
placeholder="请选择维修人员"
/>
</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>
</view> -->
<!-- 内部维修 -->
<view class="table-list-item" v-if="repairType == 1">
<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="formLeft.repairNum"/>
</uni-forms-item> -->
<div v-for="(item, index) in partItems" :key="index">
<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-data-select
:localdata="isChargeList"
v-model="item.partType"
placeholder="请选择是否收费"
:clear="false"
/>
<div class="operation-btns">
<span class="add" @click="addPart">+</span>
<span class="remove" @click="delPart(index)">-</span>
</div>
</div>
</uni-forms-item>
<uni-forms-item label="备注">
<uni-easyinput placeholder="备注" v-model="item.remark" />
</uni-forms-item>
<uni-forms-item label="附件">
<div class="upload" @click="uploadImg(item)" v-if="item.imgBeseUrl == ''">+</div>
<div class="upload" @click="uploadImg(item)" v-else>
<image
:src="item.imgBeseUrl"
style="width: 160rpx; height: 160rpx"
mode=""
></image>
</div>
</uni-forms-item>
<div style="border-bottom: 1 solid #333;"></div>
</div>
</uni-forms>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { computed, ref, reactive,nextTick } from 'vue'
import {
partTypeTreeList,
getSupplierList,
getRepairerListApi,
saveRepairRow,
getScrapReasonList,
getPartItemApi,
} from '@/services/repair/repair.js'
import { baseURL } from '@/utils/http'
import treeSelect from '../tree-select/tselectTwo.vue'
import { onLoad,onShow } from '@dcloudio/uni-app'
const queryParams = ref({})
const rowIndex = ref(-1)
// 计算属性:将选中的编码用逗号拼接显示
const selectedCodesDisplay = computed(() => {
if (queryParams.value.selectedCodes && queryParams.value.selectedCodes.length > 0) {
// 清理可能存在的双引号和其他字符
const cleanCodes = queryParams.value.selectedCodes.map(code => {
// 如果是字符串,移除首尾的双引号
if (typeof code === 'string') {
return code.replace(/^["']|["']$/g, '')
}
return String(code)
})
return cleanCodes.join(', ')
}
return ''
})
// onLoad((options) => {
// console.log(options)
// queryParams.value = JSON.parse(options.queryParams)
// rowIndex.value = options.rowIndex
// console.log(queryParams.value)
// getScrapReasonListData()
// })
onLoad((options) => {
queryParams.value = JSON.parse(decodeURIComponent(options.batchParams))
if (queryParams.value.selectedCodes) {
queryParams.value.selectedCodes = queryParams.value.selectedCodes.map(code => {
// 清理双引号
if (typeof code === 'string') {
return code.replace(/^["']|["']$/g, '')
}
return String(code)
})
}
console.log('批量维修参数:', queryParams.value)
console.log('选中的设备:', queryParams.value.selectedDevices)
console.log('选中的编码:', queryParams.value.selectedCodes)
})
onShow(() => {
getPartTreeData()
//获取定损的配件信息
getPartItemData()
})
const repairType = ref(1)
const changeTab = async (e) => {
repairType.value = e
}
const isChargeList = ref([
{ value: 1, text: '是' },
{ value: 0, text: '否' },
])
const { userInfo } = JSON.parse(uni.getStorageSync('member'))
// console.log('🚀 ~ userInfo:', userInfo)
//获取维修人员
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 imgBeseUrl = ref('') //图片展示
const bmFileInfos = ref([]) //图片数组
//上传
const uploadImg = (item) => {
// if(item.imgBeseUrl){
// uni.showToast({ title: '图片已上传', icon: 'none' })
// return
// }
// const count = 1 - imgList.value.length
// if (count <= 0) {
// uni.showToast({ title: '最多上传1张图片', icon: 'none' })
// return
// }else{
uni.showActionSheet({
itemList: ['拍照', '从相册选择'],
success: (res) => {
if (res.tapIndex === 0) {
getCameraFj(item)
} else if (res.tapIndex === 1) {
// 从相册选择
getPhotoFj(item)
}
},
fail: (err) => {
console.error('操作菜单选择失败:', err)
},
})
// }
}
// 附件拍照
const getCameraFj = (item) => {
navigator.camera.getPicture((file) => onCameraSuccessFj(file, item), onCameraErrorFj, {
quality: 50,
destinationType: window.Camera.DestinationType.DATA_URL,
sourceType: window.Camera.PictureSourceType.CAMERA,
})
}
// 附件从相册选择
const getPhotoFj = (item) => {
navigator.camera.getPicture((file) => onCameraSuccessFj(file, item), onCameraErrorFj, {
quality: 50,
destinationType: window.Camera.DestinationType.DATA_URL,
sourceType: window.Camera.PictureSourceType.SAVEDPHOTOALBUM,
})
}
const onCameraErrorFj = (message) => {
console.log(message)
}
const onCameraSuccessFj = (file, item) => {
uploadSignUrlFj(file, item)
}
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 uploadSignUrlFj = (file, item) => {
const base64Data = file
uni.request({
url: '/file/uploadBase64',
method: 'POST',
data: {
base64File:base64Data,
fileName: `${generateRandomString(10)}_${Date.now()}.png`,
fileType: 'image/png'
},
success: (uploadRes) => {
console.log("xxxxxxxxxx",uploadRes)
if(!uploadRes.data.code){
uploadRes = JSON.parse(decryptWithSM4(uploadRes.data))
}else{
uploadRes = JSON.parse(uploadRes.data)
}
if (uploadRes.code && uploadRes.code == 200) {
let obj = {
name: uploadRes.data.name,
url: uploadRes.data.url,
}
item.bmFileInfos = [obj]
item.imgBeseUrl = uploadRes.data.url
// imgList.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 partTreeData = ref([])
const defaultProps = ref({
id: 'id',
children: 'children',
label: 'label',
})
const getPartTreeData = async () => {
const res = await partTypeTreeList({})
partTreeData.value = res.data
}
const treeSelectRef = ref([])
const clearPart = (index) => {
partItems.value[index].partId = ''
}
const partTreeChange = (val, index) => {
const isHas = partItems.value.findIndex((v) => v.partId === val.id)
if (isHas > -1) {
//不可选择相同的配件
console.log(isHas)
uni.showToast({ title: '已存在该配件类型', icon: 'none' })
treeSelectRef.value[index].clearInput()
partItems.value[index].partId = ''
partItems.value[index].storageNum = ''
partItems.value[index].partNum = 0
} else {
partItems.value[index].partId = val.id
partItems.value[index].storageNum = val.storageNum
partItems.value[index].partNum = 0
}
}
//内部维修
const partItems = ref([{ partNum: '', partType: 0, partId: '', storageNum: '', remark: '', fileList: [] }])
const getPartItemData = async () => {
console.log("bbbbbbbbbbbbbbbbb",queryParams.value.ids)
const res = await getPartItemApi({
ids: queryParams.value.ids,
})
console.log("cccccccccccc",res)
if(res.data && res.data.length > 0) {
// 1. 先设置数据
partItems.value = res.data.map(item => ({
...item,
partType: item.partId ? 1 : 0 // 假设 partType 表示是否有 partId
}));
// 使用 nextTick 确保组件渲染完成
nextTick(() => {
partItems.value.forEach((item, index) => {
if (item.partId) {
console.log("尝试设置 partId", item.partId)
if (treeSelectRef.value[index] && treeSelectRef.value[index].setSelectedValue) {
treeSelectRef.value[index].setSelectedValue(item.partId);
} else {
console.warn(`treeSelectRef[${index}] 没有 setSelectedValue 方法`)
}
}
});
});
}
}
const addPart = () => {
partItems.value.push({ partNum: '', partType: 0, partId: '', storageNum: '', remark: '', fileList: [] })
}
const delPart = (index) => {
if (partItems.value.length > 1) {
partItems.value.splice(index, 1)
}
}
const partIds = ref([])
const formLeft = ref({
// repairNum:0
})
const partItemsMiddle = ref([
{ partName: '', supplierId: '', partNum: 0, partPrice: 0, partType: 0 },
])
const addMidPart = () => {
partItemsMiddle.value.push({
partName: '',
supplierId: '',
partNum: 0,
partPrice: 0,
partType: 0,
})
}
const delMidPart = (index) => {
if (partItemsMiddle.value.length > 1) {
partItemsMiddle.value.splice(index, 1)
}
}
const formMiddle = ref({
// supplierId:undefined,
// repairNum:0
})
//待报废
const formRight = ref({
// scrapNum:0,
scrapType: 0,
scrapId: null, // 报废原因id
scrapReason: '', // 报废原因
fileList: [],
})
const rowData = ref({})
// 配件框change事件
const partCheckNum = (item) => {
// console.log(item)
// console.log(item.partNum)
setTimeout(() => {
item.partNum = Number(String(item.partNum).replace(/[^\d.]/g, ''))
if (item.storageNum) {
// console.log(item.partNum)
// console.log(item.storageNum)
if (Number(item.partNum) >= Number(item.storageNum)) {
item.partNum = Number(item.storageNum)
}
}
}, 500)
}
// 金额框change事件
const costCheckNum = (item) => {
// console.log(item)
// console.log(item.partPrice)
setTimeout(() => {
item.partPrice = Number(String(item.partPrice).replace(/[^\d.]/g, ''))
}, 500)
}
//维修完成校验
const saveCode = () => {
if (repairType.value == 1) {
let index1 = partItems.value.findIndex((v) => v.partId == '')
let index2 = partItems.value.findIndex((v) => v.partNum == 0)
if (repairPerson.value == '') {
uni.showToast({ title: '请先选择维修人员!', icon: 'none' })
} else {
saveCodeApi()
}
}
}
const loading = ref(false)
//维修完成请求
const saveCodeApi = async () => {
if (loading.value) {
uni.showToast({ title: '请勿重复提交!', icon: 'none' })
}
loading.value = true
//请求接口
rowData.value = queryParams.value
// 遍历所有设备列表项,为每一项设置相同的值
for (let i = 0; i < rowData.value.repairDeviceList.length; i++) {
// 设置维修人员信息
rowData.value.repairDeviceList[i].repairList = [
{
repairer: repairPerson.value,
// remark: remark.value,
// fileList: bmFileInfos.value,
},
]
// 初始化配件列表数组(如果不存在)
if (!rowData.value.repairDeviceList[i].codeInRepairPartList) {
rowData.value.repairDeviceList[i].codeInRepairPartList = [];
}
// 添加配件信息
for (let j = 0; j < partItems.value.length; j++) {
rowData.value.repairDeviceList[i].codeInRepairPartList.push({
partType: partItems.value[j].partType,
partId: partItems.value[j].partId,
partNum: partItems.value[j].partNum,
storageNum: partItems.value[j].storageNum,
remark: partItems.value[j].remark,
fileList: partItems.value[j].bmFileInfos,
})
}
// 设置维修类型为1
rowData.value.repairDeviceList[i].repairType = 1;
}
// console.log('🚀 ~ saveCodeApi ~ 提交:', rowData.value.repairDeviceList)
const params = rowData.value.repairDeviceList.filter(item => queryParams.value.selectedCodes.includes(item.code))
console.log('🚀 ~ saveCodeApi ~ params:', params)
uni.showLoading({
title: '提交中...',
});
saveRepairRow(params).then(async (response) => {
if (response.code == 200) {
uni.showToast({ title: '保存成功', icon: 'none' })
uni.navigateBack({
delta: 2, // 返回到已存在的页面
})
}
loading.value = false
uni.hideLoading()
}).catch(() => {
loading.value = false
uni.hideLoading()
})
}
</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;
}
// 上传区域样式
.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;
}
}
.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;
margin-left: 10px;
&::before {
content: '';
position: absolute;
left: -10px;
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); */
.btnBox {
height: 88rpx;
line-height: 88rpx;
text-align: center;
color: #fff;
font-size: 28rpx;
font-weight: 600;
border-radius: 12rpx;
transition: all 0.3s ease;
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
&:active {
transform: scale(0.98);
opacity: 0.9;
}
// 内部维修按钮
&.internal {
background: linear-gradient(135deg, #2ecc71 0%, #27ae60 100%);
box-shadow: 0 6rpx 20rpx rgba(46, 204, 113, 0.2);
}
// 返厂维修按钮
&.return {
background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%);
box-shadow: 0 6rpx 20rpx rgba(243, 156, 18, 0.2);
}
// 待报废按钮
&.scrap {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
box-shadow: 0 6rpx 20rpx rgba(231, 76, 60, 0.2);
}
// 维修完成按钮
&.complete {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
box-shadow: 0 6rpx 20rpx rgba(52, 152, 219, 0.2);
}
}
}
// 加减按钮
.operation-btns {
display: flex;
align-items: center;
gap: 16rpx;
margin-left: 24rpx;
span {
width: 48rpx;
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;
}
</style>