<template>
|
<div class="page">
|
<div class="page-title">
|
<div>
|
<h2>故障上报</h2>
|
<p>T员录入装备信息和修理要求,故障编号保存后按规则自动生成</p>
|
</div>
|
<el-button icon="el-icon-plus" type="primary" @click="openCreate">新增故障</el-button>
|
</div>
|
|
<div class="toolbar query-toolbar">
|
<span class="filter-label">X号</span>
|
<el-select v-model="query.xNo" placeholder="全部" clearable style="width:100px">
|
<el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option>
|
</el-select>
|
<span class="filter-label">T队专业</span>
|
<el-select v-model="query.teamMajor" placeholder="全部" clearable style="width:130px">
|
<el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
|
</el-select>
|
<span class="filter-label">工程专业</span>
|
<el-select v-model="query.engineeringMajor" placeholder="全部" clearable style="width:130px">
|
<el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option>
|
</el-select>
|
<span class="filter-label">上报单位</span>
|
<el-input v-model="query.reportUnit" placeholder="单位名称" clearable style="width:150px" @keyup.enter.native="search"></el-input>
|
<span class="filter-label">负责人</span>
|
<el-input v-model="query.owner" placeholder="负责人" clearable style="width:110px" @keyup.enter.native="search"></el-input>
|
<span class="filter-label">上报日期</span>
|
<el-date-picker
|
v-model="query.reportDateRange"
|
type="daterange"
|
value-format="yyyy-MM-dd"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
style="width:300px"
|
></el-date-picker>
|
<div class="filter-break"></div>
|
<span class="filter-label">任务状态</span>
|
<el-select v-model="query.status" placeholder="全部" clearable style="width:160px">
|
<el-option v-for="(meta, key) in taskStatuses" :key="key" :label="meta.text" :value="key"></el-option>
|
</el-select>
|
<el-input v-model="query.keyword" placeholder="故障编号/装备名称/工程名称及损坏情况" clearable style="width:320px" @keyup.enter.native="search"></el-input>
|
<el-button type="primary" icon="el-icon-search" @click="search">查询</el-button>
|
<el-button icon="el-icon-refresh-left" @click="resetQuery">重置</el-button>
|
<el-button icon="el-icon-refresh" @click="load">刷新</el-button>
|
</div>
|
|
<div class="toolbar action-toolbar">
|
<span class="selection-tip">已选择 {{ selection.length }} 条</span>
|
<div class="spacer"></div>
|
<el-button icon="el-icon-upload2" :disabled="selection.length === 0" @click="reportSelected">上报</el-button>
|
<el-button icon="el-icon-edit" :disabled="selection.length !== 1" @click="editSelected">修改</el-button>
|
<el-button icon="el-icon-delete" type="danger" plain :disabled="selection.length === 0" @click="deleteSelected">删除</el-button>
|
<el-button icon="el-icon-download" @click="exportCsv">导出</el-button>
|
</div>
|
|
<div class="table-panel">
|
<el-table :data="rows" border stripe height="calc(100vh - 395px)" :header-cell-style="{ textAlign: 'center' }" @selection-change="selection = $event">
|
<el-table-column type="selection" width="46" :selectable="row => canEdit(row)"></el-table-column>
|
<el-table-column prop="taskStatus" label="任务状态" width="145" fixed>
|
<template slot-scope="{ row }"><el-tag size="mini" :type="taskMeta(row.taskStatus).type">{{ taskMeta(row.taskStatus).text }}</el-tag></template>
|
</el-table-column>
|
<el-table-column prop="faultCode" label="故障编号" width="180" fixed></el-table-column>
|
<el-table-column prop="xNo" label="X号" width="64"></el-table-column>
|
<el-table-column prop="equipmentName" label="装备名称" min-width="210" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="parentSystem" label="所属上级系统" min-width="120"></el-table-column>
|
<el-table-column prop="engineeringNameDesc" label="工程名称及损坏情况" min-width="210" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="repairRequirement" label="修理技术要求" min-width="200" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="engineeringMajor" label="工程专业" width="100"></el-table-column>
|
<el-table-column prop="teamMajor" label="T队专业" width="100"></el-table-column>
|
<el-table-column prop="owner" label="负责人" width="90"></el-table-column>
|
<el-table-column prop="reportUnit" label="上报单位" min-width="130"></el-table-column>
|
<el-table-column prop="reportDate" label="上报日期" width="110" align="center"></el-table-column>
|
<el-table-column prop="faultImpact" label="故障影响" width="100"></el-table-column>
|
<el-table-column prop="faultNature" label="故障性质" width="100" align="center"></el-table-column>
|
<el-table-column prop="majorFaultDesc" label="重大故障描述" min-width="200" show-overflow-tooltip></el-table-column>
|
<el-table-column label="是否自修" width="90" align="center">
|
<template slot-scope="{ row }">{{ yesNo(row.isSelfRepair) }}</template>
|
</el-table-column>
|
<el-table-column label="是否涉H" width="90" align="center">
|
<template slot-scope="{ row }">{{ yesNo(row.isHRelated) }}</template>
|
</el-table-column>
|
<el-table-column label="监测项目" width="90" align="center">
|
<template slot-scope="{ row }">{{ yesNo(row.isMonitorItem) }}</template>
|
</el-table-column>
|
<el-table-column prop="monitorValue" label="监测数值" width="110" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="requestedFinishDate" label="要求完工日期" width="120" align="center"></el-table-column>
|
<el-table-column prop="remark" label="备注" min-width="130" show-overflow-tooltip></el-table-column>
|
</el-table>
|
</div>
|
|
<div class="pagination-bar">
|
<el-pagination
|
background
|
:current-page="pagination.current"
|
:page-size="pagination.size"
|
:page-sizes="[20, 50, 100]"
|
:total="pagination.total"
|
layout="total, sizes, prev, pager, next, jumper"
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange"
|
></el-pagination>
|
</div>
|
|
<el-dialog :title="form.id ? '修改故障' : '新增故障'" :visible.sync="dialogVisible" width="960px" append-to-body @closed="clearValidate">
|
<el-form ref="form" :model="form" :rules="rules" label-width="105px">
|
<el-row :gutter="16">
|
<el-col :span="8"><el-form-item label="故障编号"><el-input v-model="form.faultCode" readonly placeholder="保存后自动生成"></el-input></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="上报单位"><el-input v-model="form.reportUnit"></el-input></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="上报日期"><el-date-picker v-model="form.reportDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="X号"><el-select v-model="form.xNo" filterable placeholder="请选择X号" style="width:100%"><el-option v-for="item in xNumbers" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="负责人"><el-input v-model="form.owner"></el-input></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="T队专业"><el-select v-model="form.teamMajor" style="width:100%"><el-option v-for="item in teamMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="工程专业"><el-select v-model="form.engineeringMajor" style="width:100%"><el-option v-for="item in engineeringMajors" :key="item.id" :label="item.name" :value="item.name"></el-option></el-select></el-form-item></el-col>
|
<el-col :span="16">
|
<el-form-item label="装备名称" prop="equipmentName">
|
<el-input v-model="form.equipmentName" readonly :title="form.equipmentName" placeholder="点击右侧按钮从产品结构树选设备,可多选">
|
<el-button slot="append" icon="el-icon-s-grid" @click="pickerVisible = true">选择装备</el-button>
|
</el-input>
|
<div v-if="selectedEquipmentIds.length" class="field-tip">已选 {{ selectedEquipmentIds.length }} 台设备</div>
|
</el-form-item>
|
</el-col>
|
<el-col :span="16"><el-form-item label="所属上级系统"><el-input v-model="form.parentSystem" readonly :title="form.parentSystem"></el-input></el-form-item></el-col>
|
<el-col :span="24"><el-form-item label="工程名称及损坏情况" prop="engineeringNameDesc"><el-input v-model="form.engineeringNameDesc" type="textarea" :rows="2"></el-input></el-form-item></el-col>
|
<el-col :span="24"><el-form-item label="修理技术要求"><el-input v-model="form.repairRequirement" type="textarea" :rows="2"></el-input></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="故障影响"><el-select v-model="form.faultImpact" style="width:100%"><el-option v-for="item in impacts" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="故障性质"><el-select v-model="form.faultNature" style="width:100%"><el-option v-for="item in natures" :key="item" :label="item" :value="item"></el-option></el-select></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="是否自修"><el-switch v-model="form.isSelfRepair" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col>
|
</el-row>
|
<el-row :gutter="16">
|
<el-col :span="8"><el-form-item label="是否涉H"><el-switch v-model="form.isHRelated" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="是否监测项目"><el-switch v-model="form.isMonitorItem" :active-value="1" :inactive-value="0"></el-switch></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="监测数值"><el-input v-model="form.monitorValue" :disabled="form.isMonitorItem !== 1"></el-input></el-form-item></el-col>
|
<el-col :span="24"><el-form-item label="重大故障描述"><el-input v-model="form.majorFaultDesc" type="textarea" :rows="2"></el-input></el-form-item></el-col>
|
<el-col :span="12"><el-form-item label="要求完工日期"><el-date-picker v-model="form.requestedFinishDate" type="date" value-format="yyyy-MM-dd" style="width:100%"></el-date-picker></el-form-item></el-col>
|
<el-col :span="12"><el-form-item label="备注"><el-input v-model="form.remark"></el-input></el-form-item></el-col>
|
</el-row>
|
</el-form>
|
<template slot="footer">
|
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
</template>
|
</el-dialog>
|
|
<product-tree-picker :visible.sync="pickerVisible" :x-no="form.xNo" :selected-ids="selectedEquipmentIds" @select="onEquipmentSelected"></product-tree-picker>
|
</div>
|
</template>
|
<script>
|
import api from '@/api'
|
import ProductTreePicker from '@/components/ProductTreePicker.vue'
|
import { taskStatuses, statusMeta } from '@/utils/dict'
|
|
export default {
|
name: 'FaultReport',
|
components: { ProductTreePicker },
|
data() {
|
return {
|
taskStatuses,
|
query: { keyword: '', status: '', xNo: '', teamMajor: '', engineeringMajor: '', reportUnit: '', owner: '', reportDateRange: [] },
|
rows: [],
|
selection: [],
|
pagination: { current: 1, size: 20, total: 0 },
|
dialogVisible: false,
|
pickerVisible: false,
|
saving: false,
|
teamMajors: [],
|
engineeringMajors: [],
|
xNumbers: [],
|
impacts: ['供气', '供电', '运行', '安全'],
|
natures: ['一般', '重大'],
|
form: {},
|
rules: {
|
equipmentName: [{ required: true, message: '请选择装备名称', trigger: 'change' }],
|
engineeringNameDesc: [{ required: true, message: '请填写工程名称及损坏情况', trigger: 'blur' }]
|
}
|
}
|
},
|
computed: {
|
selectedEquipmentIds() {
|
const text = this.form && this.form.equipmentIds
|
if (text) return String(text).split(',').map(item => item.trim()).filter(Boolean)
|
return this.form && this.form.equipmentId ? [String(this.form.equipmentId)] : []
|
}
|
},
|
created() {
|
this.load()
|
this.loadXNumbers()
|
this.loadMajors()
|
this.loadAccountInfo()
|
},
|
methods: {
|
taskMeta(value) { return statusMeta(taskStatuses, value) },
|
yesNo(value) { return Number(value) === 1 ? '\u662f' : '\u5426' },
|
loadAccountInfo() {
|
if (sessionStorage.getItem('deptName') !== null) return
|
api.get('/auth/me').then(res => {
|
const info = res.data || {}
|
sessionStorage.setItem('realName', info.realName || sessionStorage.getItem('realName') || '')
|
sessionStorage.setItem('deptName', info.deptName || '')
|
if (this.form && !this.form.id && !this.form.reportUnit) this.form.reportUnit = info.deptName || ''
|
})
|
},
|
loadMajors() {
|
api.get('/system/team-majors').then(res => {
|
this.teamMajors = res.data || []
|
if (!this.form.teamMajor && this.teamMajors.length) this.form.teamMajor = this.teamMajors[0].name
|
})
|
api.get('/system/engineering-majors').then(res => {
|
this.engineeringMajors = res.data || []
|
if (!this.form.engineeringMajor && this.engineeringMajors.length) this.form.engineeringMajor = this.engineeringMajors[0].name
|
})
|
},
|
loadXNumbers() {
|
api.get('/system/tree', { params: { type: 'product' } }).then(res => {
|
const values = []
|
const walk = nodes => {
|
if (!nodes) return
|
nodes.forEach(node => {
|
if (node.nodeType === 'X_NO' || node.nodeLevel === 2) values.push(node.name)
|
walk(node.children)
|
})
|
}
|
walk(res.data)
|
this.xNumbers = Array.from(new Set(values.filter(Boolean))).sort((a, b) => a.localeCompare(b, 'zh-CN'))
|
})
|
},
|
queryParams() {
|
const range = this.query.reportDateRange || []
|
return {
|
keyword: this.query.keyword,
|
status: this.query.status,
|
xNo: this.query.xNo,
|
teamMajor: this.query.teamMajor,
|
engineeringMajor: this.query.engineeringMajor,
|
reportUnit: this.query.reportUnit,
|
owner: this.query.owner,
|
reportDateStart: range[0] || '',
|
reportDateEnd: range[1] || ''
|
}
|
},
|
load() {
|
const params = {
|
...this.queryParams(),
|
current: this.pagination.current,
|
size: this.pagination.size
|
}
|
api.get('/faults/page', { params }).then(res => {
|
const page = res.data || {}
|
this.rows = page.records || []
|
this.pagination.total = Number(page.total || 0)
|
if (!this.rows.length && this.pagination.current > 1 && this.pagination.total > 0) {
|
this.pagination.current -= 1
|
this.load()
|
}
|
})
|
},
|
search() {
|
this.pagination.current = 1
|
this.load()
|
},
|
resetQuery() {
|
this.query = { keyword: '', status: '', xNo: '', teamMajor: '', engineeringMajor: '', reportUnit: '', owner: '', reportDateRange: [] }
|
this.search()
|
},
|
handleCurrentChange(current) {
|
this.pagination.current = current
|
this.load()
|
},
|
handleSizeChange(size) {
|
this.pagination.size = size
|
this.pagination.current = 1
|
this.load()
|
},
|
canEdit(row) {
|
return ['UNREPORTED', 'RETURNED'].includes(row.taskStatus)
|
},
|
blankForm() {
|
const today = new Date().toISOString().slice(0, 10)
|
return {
|
faultCode: '',
|
reportUnit: sessionStorage.getItem('deptName') || '',
|
reportDate: today,
|
xNo: '',
|
owner: sessionStorage.getItem('realName') || 'T员',
|
teamMajor: (this.teamMajors[0] || {}).name || '',
|
engineeringMajor: (this.engineeringMajors[0] || {}).name || '',
|
equipmentIds: '',
|
equipmentName: '',
|
parentSystem: '',
|
faultImpact: '供气',
|
faultNature: '一般',
|
isSelfRepair: 0,
|
isHRelated: 0,
|
isMonitorItem: 0,
|
monitorValue: '',
|
engineeringNameDesc: '',
|
repairRequirement: '',
|
majorFaultDesc: '',
|
requestedFinishDate: '',
|
remark: ''
|
}
|
},
|
openCreate() {
|
this.form = this.blankForm()
|
this.dialogVisible = true
|
},
|
editSelected() {
|
if (this.selection.length !== 1) return
|
this.form = JSON.parse(JSON.stringify(this.selection[0]))
|
if (!this.form.equipmentIds && this.form.equipmentId) {
|
this.form.equipmentIds = String(this.form.equipmentId)
|
}
|
this.dialogVisible = true
|
},
|
onEquipmentSelected(nodes) {
|
const selected = Array.isArray(nodes) ? nodes : [nodes]
|
this.form.equipmentIds = selected.map(node => node.id).join(',')
|
this.form.equipmentId = selected.length ? selected[0].id : null
|
this.form.equipmentName = selected.map(node => node.name).join('、')
|
this.form.xNo = selected.length && selected[0].xNo ? selected[0].xNo : this.form.xNo
|
const parentSystems = []
|
selected.forEach(node => {
|
const parent = (node.parentPathNames || []).join(' / ')
|
if (parent && !parentSystems.includes(parent)) parentSystems.push(parent)
|
})
|
this.form.parentSystem = parentSystems.length ? parentSystems.join('、') : '未指定'
|
},
|
save() {
|
this.$refs.form.validate(valid => {
|
if (!valid) return
|
this.saving = true
|
const request = this.form.id ? api.put('/faults/' + this.form.id, this.form) : api.post('/faults', this.form)
|
request.then(() => {
|
this.$message.success('保存成功')
|
this.dialogVisible = false
|
this.load()
|
}).finally(() => { this.saving = false })
|
})
|
},
|
reportSelected() {
|
const ids = this.selection.map(item => item.id)
|
this.$confirm(`确定上报选中的 ${ids.length} 条故障吗?`, '上报确认').then(() => {
|
return api.post('/faults/report', { ids })
|
}).then(() => {
|
this.$message.success('上报成功')
|
this.load()
|
}).catch(() => {})
|
},
|
deleteSelected() {
|
const ids = this.selection.map(item => item.id)
|
this.$confirm(`确定删除选中的 ${ids.length} 条故障吗?`, '删除确认', { type: 'warning' }).then(async () => {
|
for (const id of ids) await api.delete('/faults/' + id)
|
this.$message.success('删除成功')
|
this.load()
|
}).catch(() => {})
|
},
|
clearValidate() {
|
this.$refs.form && this.$refs.form.clearValidate()
|
},
|
exportCsv() {
|
api.get('/faults', { params: this.queryParams() }).then(res => {
|
const rows = res.data || []
|
const headers = ['故障编号', 'X号', '装备名称', '任务状态', '工程名称及损坏情况', '上报单位', '上报日期']
|
const lines = [headers.join(',')].concat(rows.map(row => [
|
row.faultCode, row.xNo, row.equipmentName, this.taskMeta(row.taskStatus).text,
|
row.engineeringNameDesc, row.reportUnit, row.reportDate
|
].map(value => `"${String(value || '').replace(/"/g, '""')}"`).join(',')))
|
const blob = new Blob(['\ufeff' + lines.join('\n')], { type: 'text/csv;charset=utf-8' })
|
const link = document.createElement('a')
|
link.href = URL.createObjectURL(blob)
|
link.download = '故障上报.csv'
|
link.click()
|
URL.revokeObjectURL(link.href)
|
})
|
}
|
}
|
}
|
</script>
|
<style scoped>
|
.query-toolbar { margin-bottom: 8px; }
|
.filter-break { flex-basis: 100%; height: 0; }
|
.query-toolbar ::v-deep .el-range-separator { flex: 0 0 18px; min-width: 18px; }
|
.action-toolbar { min-height: 50px; }
|
.filter-label { margin-left: 4px; color: #4d6073; font-size: 13px; white-space: nowrap; }
|
.field-tip { margin-top: 4px; color: #7b8a99; font-size: 12px; line-height: 18px; }
|
.selection-tip { color: #6d7f91; font-size: 13px; }
|
.pagination-bar { display: flex; justify-content: flex-end; padding: 12px 0 4px; }
|
@media (max-width: 900px) {
|
.query-toolbar .el-date-editor { width: 100% !important; }
|
}
|
</style>
|