bonus-ui/src/views/equipmentRepair/repairApply/addRepair.vue

589 lines
21 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>
<div class="app-container">
<!-- 列表 -->
<el-card class="content-box">
<el-row class="mb8" :gutter="10" justify="end">
<el-col :span="4">
<span style="font-size: 20px; font-weight: 800">维修申请设备</span>
</el-col>
<el-col v-if="!routerParams.isView" :span="20" style="display: flex; justify-content: flex-end">
<el-button v-if="!routerParams.isSubmit" type="primary" @click="handleNumDialog">添加</el-button>
<el-button type="primary" @click="submit">确认申请</el-button>
</el-col>
</el-row>
<el-table
v-loading="isLoading"
:data="tableList"
highlight-current-row
border
stripe
height="546"
style="width: 100%"
>
<!-- 序号列 -->
<el-table-column
type="index"
width="55"
label="序号"
align="center"
:index="(index) => (queryParams.pageNum - 1) * queryParams.pageSize + index + 1"
/>
<!-- 动态列 -->
<el-table-column
v-for="(column, index) in tableColumns"
:key="index"
:label="column.label"
:prop="column.prop"
:width="column.width"
:min-width="column.minWidth"
align="center"
show-overflow-tooltip
>
<template slot-scope="scope">
<span v-if="!column.render">{{ scope.row[column.prop] }}</span>
<component
v-else
:is="{ render: (h) => column.render(h, { row: scope.row }) }"
/>
</template>
</el-table-column>
<!-- 退役数量列 -->
<el-table-column label="退役数量" align="center" width="115">
<template slot-scope="scope">
<el-input-number
v-model="scope.row.retirementNum"
:min="0"
:max="getRetirementMax(scope.row)"
:precision="0"
:disabled="isRetirementNumDisabled(scope.row)"
controls-position="right"
size="mini"
style="width: 90px"
@change="handleRetirementNumChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column label="附件" align="center" width="90" :show-overflow-tooltip="true">
<template slot-scope="scope">
<div style="display: flex; align-items: center; justify-content: space-between">
<el-upload
ref="upload"
:limit="3"
:headers="upload.headers"
:action="upload.url"
:show-file-list="false"
accept=".png, .jpg, .jpeg, .pdf, .doc, .docx"
:on-success="handleFileSuccess2"
:auto-upload="true"
>
<el-button :disabled="scope.row.status && scope.row.status === '通过'" size="mini" type="text" @click="beforeFileUpload(scope.row)">上传</el-button>
</el-upload>
<el-button
size="mini"
type="text"
@click="picturePreview(scope.row)"
v-if="scope.row.bmFileInfos && scope.row.bmFileInfos.length > 0"
>
查看
</el-button>
</div>
</template>
</el-table-column>
<!-- 详情里不显示状态了 -->
<el-table-column v-if="false" label="状态" align="center" prop="status" width="90">
<template #default="scope">
<el-tag
:type="scope.row.status === '通过' ? 'success' : scope.row.status === '驳回' ? 'danger' : 'warning'"
size="small"
>
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" align="center" width="80px" v-if="!routerParams.isView">
<template slot-scope="scope">
<el-button v-if="!routerParams.isSubmit" :disabled="scope.row.status && scope.row.status === '通过'"
size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" style="color: red">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</el-card>
<!-- 插入选择工具的弹窗 -->
<SelectToolDialog ref="addNum" @confirm="handleAddFromDialog" />
<!-- 图片查看弹窗 -->
<el-dialog :visible.sync="dialogVisible" width="500px" height="500px">
<img width="100%" height="500px" :src="dialogImageUrl" />
</el-dialog>
</div>
</template>
<script>
import { getListByApplyIdApi, deleteToolApi, updateToolApplyApi } from '@/api/toolsManage'
import SelectToolDialog from './SelectToolDialog.vue'
import { getToken } from "@/utils/auth";
import {addRepairData, getRepairDetailsList, updateRepairData} from "@/api/equipmentRepair";
import { getApprovalInstanceByBusiness } from '@/api/approval';
export default {
name: 'AddEditTools',
components: { SelectToolDialog },
data() {
return {
routerParams: {},
isLoading: false,
showSearch: true,
queryParams: {
pageNum: 1,
pageSize: 10,
parentTypeName: null,
typeName: null,
manageMode: null,
applyId: null,
},
typeList: [
{ label: '数量管理', value: '0' },
{ label: '编码管理', value: '1' },
],
upload: {
headers: { Authorization: 'Bearer ' + getToken() },
url: process.env.VUE_APP_BASE_API + '/file/upload'
},
currentRowId: null,
dialogImageUrl: '',
dialogVisible: false,
tempMaCodeList: [],
total: 0,
tableList: [],
tableColumns: [
{ label: '分类', prop: 'type', width: 75 },
{ label: '类目', prop: 'groupName' },
{ label: '名称', prop: 'typeName' },
{ label: '规格型号', prop: 'typeModelName' },
{ label: '管理模式', prop: 'manageMode', width: 90 },
{ label: '设备编码', prop: 'code' },
{ label: '维修数量', prop: 'repairNum', width: 80 },
{
label: '维修是否合格',
prop: 'isScrap',
width: '120',
render: (h, { row }) =>
h('el-select', {
props: {
value: row.isScrap || '',
disabled: row.status === '通过' // ⭐ 状态为"通过" → 不可操作
},
on: {
input: val => {
this.$set(row, 'isScrap', val)
// 当维修是否合格字段变化时,处理退役数量逻辑
if (val === '0') {
// 合格退役数量设为0且不可操作
this.$set(row, 'reasonVal', '')
this.$set(row, 'reasonDisabled', true)
this.$set(row, 'retirementNum', 0)
} else {
// 不合格:根据管理模式设置退役数量
this.$set(row, 'reasonDisabled', false)
if (row.manageMode === '编码管理') {
// 编码管理退役数量默认为1不可操作
this.$set(row, 'retirementNum', 1)
} else {
// 数量管理退役数量保持原值或设为0可操作
if (!row.retirementNum) {
this.$set(row, 'retirementNum', 0)
}
}
}
}
},
style: 'width: 90px'
}, [
h('el-option', { props: { label: '是', value: '0' } }),
h('el-option', { props: { label: '否', value: '1' } })
])
},
{
label: '维修日期',
prop: 'repairTime',
width: '180',
render: (h, { row }) => h('el-date-picker', {
props: {
value: row.repairTime,
type: 'date',
placeholder: '选择日期',
format: 'yyyy-MM-dd',
'value-format': 'yyyy-MM-dd',
disabled: row.status === '通过' // ⭐ 禁用
},
on: {
input: val => this.$set(row, 'repairTime', val)
},
style: 'width: 140px'
})
},
{
label: '退役原因',
prop: 'reasonVal',
width: '120',
render: (h, { row }) =>
h('el-select', {
props: {
value: row.reasonVal || '',
disabled: row.reasonDisabled === true || row.status === '通过' // ⭐ 禁用条件合并
},
on: {
input: val => this.$set(row, 'reasonVal', val)
},
style: 'width: 90px'
}, [
h('el-option', { props: { label: '人为', value: '人为' } }),
h('el-option', { props: { label: '自然', value: '自然' } })
])
},
]
}
},
created() {
this.routerParams = this.$route.query
let title = '新增维修'
if (this.routerParams.isView) title = '维修查看'
else if (this.routerParams.isEdit) title = '维修编辑'
else if (this.routerParams.isSubmit) title = '维修提交'
this.queryParams.id = this.routerParams.applyId || ''
const obj = Object.assign({}, this.$route, { title })
this.$tab.updatePage(obj)
this.getList()
},
methods: {
// 判断退役数量字段是否禁用
// 判断退役数量字段是否禁用
isRetirementNumDisabled(row) {
// 状态为"通过"时禁用
if (row.status === '通过') return true
// 未选择维修是否合格时禁用
if (!row.isScrap && row.isScrap !== '0') return true
// 维修合格时禁用
if (row.isScrap === '0') return true
// 编码管理且维修不合格时禁用
if (row.manageMode === '编码管理' && row.isScrap === '1') return true
// 其他情况可操作(数量管理且维修不合格)
return false
},
// 获取退役数量的最大值
getRetirementMax(row) {
// 编码管理设备最大值为1
if (row.manageMode === '编码管理') return 1
// 数量管理设备最大值为维修数量
return row.repairNum || 0
},
// 退役数量变化处理
handleRetirementNumChange(row) {
// 确保退役数量在有效范围内
if (row.retirementNum < 0) {
this.$set(row, 'retirementNum', 0)
} else if (row.retirementNum > this.getRetirementMax(row)) {
this.$set(row, 'retirementNum', this.getRetirementMax(row))
}
},
// 文件上传成功处理-编码弹窗上传
// 文件上传成功处理
handleFileSuccess2(response, file, fileList) {
console.log('文件上传成功', response)
if (response.code === 200) {
let obj = {
fileName: response.data.name,
fileUrl: response.data.url
}
// 通过行ID找到对应的行
if (this.currentRowId !== null) {
// 在表格数据中查找对应的行
const targetRow = this.tableList.find(item => item.keyId === this.currentRowId)
if (targetRow) {
// 如果当前行还没有 bmFileInfos 数组,先创建
if (!targetRow.bmFileInfos) {
this.$set(targetRow, 'bmFileInfos', [])
}
//先清空bmFileInfos
targetRow.bmFileInfos=[]
// 将文件对象添加到 bmFileInfos 中
targetRow.bmFileInfos.push(obj)
this.$message.success('文件上传成功')
// 强制更新视图
this.$forceUpdate()
} else {
this.$message.warning('未找到对应的行数据')
}
// 重置 currentRowId
this.currentRowId = null
} else {
this.$message.warning('未找到对应的行数据')
}
} else {
this.$message.error('文件上传失败:' + response.msg)
}
},
// 上传前记录行ID
beforeFileUpload(row) {
this.currentRowId = row.keyId
console.log('记录当前行ID:', row.keyId)
},
// 图片预览方法
picturePreview(row) {
console.log('预览行数据:', row)
console.log('预览附件:', row.bmFileInfos)
if (row.bmFileInfos && row.bmFileInfos.length > 0) {
// 显示最新的文件(数组最后一个)
const file = row.bmFileInfos[row.bmFileInfos.length - 1]
console.log('预览文件:', file)
this.dialogImageUrl = file.fileUrl.replaceAll('#', '%23')
// 从URL中提取文件名
const urlParts = file.fileUrl.split('/');
const fileNameWithParams = urlParts[urlParts.length - 1];
// 去除URL参数获取纯文件名
const fileName = fileNameWithParams.split('?')[0];
const parts = fileName.split('.')
const extension = parts.pop().toLowerCase()
if (['doc', 'docx', 'pdf'].includes(extension)) {
const windowName = file.fileName
window.open(file.fileUrl, windowName)
} else {
this.dialogVisible = true
}
} else {
this.$message.warning('该行没有附件')
}
},
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
handleReset() { this.queryParams.pageNum = 1; this.queryParams.pageSize = 10; this.$refs.queryForm.resetFields(); this.getList() },
async getList() {
this.isLoading = true
try {
const res = await getRepairDetailsList({ ...this.queryParams })
this.tableList = (res.data || []).map(item => {
// 初始化退役数量为0
if (item.retirementNum === undefined || item.retirementNum === null) {
item.retirementNum = 0
}
if (item.isScrap=='0'){
item.reasonDisabled=true
}
return item
})
/** ⭐ 获取列表后同步给弹窗的 selectedMap **/
if (this.$refs.addNum && this.$refs.addNum.selectedMap) {
const dialog = this.$refs.addNum
// 清空旧的
dialog.selectedMap.clear()
// 将 tableList 里的所有 keyId 放入 selectedMap
this.tableList.forEach(row => {
dialog.selectedMap.set(row.keyId, row)
})
// 如果弹窗内还有 tableList需要保持一致
dialog.tableList = [...this.tableList]
// 让弹窗恢复勾选状态(如果有 restoreSelection 方法)
dialog.$nextTick(() => {
if (dialog.restoreSelection) dialog.restoreSelection()
})
}
this.total = this.tableList.length
} finally {
this.isLoading = false
}
},
// 在主页面中修改 handleDelete 方法
handleDelete(row) {
this.$confirm('确定删除该条数据?', '提示', { type: 'warning' }).then(() => {
this.tableList = this.tableList.filter(item => item.keyId !== row.keyId)
// 强制同步到弹窗的 selectedMap
if (this.$refs.addNum) {
const dialog = this.$refs.addNum
dialog.selectedMap.delete(row.keyId)
// 如果弹窗可见,也需要更新弹窗的表格显示
if (dialog.visible) {
dialog.tableList = dialog.tableList.filter(item => item.keyId !== row.keyId)
dialog.$nextTick(() => dialog.restoreSelection())
}
}
this.total = this.tableList.length
this.$message.success('删除成功')
})
},
// 在主页面中修改
handleNumDialog() {
if (this.$refs.addNum) {
// 将当前表格数据传递给弹窗,确保选中状态一致
this.$refs.addNum.openDialog(this.queryParams.applyId, this.tableList)
}
},
handleAddFromDialog(rows) {
if (!rows || rows.length === 0) return
const existingKeys = new Set(this.tableList.map(item => item.keyId))
const newRows = rows.filter(item => !existingKeys.has(item.keyId))
newRows.forEach(item => {
if (item.manageMode === '编码管理') item.repairNum = 1
else if (!item.repairNum) item.repairNum = 1
// 初始化退役数量为0
if (item.retirementNum === undefined || item.retirementNum === null) {
item.retirementNum = 0
}
if (this.$refs.addNum && this.$refs.addNum.selectedMap) this.$refs.addNum.selectedMap.set(item.keyId, item)
})
this.tableList = [...this.tableList, ...newRows]
this.total = this.tableList.length
},
submit() {
if (this.tableList.length <= 0) {
return this.$message.warning(`请先添加数据`)
}
this.$confirm('是否确定提交申请?', '提示', { type: 'warning' }).then(async () => {
// 校验:所有行必须填写合格字段、维修日期等(可选)
for (const row of this.tableList) {
if (!row.repairNum || row.repairNum === 0) {
return this.$message.warning(`${row.typeName}】维修数量不能为空或为0`)
}
if (!row.isScrap) {
return this.$message.warning(`${row.typeName}】请选择维修是否合格`)
}
if (!row.repairTime) {
return this.$message.warning(`${row.typeName}】请选择维修日期`)
}
// 只有维修不合格时才必须填写退役原因
if (row.isScrap === '1' && !row.reasonVal) {
return this.$message.warning(`${row.typeName}】请选择退役原因`)
}
// 校验退役数量
if (row.isScrap === '1') {
// 维修不合格时校验退役数量
if (row.retirementNum === undefined || row.retirementNum === null) {
return this.$message.warning(`${row.typeName}】退役数量不能为空`)
}
if (row.retirementNum <= 0) {
return this.$message.warning(`${row.typeName}】退役数量必须大于0`)
}
if (row.manageMode === '数量管理' && row.retirementNum > row.repairNum) {
return this.$message.warning(`${row.typeName}】退役数量不能大于维修数量`)
}
}
}
// 处理 devType 字段:装备=1工具=2
const toBeRepairListWithDevType = this.tableList.map(item => {
const newItem = { ...item }
if (item.type === '装备') {
newItem.devType = '1'
} else if (item.type === '工具') {
newItem.devType = '2'
}
return newItem
})
const payload = {
changeId:this.routerParams.applyId,
toBeRepairList: toBeRepairListWithDevType
}
console.log("提交内容", payload)
this.isLoading = true
try {
let res;
if (this.routerParams.isEdit){
res = await updateRepairData(payload)
} else {
res = await addRepairData(payload)
}
if (res.code == 200){
// 提交成功后获取维修申请ID
// res.data 可能是直接的ID数字或包含id属性的对象
let repairId = null
if (typeof res.data === 'number') {
repairId = res.data
} else if (res.data && typeof res.data === 'object' && res.data.id) {
repairId = res.data.id
} else {
repairId = this.routerParams.applyId
}
console.log('维修申请ID:', repairId, '返回数据:', res.data)
// 查询审批实例,如果不存在则会自动创建
try {
const approvalRes = await getApprovalInstanceByBusiness('EQUIPMENT_REPAIR', repairId)
console.log('审批实例查询结果:', approvalRes)
if (approvalRes && approvalRes.data) {
this.$message.success('申请已提交,请等待审批')
this.$tab.closeOpenPage({ path: './repairList' })
}
} catch (error) {
console.error('查询审批实例失败:', error)
this.$message.success('申请已提交,请等待审批')
this.$tab.closeOpenPage({ path: './repairList' })
}
} else {
this.$message.error(res.msg || '申请失败')
}
} catch (error) {
this.$message.error('申请失败')
}finally {
this.isLoading = false
}
})
},
}
}
</script>
<style lang="scss" scoped>
.content-box {
border-radius: 8px;
height: calc(100vh - 130px);
display: flex;
flex-direction: column;
overflow: hidden;
::v-deep .el-card__body {
display: flex !important;
flex-direction: column !important;
height: 100% !important;
padding: 20px;
}
}
</style>