Browse Source

feat(quality): 添加质量问题管理功能

- 实现质量问题列表页面,包含查询、新增、编辑、删除、回复等功能
- 添加质量问题详情组件,显示问题基本信息和附件
- 实现质量问题回复功能,支持原因分析和后续措施
- 集成供应商选择功能,完善问题录入流程
- 添加数据导出功能,支持质量问题数据批量导出
- 完善API接口调用,包括查询、保存、更新、删除和回复接口
master
qiankanghui 1 week ago
parent
commit
a08c12a192
  1. 6
      src/api/quality/qualityIssue.js
  2. 21
      src/views/modules/quality/com_qualityIssueDetail.vue
  3. 482
      src/views/modules/quality/qualityIssueList.vue

6
src/api/quality/qualityIssue.js

@ -6,5 +6,11 @@ export const queryQualityIssuePage = (data) => createAPI(`/srm/qualityIssue/quer
// 保存质量问题 // 保存质量问题
export const saveQualityIssue = (data) => createAPI(`/srm/qualityIssue/save`, 'post', data) export const saveQualityIssue = (data) => createAPI(`/srm/qualityIssue/save`, 'post', data)
//修改质量问题
export const updateQualityIssue = (data) => createAPI(`/srm/qualityIssue/update`, 'post', data)
//删除质量问题 //删除质量问题
export const deleteQualityIssue = (data) => createAPI(`/srm/qualityIssue/delete`, 'post', data) export const deleteQualityIssue = (data) => createAPI(`/srm/qualityIssue/delete`, 'post', data)
// 保存回复
export const saveReply = (data) => createAPI(`/srm/qualityIssue/saveReply`, 'post', data)

21
src/views/modules/quality/com_qualityIssueDetail.vue

@ -75,13 +75,13 @@
<!-- 第五行 - 原因分析 --> <!-- 第五行 - 原因分析 -->
<div class="detail-row full-width"> <div class="detail-row full-width">
<span class="label">原因分析</span> <span class="label">原因分析</span>
<div class="value textarea">{{ detailData.causeAnalysis }}</div>
<div class="value textarea">{{ detailData.analysis }}</div>
</div> </div>
<!-- 第六行 - 后续措施 --> <!-- 第六行 - 后续措施 -->
<div class="detail-row full-width"> <div class="detail-row full-width">
<span class="label">后续措施</span> <span class="label">后续措施</span>
<div class="value textarea">{{ detailData.followUpMeasures }}</div>
<div class="value textarea">{{ detailData.analysis }}</div>
</div> </div>
<!-- 第七行 --> <!-- 第七行 -->
@ -96,24 +96,7 @@
<span class="label">回复时间</span> <span class="label">回复时间</span>
<span class="value">{{ detailData.replyTime }}</span> <span class="value">{{ detailData.replyTime }}</span>
</div> </div>
</div>
<!-- 第八行 -->
<div class="detail-row">
<div class="detail-item">
<span class="label">关闭人</span>
<span class="value">{{ detailData.closedBy }}</span>
</div> </div>
<div class="detail-item">
<span class="label">关闭时间</span>
<span class="value">{{ detailData.closedTime }}</span>
</div>
</div>
</div> </div>
</template> </template>

482
src/views/modules/quality/qualityIssueList.vue

@ -70,9 +70,11 @@
</el-table-column> </el-table-column>
<el-table-column fixed="right" header-align="center" align="center" width="180" label="操作"> <el-table-column fixed="right" header-align="center" align="center" width="180" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<template v-if="scope.row.status !== '已回复'">
<a class="customer-a" @click="editQualityIssue(scope.row)">编辑 |</a> <a class="customer-a" @click="editQualityIssue(scope.row)">编辑 |</a>
<a class="customer-a" @click="deleteQualityIssue(scope.row)">删除 |</a>
<a class="customer-a" @click="replyQualityIssue(scope.row)">回复</a>
<a class="customer-a" @click="deleteQualityIssue(scope.row)">删除 </a>
<a class="customer-a" @click="replyQualityIssue(scope.row)">| 回复</a>
</template>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -205,12 +207,177 @@
</div> </div>
</el-dialog> </el-dialog>
<!-- 编辑弹窗 -->
<el-dialog
title="质量问题 - 编辑"
:visible.sync="editDialogVisible"
width="45%"
top="10vh"
:close-on-click-modal="false"
@close="handleCloseEditDialog">
<el-form :model="editFormData" :rules="editRules" ref="editFormData" label-width="110px" size="mini" class="add-dialog-form">
<!-- 第一行 -->
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="反馈单号">
<el-input v-model="editFormData.issueNo" placeholder="自动生成" disabled size="mini"></el-input>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="问题日期" prop="issueDate">
<el-date-picker
v-model="editFormData.issueDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
size="mini"
style="width: 100%">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="要求回复日期" prop="requireReplyDate">
<el-date-picker
v-model="editFormData.requireReplyDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期"
size="mini"
style="width: 100%">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="供应商编码">
<el-input v-model="editFormData.supplierNo" disabled size="mini"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="供应商名称">
<el-input v-model="editFormData.supplierName" disabled size="mini"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="10">
<el-col :span="8">
<el-form-item label="问题分类" prop="issueCategory">
<el-select v-model="editFormData.issueCategory" placeholder="请选择" size="mini" style="width: 100%">
<el-option label="产品外观" value="产品外观"></el-option>
<el-option label="产品性能" value="产品性能"></el-option>
<el-option label="尺寸不符" value="尺寸不符"></el-option>
<el-option label="材质问题" value="材质问题"></el-option>
<el-option label="包装破损" value="包装破损"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="问题简述" prop="issueSummary">
<el-input v-model="editFormData.issueSummary" placeholder="请输入问题简述" size="mini"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第五行 -->
<el-row :gutter="10">
<el-col :span="24">
<el-form-item label="问题详细描述">
<el-input
v-model="editFormData.issueDescription"
type="textarea"
:rows="4"
placeholder="请输入问题详细描述"
size="mini">
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleSaveEdit" class="customer-bun-min">保存</el-button>
<el-button @click="handleCloseEditDialog" class="customer-bun-min">关闭</el-button>
</div>
</el-dialog>
<!-- 回复弹窗 -->
<el-dialog
title="质量问题 - 回复"
:visible.sync="replyDialogVisible"
width="50%"
top="10vh"
:close-on-click-modal="false"
@close="handleCloseReplyDialog">
<div class="reply-dialog-info">
<el-row :gutter="20">
<el-col :span="8">
<div class="info-item">
<span class="info-label">反馈单号</span>
<span class="info-value">{{ replyFormData.issueNo }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">回复日期</span>
<span class="info-value">{{ replyFormData.replyTime }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="info-item">
<span class="info-label">回复人</span>
<span class="info-value">{{ replyFormData.replyBy }}</span>
</div>
</el-col>
</el-row>
</div>
<el-form :model="replyFormData" :rules="replyRules" ref="replyFormData" label-position="top" size="mini" class="reply-dialog-form">
<!-- 原因分析 -->
<el-form-item label="原因分析" prop="analysis">
<el-input
v-model="replyFormData.analysis"
type="textarea"
:rows="4"
placeholder="请输入原因分析"
size="mini">
</el-input>
</el-form-item>
<!-- 后续措施 -->
<el-form-item label="后续措施" prop="correctiveAction">
<el-input
v-model="replyFormData.correctiveAction"
type="textarea"
:rows="4"
placeholder="请输入后续措施"
size="mini">
</el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="handleSaveReply" class="customer-bun-min">保存</el-button>
<el-button @click="handleCloseReplyDialog" class="customer-bun-min">关闭</el-button>
</div>
</el-dialog>
<Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist> <Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
</div> </div>
</template> </template>
<script> <script>
import { queryQualityIssuePage, saveQualityIssue } from '@/api/quality/qualityIssue.js'
import { queryQualityIssuePage, saveQualityIssue,deleteQualityIssue,updateQualityIssue, saveReply } from '@/api/quality/qualityIssue.js'
import Chooselist from '@/views/modules/common/Chooselist_eam' import Chooselist from '@/views/modules/common/Chooselist_eam'
import ComQualityIssueDetail from './com_qualityIssueDetail.vue' import ComQualityIssueDetail from './com_qualityIssueDetail.vue'
import ComQualityIssueAttachment from './com_qualityIssueAttachment.vue' import ComQualityIssueAttachment from './com_qualityIssueAttachment.vue'
@ -258,6 +425,51 @@ export default {
{ required: true, message: '请输入问题简述', trigger: 'blur' } { required: true, message: '请输入问题简述', trigger: 'blur' }
] ]
}, },
//
editDialogVisible: false,
editFormData: {
issueNo: '',
issueDate: '',
requireReplyDate: '',
supplierNo: '',
supplierName: '',
issueCategory: '',
issueSummary: '',
issueDescription: '',
site: ''
},
editRules: {
issueDate: [
{ required: true, message: '请选择问题日期', trigger: 'change' }
],
requireReplyDate: [
{ required: true, message: '请选择要求回复日期', trigger: 'change' }
],
issueCategory: [
{ required: true, message: '请选择问题分类', trigger: 'change' }
],
issueSummary: [
{ required: true, message: '请输入问题简述', trigger: 'blur' }
]
},
//
replyDialogVisible: false,
replyFormData: {
site: '',
issueNo: '',
analysis: '',
correctiveAction: '',
replyBy: '',
replyTime: ''
},
replyRules: {
analysis: [
{ required: true, message: '请输入原因分析', trigger: 'blur' }
],
correctiveAction: [
{ required: true, message: '请输入后续措施', trigger: 'blur' }
]
},
tagNo: '', tagNo: '',
searchData: { searchData: {
issueNo: '', issueNo: '',
@ -551,6 +763,12 @@ export default {
this.resetAddForm() this.resetAddForm()
}, },
//
handleCloseEditDialog () {
this.editDialogVisible = false
this.resetEditForm()
},
// //
resetAddForm () { resetAddForm () {
const today = new Date() const today = new Date()
@ -574,7 +792,25 @@ export default {
} }
}, },
//
//
resetEditForm () {
this.editFormData = {
issueNo: '',
issueDate: '',
requireReplyDate: '',
supplierNo: '',
supplierName: '',
issueCategory: '',
issueSummary: '',
issueDescription: '',
site: ''
}
if (this.$refs.currentFormData) {
this.$refs.currentFormData.clearValidate()
}
},
//
selectSupplier () { selectSupplier () {
this.tagNo = 1100 this.tagNo = 1100
this.$nextTick(() => { this.$nextTick(() => {
@ -621,27 +857,195 @@ export default {
}) })
}, },
//
handleSaveEdit () {
this.$refs.editFormData.validate((valid) => {
if (valid) {
//
const submitData = {
site: this.editFormData.site,
issueNo: this.editFormData.issueNo,
issueDate: this.editFormData.issueDate,
requireReplyDate: this.editFormData.requireReplyDate,
issueCategory: this.editFormData.issueCategory,
issueSummary: this.editFormData.issueSummary,
issueDescription: this.editFormData.issueDescription
}
console.log('更新数据:', submitData)
updateQualityIssue(submitData).then(({ data }) => {
console.log('更新接口返回:', data)
if (data.code === 0) {
this.$message.success('更新成功')
this.handleCloseEditDialog()
this.getMainData()
} else {
this.$message.error(data.msg || '更新失败')
}
}).catch((error) => {
console.error('更新接口错误:', error)
this.$message.error('更新失败,请稍后重试')
})
} else {
console.log('表单验证失败')
this.$message.warning('请填写完整的表单信息')
}
})
},
// //
editQualityIssue (row) { editQualityIssue (row) {
this.$message.info('编辑功能开发中...')
this.editDialogVisible = true
//
this.editFormData = {
issueNo: row.issueNo || '',
issueDate: row.issueDate || '',
requireReplyDate: row.requireReplyDate || '',
supplierNo: row.supplierNo || '',
supplierName: row.supplierName || '',
issueCategory: row.issueCategory || '',
issueSummary: row.issueSummary || '',
issueDescription: row.issueDescription || '',
site: row.site || this.$store.state.user.site
}
//
this.$nextTick(() => {
if (this.$refs.editFormData) {
this.$refs.editFormData.clearValidate()
}
})
}, },
// //
deleteQualityIssue (row) {
this.$confirm('确定要删除该质量问题吗?', '提示', {
confirmButtonText: '确定',
deleteQualityIssue(row) {
this.$confirm('确定要删除该质量问题吗?删除后无法恢复!', '提示', {
confirmButtonText: '确定删除',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning'
type: 'warning',
dangerouslyUseHTMLString: true
}).then(() => { }).then(() => {
this.$message.info('删除功能开发中...')
//
const loading = this.$loading({
lock: true,
text: '删除中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
//
const deleteParams = {
site: row.site || this.$store.state.user.site,
issueNo: row.issueNo
};
console.log('删除参数:', deleteParams);
//
deleteQualityIssue(deleteParams).then(({ data }) => {
loading.close();
if (data.code === 0) {
this.$message.success('删除成功');
//
const isLastItem = this.mainDataList.length === 1;
//
if (isLastItem && this.pageIndex > 1) {
//
this.pageIndex--;
}
this.getMainData();
} else {
this.$message.error(data.msg || '删除失败');
}
}).catch((error) => {
loading.close();
console.error('删除接口错误:', error);
this.$message.error('删除失败,请稍后重试');
});
}).catch(() => { }).catch(() => {
this.$message.info('已取消删除')
})
this.$message.info('已取消删除');
});
}, },
// //
replyQualityIssue (row) { replyQualityIssue (row) {
this.$message.info('回复功能开发中...')
this.replyDialogVisible = true
//
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const currentDate = `${year}-${month}-${day}`
this.replyFormData = {
site: row.site || this.$store.state.user.site,
issueNo: row.issueNo || '',
analysis: '',
correctiveAction: '',
replyBy: this.$store.state.user.name || '',
replyTime: currentDate
}
//
this.$nextTick(() => {
if (this.$refs.replyFormData) {
this.$refs.replyFormData.clearValidate()
}
})
},
//
handleCloseReplyDialog () {
this.replyDialogVisible = false
this.resetReplyForm()
},
//
resetReplyForm () {
this.replyFormData = {
site: '',
issueNo: '',
analysis: '',
correctiveAction: '',
replyBy: '',
replyTime: ''
}
if (this.$refs.replyFormData) {
this.$refs.replyFormData.clearValidate()
}
},
//
handleSaveReply () {
this.$refs.replyFormData.validate((valid) => {
if (valid) {
//
const submitData = {
site: this.replyFormData.site,
issueNo: this.replyFormData.issueNo,
analysis: this.replyFormData.analysis,
correctiveAction: this.replyFormData.correctiveAction
}
console.log('回复数据:', submitData)
saveReply(submitData).then(({ data }) => {
console.log('回复接口返回:', data)
if (data.code === 0) {
this.$message.success('回复成功')
this.handleCloseReplyDialog()
this.getMainData()
} else {
this.$message.error(data.msg || '回复失败')
}
}).catch((error) => {
console.error('回复接口错误:', error)
this.$message.error('回复失败,请稍后重试')
})
} else {
console.log('表单验证失败')
this.$message.warning('请填写完整的回复信息')
}
})
}, },
// //
@ -707,6 +1111,10 @@ export default {
gap: 15px; gap: 15px;
} }
.search-row:last-child {
margin-top: -10px;
}
.search-item { .search-item {
flex: none; flex: none;
margin-bottom: 10px; margin-bottom: 10px;
@ -769,4 +1177,52 @@ export default {
text-align: center; text-align: center;
} }
/* 回复弹窗样式 */
.reply-dialog-info {
margin-bottom: 20px;
padding: 15px;
background-color: #f5f7fa;
border-radius: 4px;
}
.reply-dialog-info .info-item {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.reply-dialog-info .info-item:last-child {
margin-bottom: 0;
}
.reply-dialog-info .info-label {
font-size: 13px;
color: #606266;
font-weight: 500;
min-width: 80px;
}
.reply-dialog-info .info-value {
font-size: 13px;
color: #303133;
flex: 1;
}
.reply-dialog-form /deep/ .el-form-item {
margin-bottom: 15px;
}
.reply-dialog-form /deep/ .el-form-item__label {
font-size: 13px;
color: #606266;
font-weight: 500;
padding-bottom: 5px;
line-height: 1;
height: auto;
}
.reply-dialog-form /deep/ .el-form-item__content {
line-height: normal;
}
</style> </style>
Loading…
Cancel
Save