- 新增质量评审相关的实体、Mapper、Service、Controller接口与实现 - 新增前端页面与API接口,支持评审单增删改查、提交送审、审批驳回、改判执行 - 新增数据库初始化脚本与字典数据 - 修复vue.config热加载监视系统文件导致的EBUSY错误 - 清理HRM模块API导出注释
816 lines
31 KiB
Vue
816 lines
31 KiB
Vue
<template>
|
||
<div class="app-container quality-review-container">
|
||
<div class="left-panel">
|
||
<div class="panel-header">
|
||
<div class="header-title">
|
||
<i class="el-icon-document-checked"></i>
|
||
<span>质量评审单</span>
|
||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||
</div>
|
||
<el-select v-model="queryParams.flowStatus" placeholder="全部状态" clearable size="mini" @change="handleQuery" class="header-filter">
|
||
<el-option v-for="item in dict.type.quality_review_status" :key="item.value" :label="item.label" :value="parseInt(item.value)" />
|
||
</el-select>
|
||
</div>
|
||
|
||
<div class="search-row">
|
||
<el-input v-model="queryParams.reviewNo" placeholder="搜索评审单编号..." clearable prefix-icon="el-icon-search" size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||
<el-button type="primary" size="small" @click="handleAdd" v-hasPermi="['qc:qualityReview:add']">
|
||
<i class="el-icon-plus"></i>
|
||
</el-button>
|
||
</div>
|
||
|
||
<div v-loading="loading" class="list-body">
|
||
<div v-for="item in dataList" :key="item.reviewId" class="list-item"
|
||
:class="{ active: currentRow && currentRow.reviewId === item.reviewId }" @click="handleRowClick(item)">
|
||
<div class="item-main">
|
||
<span class="item-title">{{ item.reviewNo }}</span>
|
||
<span class="item-sub">{{ item.transmitUser }} · {{ item.productName }}</span>
|
||
</div>
|
||
<div class="item-meta">
|
||
<el-tag v-if="item.flowStatus === 1" type="info" size="mini">待提交</el-tag>
|
||
<el-tag v-else-if="item.flowStatus === 2" type="warning" size="mini">待审批</el-tag>
|
||
<el-tag v-else-if="item.flowStatus === 3" type="success" size="mini">已通过</el-tag>
|
||
<el-tag v-else-if="item.flowStatus === 4" type="danger" size="mini">已驳回</el-tag>
|
||
</div>
|
||
<div class="item-actions">
|
||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(item)" v-hasPermi="['qc:qualityReview:edit']"></el-button>
|
||
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)" v-hasPermi="['qc:qualityReview:delete']"></el-button>
|
||
</div>
|
||
</div>
|
||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||
<i class="el-icon-folder-opened"></i>
|
||
<span>暂无质量评审单</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="list-footer">
|
||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧详情 -->
|
||
<div class="right-panel">
|
||
<div v-if="!currentRow" class="empty-tip">
|
||
<i class="el-icon-info"></i>
|
||
<span>请在左侧列表中选择一条评审单查看详情</span>
|
||
</div>
|
||
<div v-else v-loading="detailLoading" class="detail-content">
|
||
|
||
<!-- ===== 顶部操作栏 ===== -->
|
||
<div class="detail-header">
|
||
<div class="detail-header-left">
|
||
<span class="detail-title">{{ currentRow.reviewNo }}</span>
|
||
<el-tag v-if="currentRow.flowStatus === 1" type="info" size="small">待提交</el-tag>
|
||
<el-tag v-else-if="currentRow.flowStatus === 2" type="warning" size="small">待审批</el-tag>
|
||
<el-tag v-else-if="currentRow.flowStatus === 3" type="success" size="small">已通过</el-tag>
|
||
<el-tag v-else-if="currentRow.flowStatus === 4" type="danger" size="small">已驳回</el-tag>
|
||
</div>
|
||
<div class="detail-header-actions">
|
||
<!-- 待提交:提交送审 -->
|
||
<el-button v-if="currentRow.flowStatus === 1" size="mini" type="primary" plain icon="el-icon-s-promotion"
|
||
@click="handleSubmit" v-hasPermi="['qc:qualityReview:submit']">提交送审</el-button>
|
||
<!-- 待审批:审批/驳回 -->
|
||
<el-button v-if="currentRow.flowStatus === 2" size="mini" type="success" plain icon="el-icon-check"
|
||
@click="handleApprove" v-hasPermi="['qc:qualityReview:approve']">审批通过</el-button>
|
||
<el-button v-if="currentRow.flowStatus === 2" size="mini" type="danger" plain icon="el-icon-close"
|
||
@click="handleReject" v-hasPermi="['qc:qualityReview:approve']">驳回</el-button>
|
||
<!-- 已通过:执行改判 -->
|
||
<el-button v-if="currentRow.flowStatus === 3" size="mini" type="warning" plain icon="el-icon-setting"
|
||
@click="handleExecute" v-hasPermi="['qc:qualityReview:execute']">执行改判</el-button>
|
||
<!-- 已驳回:重新提交 -->
|
||
<el-button v-if="currentRow.flowStatus === 4" size="mini" type="primary" plain icon="el-icon-s-promotion"
|
||
@click="handleReSubmit" v-hasPermi="['qc:qualityReview:submit']">重新提交</el-button>
|
||
<!-- 通用 -->
|
||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail">刷新</el-button>
|
||
<el-button v-if="currentRow.flowStatus === 1 || currentRow.flowStatus === 4" size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)">编辑</el-button>
|
||
<el-button v-if="currentRow.flowStatus === 1 || currentRow.flowStatus === 4" size="mini" type="text" icon="el-icon-delete" @click="handleDelete(currentRow)">删除</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 基本信息 ===== -->
|
||
<el-card class="detail-section" shadow="never">
|
||
<div slot="header"><span><i class="el-icon-info"></i> 基本信息</span></div>
|
||
<el-form :model="currentRow" label-width="100px" size="small" class="basic-form">
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-form-item label="评审单编号">{{ currentRow.reviewNo }}</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="产品名称">{{ currentRow.productName }}</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="传递部门">{{ currentRow.transmitDept }}</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-form-item label="传递人">{{ currentRow.transmitUser }}</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="传递日期">{{ currentRow.transmitDate }}</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="生产日期">{{ currentRow.prodDateRange }}</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
</el-card>
|
||
|
||
<!-- ===== 钢卷明细表(详情只读) ===== -->
|
||
<el-card class="detail-section" shadow="never">
|
||
<div slot="header">
|
||
<span><i class="el-icon-s-management"></i> 问题钢卷明细</span>
|
||
</div>
|
||
<el-table :data="coilList" border style="width: 100%" size="small" max-height="400">
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column prop="currentCoilNo" label="产品卷号" min-width="140" />
|
||
<el-table-column prop="supplierCoilNo" label="原料卷号" min-width="140" />
|
||
<el-table-column prop="spec" label="规格(mm)" width="110" />
|
||
<el-table-column prop="netWeight" label="卷重(t)" width="90" align="right" />
|
||
<el-table-column prop="defectDesc" label="缺陷描述" min-width="200" show-overflow-tooltip />
|
||
<el-table-column prop="beforeQuality" label="改判前" width="80" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag size="mini" type="danger">{{ scope.row.beforeQuality }}</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="regradeQuality" label="改判后" width="110" align="center">
|
||
<template slot-scope="scope">
|
||
<el-tag v-if="scope.row.regradeQuality" size="mini" type="warning">{{ scope.row.regradeQuality }}</el-tag>
|
||
<span v-else class="text-muted">待指定</span>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-card>
|
||
|
||
<!-- ===== 品质部评审意见 ===== -->
|
||
<el-card class="detail-section" shadow="never">
|
||
<div slot="header"><span><i class="el-icon-edit-outline"></i> 品质部评审意见</span></div>
|
||
<div v-if="editing" class="section-editor">
|
||
<el-input type="textarea" :rows="3" v-model="editForm.deptOpinion" placeholder="请填写品质部评审意见..." />
|
||
<div style="margin-top:8px; display:flex; gap:8px;">
|
||
<el-input v-model="editForm.deptSign" placeholder="签字人" style="width:160px;" size="small" />
|
||
<el-date-picker v-model="editForm.deptSignDate" type="date" placeholder="签字日期" size="small" />
|
||
</div>
|
||
<el-button size="small" type="primary" @click="saveOpinion" style="margin-top:8px;">保存意见</el-button>
|
||
</div>
|
||
<div v-else class="section-display">
|
||
<div class="opinion-text">{{ currentRow.deptOpinion || '暂未填写' }}</div>
|
||
<div class="opinion-footer" v-if="currentRow.deptOpinion">
|
||
<span>签字:{{ currentRow.deptSign || '-' }}</span>
|
||
<span>日期:{{ currentRow.deptSignDate || '-' }}</span>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- ===== 领导审批意见 ===== -->
|
||
<el-card class="detail-section" shadow="never">
|
||
<div slot="header"><span><i class="el-icon-s-check"></i> 领导审批意见</span></div>
|
||
<!-- flowStatus=2时显示审批表单,权限由后端API控制,前端不做限制 -->
|
||
<div v-if="currentRow.flowStatus === 2" class="section-editor">
|
||
<!-- 审批通过模式 -->
|
||
<el-radio-group v-model="approveAction" style="margin-bottom:12px;">
|
||
<el-radio-button label="approve">通过</el-radio-button>
|
||
<el-radio-button label="reject">驳回</el-radio-button>
|
||
</el-radio-group>
|
||
|
||
<template v-if="approveAction === 'approve'">
|
||
<el-input type="textarea" :rows="3" v-model="approveForm.leaderOpinion" placeholder="请输入领导审批意见..." />
|
||
<div style="margin:8px 0;">
|
||
<el-input v-model="approveForm.leaderSign" placeholder="签字人" style="width:160px;" size="small" />
|
||
</div>
|
||
<div class="approve-coil-section">
|
||
<div class="approve-coil-title">请为每个钢卷指定改判后质量状态:</div>
|
||
<div v-for="coil in coilList" :key="coil.detailId" class="approve-coil-row">
|
||
<span class="approve-coil-label">{{ coil.currentCoilNo }}({{ coil.spec }})</span>
|
||
<el-select v-model="approveCoilMap[coil.detailId]" placeholder="请选择改判后状态" size="small" style="width:180px;">
|
||
<el-option v-for="item in regradeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
<el-button size="small" type="success" @click="doApprove">确认通过</el-button>
|
||
</template>
|
||
<template v-else>
|
||
<el-input type="textarea" :rows="3" v-model="rejectReason" placeholder="请输入驳回原因..." />
|
||
<el-button size="small" type="danger" @click="doReject" style="margin-top:8px;">确认驳回</el-button>
|
||
</template>
|
||
</div>
|
||
<div v-else class="section-display">
|
||
<div class="opinion-text">{{ currentRow.leaderOpinion || '暂无审批意见' }}</div>
|
||
<div class="opinion-footer" v-if="currentRow.leaderOpinion">
|
||
<span>签字:{{ currentRow.leaderSign || '-' }}</span>
|
||
<span>日期:{{ currentRow.leaderSignDate || '-' }}</span>
|
||
</div>
|
||
<div v-if="currentRow.flowStatus === 4 && currentRow.rejectReason" class="reject-reason">
|
||
<el-alert :title="'驳回原因:' + currentRow.rejectReason" type="error" show-icon :closable="false" />
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
|
||
<!-- ===== 审批日志 ===== -->
|
||
<el-card class="detail-section" shadow="never">
|
||
<div slot="header"><span><i class="el-icon-time"></i> 审批记录</span></div>
|
||
<div v-if="logList.length === 0" class="text-muted" style="padding:12px;">暂无审批记录</div>
|
||
<el-timeline v-else>
|
||
<el-timeline-item v-for="log in logList" :key="log.logId"
|
||
:timestamp="log.createTime"
|
||
:type="log.action === 'submit' ? 'primary' : (log.action === 'approve' ? 'success' : 'danger')"
|
||
:icon="log.action === 'submit' ? 'el-icon-s-promotion' : (log.action === 'approve' ? 'el-icon-check' : 'el-icon-close')">
|
||
<div>
|
||
<strong>{{ log.action === 'submit' ? '提交送审' : (log.action === 'approve' ? '审批通过' : '驳回') }}</strong>
|
||
<span v-if="log.operator"> — {{ log.operator }}</span>
|
||
</div>
|
||
<div v-if="log.opinion" class="timeline-opinion">{{ log.opinion }}</div>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</el-card>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 编辑弹窗 -->
|
||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="900px" :close-on-click-modal="false" append-to-body>
|
||
<el-form ref="form" :model="editForm" :rules="rules" label-width="100px" size="small">
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="产品名称" prop="productName">
|
||
<el-input v-model="editForm.productName" placeholder="如:镀锌产品" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="传递部门" prop="transmitDept">
|
||
<el-input v-model="editForm.transmitDept" placeholder="品质部" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-form-item label="传递人" prop="transmitUser">
|
||
<el-input v-model="editForm.transmitUser" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="传递日期" prop="transmitDate">
|
||
<el-date-picker v-model="editForm.transmitDate" type="date" placeholder="选择日期" style="width:100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="生产日期" prop="prodDateRange">
|
||
<el-input v-model="editForm.prodDateRange" placeholder="如:14-15" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
|
||
<!-- ===== 钢卷选择 ===== -->
|
||
<div class="dialog-section">
|
||
<div class="dialog-section-header">
|
||
<span>问题钢卷</span>
|
||
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="showCoilSelector = true">选择O级钢卷</el-button>
|
||
</div>
|
||
<el-table :data="editForm.coilList" border style="width:100%" size="small" max-height="250">
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column prop="currentCoilNo" label="产品卷号" min-width="130" />
|
||
<el-table-column prop="supplierCoilNo" label="原料卷号" min-width="130" />
|
||
<el-table-column prop="spec" label="规格(mm)" width="100" />
|
||
<el-table-column prop="netWeight" label="卷重(t)" width="80" align="right" />
|
||
<el-table-column prop="defectDesc" label="缺陷描述" min-width="160">
|
||
<template slot-scope="scope">
|
||
<el-input v-model="scope.row.defectDesc" size="mini" placeholder="输入缺陷描述" />
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="操作" width="60" align="center">
|
||
<template slot-scope="scope">
|
||
<el-button size="mini" type="text" icon="el-icon-delete" @click="editForm.coilList.splice(scope.$index, 1)"></el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<div v-if="editForm.coilList.length === 0" class="dialog-table-empty">请点击上方按钮选择O级钢卷</div>
|
||
</div>
|
||
|
||
<!-- ===== 钢卷选择器(嵌套在弹窗内) ===== -->
|
||
<CoilSelector
|
||
:visible.sync="showCoilSelector"
|
||
:multiple="true"
|
||
:filters="{ qualityStatusCsv: 'O', status: 0 }"
|
||
@confirm="onDialogCoilConfirm" />
|
||
|
||
<el-divider />
|
||
|
||
<el-row :gutter="20">
|
||
<el-col :span="24">
|
||
<el-form-item label="品质部意见" prop="deptOpinion">
|
||
<el-input type="textarea" :rows="3" v-model="editForm.deptOpinion" placeholder="请填写品质部评审意见" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="8">
|
||
<el-form-item label="签字人">
|
||
<el-input v-model="editForm.deptSign" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-form-item label="签字日期">
|
||
<el-date-picker v-model="editForm.deptSignDate" type="date" placeholder="选择日期" style="width:100%;" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<span slot="footer">
|
||
<el-button @click="dialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="saveForm">确定</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listQualityReview, getQualityReview, addQualityReview, updateQualityReview, delQualityReview,
|
||
submitQualityReview, approveQualityReview, rejectQualityReview, executeQualityReview,
|
||
listQualityReviewCoil, listQualityReviewLog } from '@/api/mes/qc/qualityReview'
|
||
import CoilSelector from '@/components/CoilSelector'
|
||
|
||
export default {
|
||
name: 'QualityReview',
|
||
components: { CoilSelector },
|
||
dicts: ['quality_review_status', 'regrade_quality_type'],
|
||
data() {
|
||
return {
|
||
// 列表
|
||
loading: false,
|
||
dataList: [],
|
||
total: 0,
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 20,
|
||
reviewNo: undefined,
|
||
flowStatus: undefined
|
||
},
|
||
currentRow: null,
|
||
|
||
// 详情
|
||
detailLoading: false,
|
||
coilList: [],
|
||
logList: [],
|
||
|
||
// 钢卷选择
|
||
showCoilSelector: false,
|
||
|
||
// 编辑弹窗
|
||
dialogVisible: false,
|
||
dialogTitle: '',
|
||
editing: false,
|
||
editForm: {
|
||
reviewId: undefined,
|
||
productName: '',
|
||
transmitDept: '品质部',
|
||
transmitUser: '',
|
||
transmitDate: undefined,
|
||
prodDateRange: '',
|
||
deptOpinion: '',
|
||
deptSign: '',
|
||
deptSignDate: undefined,
|
||
coilList: []
|
||
},
|
||
rules: {
|
||
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
|
||
},
|
||
|
||
// 改判状态选项(硬编码后备,优先使用字典)
|
||
regradeOptions: [
|
||
{ label: '协议销售', value: 'protocol_sale' },
|
||
{ label: '转分剪', value: 'to_slitting' },
|
||
{ label: '降级', value: 'downgrade' },
|
||
{ label: '返修', value: 'rework' },
|
||
{ label: '报废', value: 'scrap' }
|
||
],
|
||
// 审批
|
||
approveAction: 'approve',
|
||
approveForm: {
|
||
reviewId: undefined,
|
||
leaderOpinion: '',
|
||
leaderSign: ''
|
||
},
|
||
approveCoilMap: {},
|
||
rejectReason: ''
|
||
}
|
||
},
|
||
created() {
|
||
this.getList()
|
||
},
|
||
methods: {
|
||
// ===== 列表 =====
|
||
handleQuery() {
|
||
this.queryParams.pageNum = 1
|
||
this.getList()
|
||
},
|
||
getList() {
|
||
this.loading = true
|
||
listQualityReview(this.queryParams).then(res => {
|
||
this.dataList = res.rows || []
|
||
this.total = res.total || 0
|
||
this.loading = false
|
||
}).catch(() => { this.loading = false })
|
||
},
|
||
handleRowClick(row) {
|
||
this.currentRow = row
|
||
this.loadDetail(row.reviewId)
|
||
},
|
||
|
||
// ===== 详情 =====
|
||
loadDetail(reviewId) {
|
||
this.detailLoading = true
|
||
getQualityReview(reviewId).then(res => {
|
||
const data = res.data
|
||
this.currentRow = data
|
||
this.coilList = data.coilList || []
|
||
this.logList = data.logList || []
|
||
// 初始化审批表单数据(先构建完整对象再赋值,解决Vue2响应式问题)
|
||
const map = {}
|
||
this.coilList.forEach(c => {
|
||
map[c.detailId] = c.regradeQuality || ''
|
||
})
|
||
this.approveCoilMap = map
|
||
this.approveForm.reviewId = reviewId
|
||
this.approveForm.leaderOpinion = ''
|
||
this.approveForm.leaderSign = ''
|
||
this.rejectReason = ''
|
||
this.detailLoading = false
|
||
}).catch(() => { this.detailLoading = false })
|
||
},
|
||
handleRefreshDetail() {
|
||
if (this.currentRow) {
|
||
this.loadDetail(this.currentRow.reviewId)
|
||
this.getList()
|
||
}
|
||
},
|
||
|
||
// ===== 新增/编辑 =====
|
||
handleAdd() {
|
||
this.dialogTitle = '新建质量评审单'
|
||
this.editForm = {
|
||
reviewId: undefined,
|
||
productName: '',
|
||
transmitDept: '品质部',
|
||
transmitUser: '',
|
||
transmitDate: undefined,
|
||
prodDateRange: '',
|
||
deptOpinion: '',
|
||
deptSign: '',
|
||
deptSignDate: undefined,
|
||
coilList: []
|
||
}
|
||
this.editing = true
|
||
this.dialogVisible = true
|
||
},
|
||
handleUpdate(row) {
|
||
this.dialogTitle = '编辑质量评审单'
|
||
this.editForm = {
|
||
reviewId: row.reviewId,
|
||
productName: row.productName,
|
||
transmitDept: row.transmitDept,
|
||
transmitUser: row.transmitUser,
|
||
transmitDate: row.transmitDate,
|
||
prodDateRange: row.prodDateRange,
|
||
deptOpinion: row.deptOpinion,
|
||
deptSign: row.deptSign,
|
||
deptSignDate: row.deptSignDate,
|
||
coilList: this.coilList.map(c => ({ ...c }))
|
||
}
|
||
this.editing = true
|
||
this.dialogVisible = true
|
||
},
|
||
saveForm() {
|
||
this.$refs.form.validate(valid => {
|
||
if (!valid) return
|
||
const data = { ...this.editForm, coilList: this.editForm.coilList || [] }
|
||
if (this.editForm.reviewId) {
|
||
updateQualityReview(data).then(() => {
|
||
this.$modal.msgSuccess('修改成功')
|
||
this.dialogVisible = false
|
||
this.handleRefreshDetail()
|
||
})
|
||
} else {
|
||
addQualityReview(data).then(() => {
|
||
this.$modal.msgSuccess('创建成功')
|
||
this.dialogVisible = false
|
||
this.getList()
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
// ===== 钢卷选择(弹窗内) =====
|
||
onDialogCoilConfirm(selected) {
|
||
if (!selected || selected.length === 0) return
|
||
const startIdx = this.editForm.coilList.length || 0
|
||
const newCoils = selected.map((coil, idx) => ({
|
||
coilId: coil.coilId,
|
||
currentCoilNo: coil.currentCoilNo,
|
||
supplierCoilNo: coil.supplierCoilNo || coil.enterCoilNo,
|
||
spec: coil.spec,
|
||
netWeight: coil.netWeight,
|
||
defectDesc: '',
|
||
groupSeq: startIdx + idx + 1,
|
||
beforeQuality: coil.qualityStatus || 'O'
|
||
}))
|
||
this.editForm.coilList = [...(this.editForm.coilList || []), ...newCoils]
|
||
},
|
||
handleRemoveCoil(index) {
|
||
this.coilList.splice(index, 1)
|
||
},
|
||
|
||
// ===== 提交送审 =====
|
||
handleSubmit() {
|
||
this.$confirm('确认提交送审?', '提示', { type: 'warning' }).then(() => {
|
||
submitQualityReview(this.currentRow.reviewId).then(() => {
|
||
this.$modal.msgSuccess('送审成功')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
handleReSubmit() {
|
||
this.$confirm('确认重新提交?提交后将进入待审批状态。', '提示', { type: 'warning' }).then(() => {
|
||
submitQualityReview(this.currentRow.reviewId).then(() => {
|
||
this.$modal.msgSuccess('送审成功')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
// ===== 审批 =====
|
||
handleApprove() {
|
||
this.approveAction = 'approve'
|
||
this.approveForm = {
|
||
reviewId: this.currentRow.reviewId,
|
||
leaderOpinion: '',
|
||
leaderSign: ''
|
||
}
|
||
// 先构建完整对象再赋值
|
||
const map = {}
|
||
this.coilList.forEach(c => {
|
||
map[c.detailId] = c.regradeQuality || ''
|
||
})
|
||
this.approveCoilMap = map
|
||
},
|
||
doApprove() {
|
||
if (!this.approveForm.leaderOpinion) {
|
||
this.$modal.msgError('请输入审批意见')
|
||
return
|
||
}
|
||
const coilRegradeList = this.coilList.map(c => ({
|
||
detailId: c.detailId,
|
||
regradeQuality: this.approveCoilMap[c.detailId]
|
||
}))
|
||
const invalid = coilRegradeList.some(r => !r.regradeQuality)
|
||
if (invalid) {
|
||
this.$modal.msgError('请为每个钢卷指定改判后质量状态')
|
||
return
|
||
}
|
||
this.$confirm('确认审批通过?', '提示', { type: 'success' }).then(() => {
|
||
approveQualityReview({
|
||
reviewId: this.currentRow.reviewId,
|
||
leaderOpinion: this.approveForm.leaderOpinion,
|
||
leaderSign: this.approveForm.leaderSign,
|
||
coilRegradeList
|
||
}).then(() => {
|
||
this.$modal.msgSuccess('审批通过')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
handleReject() {
|
||
this.approveAction = 'reject'
|
||
this.rejectReason = ''
|
||
},
|
||
doReject() {
|
||
if (!this.rejectReason) {
|
||
this.$modal.msgError('请输入驳回原因')
|
||
return
|
||
}
|
||
this.$confirm('确认驳回该评审单?', '提示', { type: 'warning' }).then(() => {
|
||
rejectQualityReview(this.currentRow.reviewId, this.rejectReason).then(() => {
|
||
this.$modal.msgSuccess('已驳回')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
// ===== 执行改判 =====
|
||
handleExecute() {
|
||
this.$confirm('确认执行改判?执行后钢卷质量状态将被更新。', '提示', { type: 'warning' }).then(() => {
|
||
executeQualityReview(this.currentRow.reviewId).then(() => {
|
||
this.$modal.msgSuccess('改判执行成功')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
// ===== 删除 =====
|
||
handleDelete(row) {
|
||
this.$confirm('确认删除评审单【' + (row.reviewNo || row.reviewId) + '】?', '提示', { type: 'warning' }).then(() => {
|
||
delQualityReview(row.reviewId).then(() => {
|
||
this.$modal.msgSuccess('删除成功')
|
||
if (this.currentRow && this.currentRow.reviewId === row.reviewId) {
|
||
this.currentRow = null
|
||
this.coilList = []
|
||
this.logList = []
|
||
}
|
||
this.getList()
|
||
})
|
||
}).catch(() => {})
|
||
},
|
||
|
||
// ===== 辅助 =====
|
||
hasPermi(permi) {
|
||
return this.$store.getters.permissions && this.$store.getters.permissions.includes(permi)
|
||
},
|
||
saveOpinion() {
|
||
if (!this.currentRow) return
|
||
updateQualityReview({
|
||
reviewId: this.currentRow.reviewId,
|
||
deptOpinion: this.editForm.deptOpinion,
|
||
deptSign: this.editForm.deptSign,
|
||
deptSignDate: this.editForm.deptSignDate
|
||
}).then(() => {
|
||
this.$modal.msgSuccess('保存成功')
|
||
this.handleRefreshDetail()
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.quality-review-container {
|
||
display: flex;
|
||
height: calc(100vh - 84px);
|
||
gap: 12px;
|
||
padding: 0;
|
||
|
||
.left-panel {
|
||
width: 320px;
|
||
min-width: 320px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
border: 1px solid #e4e7ed;
|
||
|
||
.panel-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 12px 0;
|
||
|
||
.header-title {
|
||
font-weight: 600;
|
||
font-size: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
.header-filter { width: 130px; }
|
||
}
|
||
.search-row {
|
||
display: flex;
|
||
padding: 8px 12px;
|
||
gap: 6px;
|
||
.el-input { flex: 1; }
|
||
}
|
||
.list-body {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 0 8px;
|
||
}
|
||
.list-item {
|
||
padding: 10px 12px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
cursor: pointer;
|
||
border-radius: 4px;
|
||
margin: 2px 0;
|
||
&:hover { background: #f5f7fa; }
|
||
&.active { background: #ecf5ff; }
|
||
.item-main {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
.item-title { font-weight: 500; font-size: 13px; }
|
||
.item-sub { font-size: 12px; color: #909399; }
|
||
}
|
||
.item-meta { margin-top: 4px; }
|
||
.item-actions {
|
||
margin-top: 4px;
|
||
text-align: right;
|
||
}
|
||
}
|
||
.list-empty {
|
||
text-align: center;
|
||
padding: 40px 0;
|
||
color: #c0c4cc;
|
||
i { font-size: 36px; display: block; margin-bottom: 8px; }
|
||
}
|
||
.list-footer { padding: 8px; }
|
||
}
|
||
|
||
.right-panel {
|
||
flex: 1;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
border: 1px solid #e4e7ed;
|
||
overflow-y: auto;
|
||
padding: 16px;
|
||
|
||
.empty-tip {
|
||
text-align: center;
|
||
padding: 80px 0;
|
||
color: #c0c4cc;
|
||
i { font-size: 48px; display: block; margin-bottom: 12px; }
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #e4e7ed;
|
||
margin-bottom: 12px;
|
||
.detail-header-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
.detail-title { font-size: 16px; font-weight: 600; }
|
||
}
|
||
.detail-header-actions { display: flex; gap: 4px; flex-wrap: wrap; }
|
||
}
|
||
|
||
.detail-section {
|
||
margin-bottom: 12px;
|
||
.basic-form .el-form-item { margin-bottom: 0; }
|
||
}
|
||
|
||
.section-editor {
|
||
.approve-coil-section {
|
||
background: #fafafa;
|
||
padding: 12px;
|
||
border-radius: 4px;
|
||
margin: 8px 0;
|
||
.approve-coil-title { font-weight: 500; margin-bottom: 8px; }
|
||
.approve-coil-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 6px;
|
||
.approve-coil-label { min-width: 200px; font-size: 13px; }
|
||
}
|
||
}
|
||
}
|
||
.section-display {
|
||
.opinion-text {
|
||
background: #fafafa;
|
||
padding: 12px;
|
||
border-radius: 4px;
|
||
line-height: 1.6;
|
||
white-space: pre-wrap;
|
||
}
|
||
.opinion-footer {
|
||
margin-top: 8px;
|
||
display: flex;
|
||
gap: 20px;
|
||
color: #909399;
|
||
font-size: 13px;
|
||
}
|
||
.reject-reason { margin-top: 12px; }
|
||
}
|
||
.dialog-section {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 4px;
|
||
padding: 12px;
|
||
margin-bottom: 16px;
|
||
.dialog-section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
}
|
||
.dialog-table-empty {
|
||
text-align: center;
|
||
padding: 20px;
|
||
color: #c0c4cc;
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
.text-muted { color: #c0c4cc; }
|
||
.timeline-opinion {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
margin-top: 4px;
|
||
background: #f5f7fa;
|
||
padding: 6px 10px;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
}
|
||
</style>
|