bonus-material-app/src/pages/back/backNum.vue

820 lines
27 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>
<uni-nav-bar
status-bar
leftIcon="left"
title="退料数量"
backgroundColor="#dcf4ff"
:border="false"
fixed
@clickLeft="leftClick"
>
<!-- 使用右侧插槽自定义提交按钮 -->
<!-- <template #right>
<button
class="submit-btn"
@click="submitNum"
>
确定
</button>
</template> -->
</uni-nav-bar>
<view class="accept page-common">
<div class="card top-content" :class="{'is-expanded': isExpanded}">
<div class="card-header" @click="isExpanded = !isExpanded">
<span class="title">任务信息</span>
<uni-icons class="icon" type="down" size="19" :class="{'is-expanded': isExpanded}"></uni-icons>
</div>
<uni-forms :model="taskInfo" label-width="170rpx" :border="true">
<uni-forms-item label="单位:" name="unitName">
<span style="height: 100%;display: flex;align-items: center;">{{ taskInfo.unitName }}</span>
</uni-forms-item>
<uni-forms-item label="工程:" name="proName">
<span style="height: 100%;display: flex;align-items: center;">{{ taskInfo.proName }}</span>
</uni-forms-item>
<uni-forms-item label="单号:" name="code">
<span style="height: 100%;display: flex;align-items: center;">{{ taskInfo.code }}</span>
</uni-forms-item>
<uni-forms-item label="人员:" name="backPerson">
<span style="height: 100%;display: flex;align-items: center;">{{ taskInfo.backPerson }}</span>
</uni-forms-item>
<uni-forms-item label="电话:" name="phone">
<span style="height: 100%;display: flex;align-items: center;">{{ taskInfo.phone }}</span>
</uni-forms-item>
</uni-forms>
</div>
<div class="card">
<div class="card-header">
<span class="title">退料物资</span>
<div class="header-right">
<span class="tip">已选 {{ typeList.length }} </span>
</div>
</div>
<div class="select-area">
<uni-row :gutter="24" style="display: flex; align-items: center">
<uni-col :span="12">
<view>
<!-- <uni-data-select v-model="typeId"
placeholder="请选择物资类型"
:localdata="maTypeSelectList" @change="getMaCode" filterable >
</uni-data-select> -->
<eselect
style="width: 100%; height: 90rpx"
v-model="typeId"
:options="maTypeSelectList"
@change="getMaCode"
@clear="clearTypeId"
></eselect>
</view>
</uni-col>
<uni-col :span="12">
<view>
<!-- <uni-data-select v-model="typeCode"
placeholder="请选择规格型号"
:localdata="maCodeSelectList" @change="selectMaCode">
</uni-data-select> -->
<eselect
style="width: 100%; height: 90rpx"
v-model="typeCode"
ref="treeSelect"
:options="maCodeSelectList"
@change="selectMaCode"
@clear=""
></eselect>
</view>
</uni-col>
</uni-row>
</div>
</div>
<uni-table border stripe emptyText="暂无更多数据">
<!-- 表头行 -->
<uni-tr>
<uni-th width="100px" style="font-size: 24rpx;" align="center">类型名称</uni-th>
<uni-th width="100px" style="font-size: 24rpx;" align="center">规格型号</uni-th>
<uni-th width="60px" style="font-size: 24rpx;" align="center">在用数</uni-th>
<uni-th width="90px" style="font-size: 24rpx;" align="center">退料数</uni-th>
<!-- <uni-th width="90px" style="font-size: 24rpx;" align="center">外观</uni-th> -->
<uni-th width="90px" style="font-size: 24rpx;" align="center">完好数量</uni-th>
<uni-th width="90px" style="font-size: 24rpx;" align="center">不合格数量</uni-th>
<uni-th width="80px" style="font-size: 24rpx;" align="center">附件</uni-th>
<uni-th width="70px" style="font-size: 24rpx;" align="center">操作</uni-th>
</uni-tr>
<!-- 表格数据行 -->
<uni-tr v-for="(item,index) in typeList" :key="item.id">
<uni-td style="font-size: 24rpx;text-align: center;">{{item.materialName}}</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">{{item.typeName}}</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">{{item.num}}</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">
<uni-easyinput
placeholder="退料数"
v-model="item.preNum"
type="number"
:clearable="false"
@blur="onChangeNumber(item)"
:styles="{width: '100rpx'}"
/>
</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">
<uni-easyinput
placeholder="完好数量"
v-model="item.goodNum"
type="number"
:clearable="false"
@change="changeNum(item)"
:styles="{width: '100rpx'}"
:disabled="!item.preNum"
/>
</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">
<uni-easyinput
placeholder="不合格数量"
v-model="item.badNum"
type="number"
:clearable="false"
@change="changeNum(item)"
:styles="{width: '100rpx'}"
:disabled="!item.preNum"
/>
</uni-td>
<!-- <uni-td style="font-size: 24rpx;text-align: center;">
<uni-easyinput
placeholder="外观"
v-model="item.apDetection"
:styles="{width: '100rpx'}"
/>
</uni-td> -->
<uni-td style="font-size: 24rpx;text-align: center;">
<text
style="color: #3784fb; text-decoration: underline; cursor: pointer;"
@click="viewImgs(item)"
>
{{ item.bmFileInfos && item.bmFileInfos.length ? item.bmFileInfos.length : 0 }}
</text>
</uni-td>
<uni-td style="font-size: 24rpx;text-align: center;">
<view class="uni-group">
<view class="action-btn" @click="uploadImg(item)">
<uni-icons type="camera" size="20" style="color: #3784fb;"/>
</view>
<view class="action-btn delete" @click="delRow(index)">
<uni-icons type="trash-filled" size="20" style="color: red;margin-left:10px;"/>
</view>
</view>
</uni-td>
</uni-tr>
</uni-table>
<div class="footer-btn">
<button class="btn-cont" @click="submitNum">确认</button>
</div>
</view>
<PopupConfirm ref="popupConfirm" />
</template>
<script setup>
import { ref, reactive } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getUseType,insertApp } from '../../services/back.js';
import eselect from '@/components/tree-select/eselect.vue'
import PopupConfirm from '@/components/PopupConfirm'
import {decryptWithSM4} from "../../utils/sm";
const taskInfo = ref({})
const maTypeSelectList = ref([])
const typeId = ref()//类型
const maCodeSelectList = ref([])
const typeCode = ref("")//规格型号
const typeList = ref([])
const isExpanded = ref(false)
const treeSelect = ref()
const popupConfirm = ref()
const leftClick = () => {
// 返回
uni.navigateBack({
delta: 1, // 返回到已存在的页面
})
}
//类下拉选
const getMaType = () => {
let obj = {
"agreementId":taskInfo.value.agreementId,
}
getUseType(obj).then(res => {
console.log(res)
maTypeSelectList.value = res.data.map(option => {
return {
// value:option.typeId,
// text:option.typeName,
id:option.typeId,
name:option.typeName,
parentId: 0
}
});
}).catch(error => {
console.log(error)
})
}
//规格
const getMaCode = (e) => {
console.log('🚀 ~ getMaCode ~ e:', e.id)
typeId.value = e.id
console.log('🚀 ~ getMaCode ~ typeId.value:', typeId.value)
let obj = {
"agreementId":taskInfo.value.agreementId,
"typeId":typeId.value
}
console.log('🚀 ~ getMaCode ~ obj:', obj)
getUseType(obj).then(res => {
console.log(res)
maCodeSelectList.value = res.data.map(option => {
let obj = {
...option,
bmFileInfos:[],
// value:option.typeId,
// text:option.typeName,
id:option.typeId,
name:option.typeName,
parentId: 0
}
return obj
});
}).catch(error => {
console.log(error)
})
}
const clearTypeId = () => {
typeId.value = ''
typeCode.value = ''
maCodeSelectList.value = []
treeSelect.value.showLabel = ''
console.log('🚀 ~ clearTypeId ~ treeSelect.value:', treeSelect.value)
}
//选择规格型号
const selectMaCode = (e) => {
console.log('🚀 ~ selectMaCode ~ e:', e)
// 不可重复添加
if(typeList.value.some(item => item.typeId == e.id)){
typeCode.value = '' // 清空绑定的值
treeSelect.value.showLabel = '' // 清空下拉框的显示文本
// 提示
uni.showToast({
title: '请勿重复添加',
icon: 'none'
});
return
}
maCodeSelectList.value.forEach(item=>{
console.log(item)
if(item.typeId==e.id){
typeList.value.unshift(item)
}
})
console.log(typeList.value)
// 选完型号后,清空型号选择框的值
typeCode.value = '' // 清空绑定的值
treeSelect.value.showLabel = '' // 清空下拉框的显示文本
}
//提交
const submitNum = async () => {
console.log(taskInfo.value)
console.log(typeList.value)
// 校验 preNum 必须大于 0
if (typeList.value.length === 0) {
console.log('🚀 ~ submitNum ~ typeList.value:', typeList.value)
uni.showToast({
title: '请添加退料物资',
icon: 'none',
})
return
}
for (let i = 0; i < typeList.value.length; i++) {
if (typeList.value[i].preNum <= 0 || !typeList.value[i].preNum) {
uni.showToast({
title: '退料数不能为空或等于0',
icon: 'none',
})
return
}
}
const { data: confirm } = await popupConfirm.value.openPopup()
if (!confirm) return
let obj = {
"backApplyInfo":taskInfo.value,
"backApplyDetailsList":typeList.value
}
if (obj.backApplyInfo.signUrl) delete obj.backApplyInfo.signUrl
if (obj.backApplyInfo.approveSignList) delete obj.backApplyInfo.approveSignList
insertApp(obj).then(res => {
console.log(res)
if(res.code==200){
uni.showToast({ title: '编辑成功', icon: 'none' })
const pages = getCurrentPages()
console.log('🚀 ~ submitNum ~ pages:', pages)
// uni.redirectTo({ url: '/pages/back/index' })
uni.navigateBack({
delta: 1, // 返回到已存在的页面
})
}else{
uni.showToast({ title: res.msg, icon: 'none' })
}
}).catch(error => {
console.log(error)
})
}
//上传
const uploadImg = (item) => {
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 uploadImg = (item) => {
// // 检查是否在 Cordova 环境中
// if (window.cordova) {
// // Cordova 环境 - 使用方法二的方式
// navigator.camera.getPicture(
// (file) => {
// onCameraSuccess(file, item)
// },
// (message) => {
// console.log('选择图片失败', message)
// uni.showToast({ title: '选择图片失败', icon: 'none' })
// },
// {
// quality: 50,
// destinationType: window.Camera.DestinationType.DATA_URL,
// sourceType: window.Camera.PictureSourceType.SAVEDPHOTOALBUM,
// }
// )
// } else {
// // 非 Cordova 环境 - 保持原来的 uni.chooseImage 方式作为降级方案
// uni.chooseImage({
// count: 1,
// sizeType: ['original', 'compressed'],
// sourceType: ['album', 'camera'],
// success: res => {
// let imgFiles = res.tempFilePaths
// // 这里可以保持原来的 uni.uploadFile 方式,或者也改为 base64 方式
// uploadFileTraditional(imgFiles[0], item)
// }
// })
// }
// }
// // 相机成功回调
// const onCameraSuccess = (file, item) => {
// uploadSignUrl(file, item)
// }
// 使用 base64 方式上传
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,
}
if (!item.bmFileInfos) item.bmFileInfos = []
item.bmFileInfos.push(obj)
uni.showToast({ title: '上传成功', icon: 'none' })
} else {
uni.showToast({ title: '上传失败', icon: 'none' })
}
},
fail: (err) => {
console.error('上传失败', err)
uni.showToast({ title: '上传失败', icon: 'none' })
}
})
}
// // 生成随机文件名
// const fileName = `${generateRandomString(10)}_${Date.now()}.png`
//
// uni.request({
// // url: baseURL + "/file/uploadBase64", // app
// url: "/file/uploadBase64", // h5
// method: 'POST',
// data: {
// base64File: base64Data,
// fileName: fileName,
// fileType: 'image/png'
// },
// success: (uploadRes) => {
// // 处理响应数据
// let responseData = uploadRes.data
// if (typeof responseData === 'string') {
// responseData = JSON.parse(responseData)
// }
//
// console.log('上传成功', responseData.code)
// console.log('上传成功', responseData.data)
//
// if (responseData.code && responseData.code == 200) {
// let obj = {
// name: responseData.data.name,
// url: responseData.data.url
// }
//
// if (!item.bmFileInfos) item.bmFileInfos = []
// item.bmFileInfos.push(obj)
// uni.showToast({ title: '上传成功', icon: 'none' })
// } else {
// uni.showToast({ title: '上传失败', icon: 'none' })
// }
// },
// fail: (err) => {
// console.error('上传失败', err)
// uni.showToast({ title: '上传失败', icon: 'none' })
// }
// })
// }
// // 传统的文件上传方式(作为降级方案)
// const uploadFileTraditional = (filePath, item) => {
// uni.uploadFile({
// // url: baseURL+"/file/upload", // app
// url: "/file/upload", // h5
// filePath: filePath,
// name: 'file',
// success: (res) => {
// res = JSON.parse(res.data)
// console.log('上传成功', res.code)
// console.log('上传成功', res.data)
// if(res.code && res.code == 200){
// let obj = {
// name: res.data.name,
// url: res.data.url
// }
// if (!item.bmFileInfos) item.bmFileInfos = []
// item.bmFileInfos.push(obj)
// uni.showToast({ title: '上传成功', icon: 'none' })
// } else {
// uni.showToast({ title: '上传失败', icon: 'none' })
// }
// },
// 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 uploadImg = (item) => {
// uni.chooseImage({
// count: 1, //图片可选择数量
// sizeType: ['original', 'compressed'], //original 原图compressed 压缩图,默认二者都有
// sourceType: ['album', 'camera',], //album 从相册选图camera 使用相机,默认二者都有。
// success: res => {
// console.log(res)
// let imgFiles = res.tempFilePaths //图片的本地文件路径列表
// // imgBeseUrl.value = imgFiles[0]
// // console.log('本地地址', imgFiles)
// // console.log('请求地址', baseURL+"/file/upload")
// uni.uploadFile({
// // url: baseURL+"/file/upload",//app
// url: "/file/upload",//h5
// filePath: imgFiles[0],
// name: 'file',
// success: (res) => {
// res = JSON.parse(res.data)
// console.log('上传成功', res.code);
// console.log('上传成功', res.data);
// if(res.code&&res.code==200){
// let obj = {
// name:res.data.name,
// url:res.data.url
// }
// if (!item.bmFileInfos) item.bmFileInfos = []
// item.bmFileInfos.push(obj)
// // item.bmFileInfos.value = [obj]
// uni.showToast({ title: '上传成功', icon: 'none' })
// }else{
// // item.bmFileInfos.value = []
// uni.showToast({ title: '上传失败', icon: 'none' })
// }
// },
// fail: (err) => {
// console.error('上传失败', err);
// }
// });
// // this.$refs.vForm.clearValidate()
// }
// })
// }
const viewImgs = (item) => {
if (!item.bmFileInfos || item.bmFileInfos.length === 0) {
uni.showToast({ title: '暂无附件', icon: 'none' })
return
}
// 提取 URL 列表
const urls = item.bmFileInfos.map(f => f.url)
uni.previewImage({
urls: urls,
current: 0
})
}
//删除
const delRow = (index) => {
console.log(index)
console.log(typeList.value)
typeList.value.splice(index,1)
}
// 数量框change事件
const onChangeNumber = (item) => {
console.log(item)
console.log(item.preNum)
console.log(item.preNum)
let maxNum = Number(item.num)
// outboundNum.value
setTimeout(()=>{
if(item.unitValue==1){
item.preNum = Number(String(item.preNum).replace(/[^\d.]/g,''))
}else{
item.preNum = Number(String(item.preNum).replace(/[^\d]/g,''))
}
if (Number(item.preNum)<= 0) {
item.preNum = 0;
}
if (Number(item.preNum) > maxNum) {
uni.showToast({
title: '已达到当前资最大在用数量!',
icon: 'none',
})
item.preNum = maxNum;
}
},500)
}
const changeNum = (row) => {
console.log('🚀 ~ changeNum ~ row:', row)
// 将badNum 和 goodNum 向上取整
// 只能输入正整数 过滤掉除正整数外的东西
if (row.badNum) {
row.badNum = Math.ceil(String(row.badNum).replace(/[^\d]/g, ''))
console.log('🚀 ~ changeNum ~ row.badNum:', row.badNum)
}
if (row.goodNum) {
console.log('🚀 ~ changeNum ~ row.goodNum:', row.goodNum)
row.goodNum = Math.ceil(String(row.goodNum).replace(/[^\d]/g, ''))
}
// 不能大于preNum
if (Number(row.badNum) + Number(row.goodNum) > row.preNum) {
console.log('🚀 ~ changeNum ~ row.badNum + row.goodNum > row.preNum:', row.badNum , row.goodNum)
// this.$message.error('完好数量和不合格数量之和不能大于退料数量')
uni.showToast({
title: '完好数量和不合格数量之和不能大于退料数量',
icon: 'none',
})
row.badNum = 0
row.goodNum = 0
}
}
onLoad((options)=>{
console.log(options)
taskInfo.value = JSON.parse(options.taskInfo)
console.log(taskInfo.value)
getMaType()
})
</script>
<style lang="scss" scoped>
.submit-btn {
background-color: #007aff; /* 蓝色背景 */
color: white; /* 白色文字 */
border: none; /* 去掉边框 */
border-radius: 5px; /* 圆角 */
padding: 0 14px; /* 左右内边距 */
height: 30px; /* 高度 */
line-height: 30px; /* 垂直居中 */
font-size: 15px; /* 字体放大 */
margin-right: 2px; /* 与边缘间距 */
}
::v-deep .tree-item .head {
justify-content: center !important;
}
.accept {
padding: 24rpx;
height: 95vh;
word-break: break-all;
background-color: #f7f8fa;
display: flex;
flex-direction: column;
// 卡片样式
.card {
padding: 32rpx;
background-color: #fff;
border-radius: 20rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
margin-bottom: 24rpx;
// 卡片头部
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.title {
font-size: 32rpx;
font-weight: 600;
color: #262626;
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;
}
}
.header-right {
.tip {
font-size: 26rpx;
color: #8c8c8c;
}
}
.icon {
transition: transform 0.5s ease; /* 添加动画过渡 */
transform: rotate(0deg);
&.is-expanded {
transform: rotate(180deg);
}
}
}
// 操作按钮样式
.uni-group {
display: flex;
justify-content: center;
align-items: center;
.uni-icons {
padding: 8rpx;
border-radius: 8rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
opacity: 0.8;
}
}
}
// 选择区域
.select-area {
padding: 24rpx 0;
border-bottom: 2rpx solid #f5f5f5;
}
// // 表格区域
// .table-area {
// margin-top: 24rpx;
// margin: 24rpx -32rpx 0;
// overflow-x: auto;
// :deep(.uni-table) {
// width: 1080rpx;
// padding: 0 10rpx;
// .uni-table-tr {
// .uni-table-th {
// min-width: 140rpx;
// &:last-child {
// min-width: 120rpx;
// }
// }
// .uni-table-td {
// .action-btn {
// display: inline-flex;
// padding: 12rpx;
// border-radius: 8rpx;
// transition: all 0.3s ease;
// margin: 0 8rpx;
// &:active {
// background-color: rgba(0, 0, 0, 0.05);
// }
// &.delete:active {
// background-color: rgba(255, 77, 79, 0.05);
// }
// }
// }
// }
// }
// }
}
// 底部按钮
.footer-btn {
margin-top: auto;
padding: 32rpx;
background: #fff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
.btn-cont {
width: 100%;
height: 88rpx;
line-height: 88rpx;
text-align: center;
background: linear-gradient(135deg, #4b8eff 0%, #3784fb 100%);
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 600;
box-shadow: 0 6rpx 20rpx rgba(55, 132, 251, 0.2);
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
opacity: 0.9;
box-shadow: 0 2rpx 8rpx rgba(55, 132, 251, 0.2);
}
}
}
}
.top-content {
height: 30px;
overflow: hidden;
transition: max-height 0.5s ease-out;
&.is-expanded {
height: auto;
}
}
</style>