Browse Source

支持任意一位生产、重量经理审批通过即可流转

master
han\hanst 2 weeks ago
parent
commit
46ce0c67bc
  1. 10
      src/api/erf/erf.js
  2. 98
      src/views/modules/erf/approvalCycleReport.vue
  3. 44
      src/views/modules/erf/components/expRawMaterialList.vue
  4. 20
      src/views/modules/erf/expApplyList.vue
  5. 98
      src/views/modules/erf/sampleCycleReport.vue

10
src/api/erf/erf.js

@ -210,6 +210,11 @@ export const getPartDescByPartNo = data => createAPI(`/erf/rawMaterial/getPartDe
*/
export const getSampleCycleReportList = data => createAPI(`/erf/sampleCycleReport/list`, 'post', data)
/**
* 导出样品完成周期报表
*/
export const exportSampleCycleReport = data => createAPI(`/erf/sampleCycleReport/export`, 'post', data, 'download')
// =====================================================
// 审批周期报表 API接口定义
// =====================================================
@ -220,6 +225,11 @@ export const getSampleCycleReportList = data => createAPI(`/erf/sampleCycleRepor
*/
export const getApprovalCycleReportList = data => createAPI(`/erf/approvalCycleReport/list`, 'post', data)
/**
* 导出审批周期报表
*/
export const exportApprovalCycleReport = data => createAPI(`/erf/approvalCycleReport/export`, 'post', data, 'download')
/**
* 催办向指定申请单所有未确认的审批人技术经理生产经理质量经理计划员发送催办邮件
*/

98
src/views/modules/erf/approvalCycleReport.vue

@ -107,6 +107,9 @@
<el-form-item label=" " style="margin-top: -11px">
<el-button @click="getDataList('Y')" type="primary" class="search-btn" plain>查询</el-button>
<el-button @click="resetQuery()" plain class="reset-btn">重置</el-button>
<el-button @click="exportReport()" plain class="export-btn" :loading="exportLoading">
{{ exportLoading ? '导出中...' : '导出' }}
</el-button>
</el-form-item>
</el-form>
@ -335,7 +338,7 @@
</template>
<script>
import { getApprovalCycleReportList } from '@/api/erf/erf'
import { getApprovalCycleReportList, exportApprovalCycleReport } from '@/api/erf/erf'
export default {
name: 'ApprovalCycleReport',
@ -367,6 +370,7 @@ export default {
pageSize: 20,
totalPage: 0,
dataListLoading: false,
exportLoading: false,
//
tableHeight: 500
@ -464,6 +468,85 @@ export default {
this.getDataList('Y')
},
/**
* 导出报表
*/
exportReport() {
this.exportLoading = true
const exportParams = { ...this.queryHeaderData }
delete exportParams.page
delete exportParams.limit
exportApprovalCycleReport(exportParams).then((response) => {
const contentType = (response.headers['content-type'] || '').toLowerCase()
if (!contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
this.handleExportErrorResponse(response.data)
return
}
const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const fileName = this.getDownloadFileName(response, `审批周期报表_${Date.now()}.xlsx`)
const linkNode = document.createElement('a')
linkNode.download = fileName
linkNode.style.display = 'none'
linkNode.href = URL.createObjectURL(blob)
document.body.appendChild(linkNode)
linkNode.click()
URL.revokeObjectURL(linkNode.href)
document.body.removeChild(linkNode)
this.$message.success('导出成功')
}).catch(() => {
this.$message.error('导出失败')
}).finally(() => {
this.exportLoading = false
})
},
/**
* 处理导出错误响应JSON/HTML等非Excel内容
*/
handleExportErrorResponse(blobData) {
const reader = new FileReader()
reader.onload = () => {
let errorMsg = '导出失败'
try {
const result = JSON.parse(reader.result)
errorMsg = result.msg || result.message || errorMsg
} catch (e) {
if (reader.result && String(reader.result).indexOf('404') > -1) {
errorMsg = '导出接口不存在,请确认后端已更新并重启'
}
}
this.$message.error(errorMsg)
}
reader.readAsText(blobData, 'utf-8')
},
/**
* 解析下载文件名
*/
getDownloadFileName(response, defaultName) {
const disposition = response.headers['content-disposition']
if (!disposition) {
return defaultName
}
const utf8Match = disposition.match(/filename\*=utf-8''([^;]+)/i)
if (utf8Match && utf8Match[1]) {
return decodeURIComponent(utf8Match[1])
}
const normalMatch = disposition.match(/filename="?([^"]+)"?/i)
if (normalMatch && normalMatch[1]) {
return decodeURIComponent(normalMatch[1])
}
return defaultName
},
/**
* 行点击事件
*/
@ -747,4 +830,17 @@ export default {
border-color: #909399;
color: #FFFFFF;
}
/* 导出按钮 - 绿色扁平 */
.export-btn {
background-color: #F0F9EB;
border-color: #C2E7B0;
color: #67C23A;
}
.export-btn:hover {
background-color: #67C23A;
border-color: #67C23A;
color: #FFFFFF;
}
</style>

44
src/views/modules/erf/components/expRawMaterialList.vue

@ -261,7 +261,7 @@
</el-table-column>
<el-table-column
prop="operationDesc"
prop="operationDescDisplay"
label="操作摘要"
min-width="240"
show-overflow-tooltip>
@ -551,7 +551,13 @@ export default {
getRawMaterialChangeLogList({ applyNo: this.applyNo }).then(({data}) => {
this.changeLogLoading = false
if (data && data.code === 0) {
this.changeLogList = data.list || []
const logList = data.list || []
this.changeLogList = logList.map(item => {
const rowData = item || {}
return Object.assign({}, rowData, {
operationDescDisplay: this.buildOperationDescDisplay(rowData)
})
})
} else {
this.changeLogList = []
this.$message.error(data.msg || '查询原材料修改记录失败')
@ -858,6 +864,40 @@ export default {
*/
formatOperationType(operationType) {
return operationType ? String(operationType).trim() : ''
},
/**
* 生成摘要展示文本兼容历史仅字段名日志
*/
buildOperationDescDisplay(logItem) {
const rowData = logItem || {}
const operationDesc = rowData.operationDesc ? String(rowData.operationDesc).trim() : ''
const operationType = this.formatOperationType(rowData.operationType)
const detailDesc = this.buildOperationDescFromDetail(rowData.detailContent)
if (operationType === '修改' && detailDesc) {
return detailDesc
}
return operationDesc || detailDesc || '-'
},
/**
* 从详细说明提取旧值 -> 新值明细并拼成摘要
*/
buildOperationDescFromDetail(detailContent) {
if (!detailContent) {
return ''
}
const diffList = String(detailContent)
.split('\n')
.map(item => item.trim())
.filter(item => /^\d+\.\s+.+\s+->\s+.+$/.test(item))
.map(item => item.replace(/^\d+\.\s*/, ''))
if (diffList.length === 0) {
return ''
}
return `非草稿状态修改原材料,变更明细:${diffList.join(';')}`
}
}
}

20
src/views/modules/erf/expApplyList.vue

@ -606,8 +606,13 @@
:value="manager.userId">
</el-option>
</el-select>
<el-checkbox v-model="submitData.prodManagerAnyApproved" style="margin-top: 6px">
任意一位生产经理审批通过即可流转
</el-checkbox>
<div style="margin-top: 1px; color: #909399; font-size: 12px">
可多选所有选中的生产经理都必须审批通过后才会流转到计划员排产
{{ submitData.prodManagerAnyApproved
? '已启用任意一位生产经理审批通过即可流转'
: '可多选,所有选中的生产经理都必须审批通过后才会流转到计划员排产' }}
</div>
</el-form-item>
@ -625,8 +630,13 @@
:value="manager.userId">
</el-option>
</el-select>
<el-checkbox v-model="submitData.qualityManagerAnyApproved" style="margin-top: 6px">
任意一位质量经理审批通过即可流转
</el-checkbox>
<div style="margin-top: 1px; color: #909399; font-size: 12px">
可多选所有选中的质量经理都必须审批通过后才会流转到计划员排产
{{ submitData.qualityManagerAnyApproved
? '已启用任意一位质量经理审批通过即可流转'
: '可多选,所有选中的质量经理都必须审批通过后才会流转到计划员排产' }}
</div>
</el-form-item>
@ -744,6 +754,8 @@ export default {
techManagerName: '',
prodManagerIds: [],
qualityManagerIds: [],
prodManagerAnyApproved: true,
qualityManagerAnyApproved: true,
plannerIds: []
},
prodManagerList: [], //
@ -1119,6 +1131,8 @@ export default {
techManagerName: data.techManager.managerName,
prodManagerIds: defaultProdManagerIds,
qualityManagerIds: defaultQualityManagerIds,
prodManagerAnyApproved: true,
qualityManagerAnyApproved: true,
plannerIds: (() => {
// Joyce
const joyce = this.plannerList.find(p =>
@ -1183,6 +1197,8 @@ export default {
techManagerId: this.submitData.techManagerId,
prodManagerIds: this.submitData.prodManagerIds,
qualityManagerIds: this.submitData.qualityManagerIds,
prodManagerAnyApproved: this.submitData.prodManagerAnyApproved,
qualityManagerAnyApproved: this.submitData.qualityManagerAnyApproved,
plannerUserIds: this.submitData.plannerIds
}).then(({data}) => {
this.submitLoading = false

98
src/views/modules/erf/sampleCycleReport.vue

@ -131,6 +131,9 @@
<el-form-item label=" " style="margin-top: -11px">
<el-button @click="getDataList('Y')" type="primary" class="search-btn" plain>查询</el-button>
<el-button @click="resetQuery()" plain class="reset-btn">重置</el-button>
<el-button @click="exportReport()" plain class="export-btn" :loading="exportLoading">
{{ exportLoading ? '导出中...' : '导出' }}
</el-button>
</el-form-item>
</el-form>
@ -282,7 +285,7 @@
</template>
<script>
import { getSampleCycleReportList } from '@/api/erf/erf'
import { getSampleCycleReportList, exportSampleCycleReport } from '@/api/erf/erf'
export default {
name: 'SampleCycleReport',
@ -316,6 +319,7 @@ export default {
pageSize: 20,
totalPage: 0,
dataListLoading: false,
exportLoading: false,
//
tableHeight: 500
@ -392,6 +396,85 @@ export default {
this.getDataList('Y')
},
/**
* 导出报表
*/
exportReport() {
this.exportLoading = true
const exportParams = { ...this.queryHeaderData }
delete exportParams.page
delete exportParams.limit
exportSampleCycleReport(exportParams).then((response) => {
const contentType = (response.headers['content-type'] || '').toLowerCase()
if (!contentType.includes('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')) {
this.handleExportErrorResponse(response.data)
return
}
const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const fileName = this.getDownloadFileName(response, `样品完成周期报表_${Date.now()}.xlsx`)
const linkNode = document.createElement('a')
linkNode.download = fileName
linkNode.style.display = 'none'
linkNode.href = URL.createObjectURL(blob)
document.body.appendChild(linkNode)
linkNode.click()
URL.revokeObjectURL(linkNode.href)
document.body.removeChild(linkNode)
this.$message.success('导出成功')
}).catch(() => {
this.$message.error('导出失败')
}).finally(() => {
this.exportLoading = false
})
},
/**
* 处理导出错误响应JSON/HTML等非Excel内容
*/
handleExportErrorResponse(blobData) {
const reader = new FileReader()
reader.onload = () => {
let errorMsg = '导出失败'
try {
const result = JSON.parse(reader.result)
errorMsg = result.msg || result.message || errorMsg
} catch (e) {
if (reader.result && String(reader.result).indexOf('404') > -1) {
errorMsg = '导出接口不存在,请确认后端已更新并重启'
}
}
this.$message.error(errorMsg)
}
reader.readAsText(blobData, 'utf-8')
},
/**
* 解析下载文件名
*/
getDownloadFileName(response, defaultName) {
const disposition = response.headers['content-disposition']
if (!disposition) {
return defaultName
}
const utf8Match = disposition.match(/filename\*=utf-8''([^;]+)/i)
if (utf8Match && utf8Match[1]) {
return decodeURIComponent(utf8Match[1])
}
const normalMatch = disposition.match(/filename="?([^"]+)"?/i)
if (normalMatch && normalMatch[1]) {
return decodeURIComponent(normalMatch[1])
}
return defaultName
},
/**
* 行点击事件
*/
@ -675,4 +758,17 @@ export default {
border-color: #909399;
color: #FFFFFF;
}
/* 导出按钮 - 绿色扁平 */
.export-btn {
background-color: #F0F9EB;
border-color: #C2E7B0;
color: #67C23A;
}
.export-btn:hover {
background-color: #67C23A;
border-color: #67C23A;
color: #FFFFFF;
}
</style>
Loading…
Cancel
Save