Files
klp-oa/klp-ui/src/views/mes/qc/qualityReview/index.vue
王文昊 eef375cc2a feat(wms/move): 新增调拨单记忆输入并优化权限与功能
1. 新增MemoInput组件的maxSuggestions配置,限制下拉提示条数
2. 为调拨单相关页面替换输入框为带记忆功能的MemoInput
3. 移除冗余的regrade_quality_type字典依赖
4. 给删除按钮增加glc角色权限控制
5. 简化删除操作的Promise链式调用逻辑
6. 新增调拨单号、名称的枚举存储key
2026-07-08 10:34:10 +08:00

895 lines
35 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 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 === 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-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="100" 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-column prop="beforeWarehouseName" label="当前库区" width="100">
<template slot-scope="scope">
<span>{{ scope.row.beforeWarehouseName || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="targetWarehouseName" label="改判后库区" width="120">
<template slot-scope="scope">
<span>{{ scope.row.targetWarehouseName || '不移动' }}</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 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时显示审批表单创建时已填好内容领导仅确认/驳回 -->
<div v-if="currentRow.flowStatus === 2" class="section-editor">
<!-- 领导意见只读展示 -->
<div class="approve-opinion-box">
<label>领导审批意见</label>
<div class="opinion-content">{{ currentRow.leaderOpinion || '暂未填写' }}</div>
<div class="opinion-footer" v-if="currentRow.leaderOpinion">
<span>签字{{ currentRow.leaderSign || '-' }}</span>
<span>日期{{ currentRow.leaderSignDate || '-' }}</span>
</div>
</div>
<!-- 各钢卷改判后等级只读展示 -->
<div class="approve-coil-section">
<div class="approve-coil-title">各钢卷改判后质量状态</div>
<el-table :data="coilList" size="small" border max-height="300">
<el-table-column prop="currentCoilNo" label="产品卷号" width="140" />
<el-table-column prop="spec" label="规格(mm)" width="100" />
<el-table-column prop="defectDesc" label="缺陷描述" min-width="160" show-overflow-tooltip />
<el-table-column prop="beforeQuality" label="当前等级" width="80" align="center">
<template slot-scope="s"><el-tag size="mini" type="danger">{{ s.row.beforeQuality }}</el-tag></template>
</el-table-column>
<el-table-column prop="regradeQuality" label="改判后等级" width="100" align="center">
<template slot-scope="s"><el-tag size="mini" type="warning">{{ s.row.regradeQuality }}</el-tag></template>
</el-table-column>
<el-table-column prop="beforeWarehouseName" label="当前库区" width="100" />
<el-table-column prop="targetWarehouseName" label="改判后库区" width="120">
<template slot-scope="s">
<span>{{ s.row.targetWarehouseName || '不移动' }}</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="approve-actions">
<el-radio-group v-model="approveAction" size="small">
<el-radio-button label="approve">通过</el-radio-button>
<el-radio-button label="reject">驳回</el-radio-button>
</el-radio-group>
<div class="approve-action-body">
<template v-if="approveAction === 'approve'">
<p class="approve-hint">确认审批通过后将直接执行改判钢卷质量状态和库区将同步更新</p>
<el-button type="success" @click="doApprove" :size="'small'" icon="el-icon-check" round>确认通过</el-button>
</template>
<template v-else>
<el-input type="textarea" :rows="2" v-model="rejectReason" placeholder="请输入驳回原因..." style="margin-bottom:8px;" />
<el-button type="danger" @click="doReject" :size="'small'" icon="el-icon-close" round>确认驳回</el-button>
</template>
</div>
</div>
</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="1200px" :close-on-click-modal="false" append-to-body class="review-dialog">
<el-form ref="form" :model="editForm" :rules="rules" label-width="100px" size="small">
<!-- ===== 基本信息 ===== -->
<div class="dialog-section">
<div class="dialog-section-header">
<i class="el-icon-info"></i>
<span>基本信息</span>
</div>
<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="12">
<el-form-item label="传递人" prop="transmitUser">
<el-input v-model="editForm.transmitUser" placeholder="填写传递人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<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-row>
</div>
<!-- ===== 钢卷选择 ===== -->
<div class="dialog-section">
<div class="dialog-section-header">
<span>问题钢卷</span>
</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="150">
<template slot-scope="scope">
<el-input v-model="scope.row.defectDesc" size="mini" placeholder="输入缺陷" />
</template>
</el-table-column>
<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="120">
<template slot-scope="scope">
<el-select v-model="scope.row.regradeQuality" placeholder="请选择" size="mini" style="width:100%;">
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
</el-table-column>
<el-table-column prop="beforeWarehouseName" label="当前库区" width="100">
<template slot-scope="scope">
<span>{{ scope.row.beforeWarehouseName || '-' }}</span>
</template>
</el-table-column>
<el-table-column prop="targetWarehouseId" label="改判后库区" width="140">
<template slot-scope="scope">
<el-select v-model="scope.row.targetWarehouseId" placeholder="请选择" size="mini" style="width:100%;" @change="onTargetWarehouseChange(scope.row)">
<el-option label="不移动" :value="null" />
<el-option v-for="w in warehouseOptions" v-if="w.warehouseType === 1" :key="w.warehouseId" :label="w.warehouseName" :value="w.warehouseId" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" width="55" 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" @click="showCoilSelector = true">
<i class="el-icon-circle-plus-outline"></i>
<span>点击选择O级钢卷</span>
</div>
</div>
<!-- ===== 钢卷选择器嵌套在弹窗内 ===== -->
<CoilSelector
:visible.sync="showCoilSelector"
:multiple="true"
:filters="{ qualityStatusCsv: 'O', status: 0 }"
@confirm="onDialogCoilConfirm" />
<!-- ===== 品质部评审 ===== -->
<div class="dialog-section">
<div class="dialog-section-header">
<i class="el-icon-edit-outline"></i>
<span>品质部评审意见</span>
</div>
<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="12">
<el-form-item label="签字人">
<el-input v-model="editForm.deptSign" placeholder="签字人姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="签字日期">
<el-date-picker v-model="editForm.deptSignDate" type="date" placeholder="选择日期" style="width:100%;" />
</el-form-item>
</el-col>
</el-row>
</div>
<!-- ===== 领导审批 ===== -->
<div class="dialog-section">
<div class="dialog-section-header">
<i class="el-icon-s-check"></i>
<span>领导审批意见</span>
</div>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="审批意见" prop="leaderOpinion">
<el-input type="textarea" :rows="3" v-model="editForm.leaderOpinion" placeholder="请填写领导审批意见" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="签字人">
<el-input v-model="editForm.leaderSign" placeholder="领导姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="签字日期">
<el-date-picker v-model="editForm.leaderSignDate" type="date" placeholder="选择日期" style="width:100%;" />
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" icon="el-icon-close">取消</el-button>
<el-button type="primary" @click="saveForm" icon="el-icon-check">确定</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'
import { listWarehouse } from '@/api/wms/warehouse'
export default {
name: 'QualityReview',
components: { CoilSelector },
dicts: ['quality_review_status', 'coil_quality_status'],
data() {
return {
// 列表
loading: false,
dataList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 20,
reviewNo: undefined,
flowStatus: undefined
},
currentRow: null,
// 详情
detailLoading: false,
coilList: [],
logList: [],
// 钢卷选择
showCoilSelector: false,
warehouseOptions: [], // 库区下拉选项
// 编辑弹窗
dialogVisible: false,
dialogTitle: '',
editing: false,
editForm: {
reviewId: undefined,
productName: '',
transmitDept: '品质部',
transmitUser: '',
transmitDate: undefined,
prodDateRange: '',
deptOpinion: '',
deptSign: '',
deptSignDate: undefined,
leaderOpinion: '',
leaderSign: '',
leaderSignDate: undefined,
coilList: []
},
rules: {
productName: [{ required: true, message: '请输入产品名称', trigger: 'blur' }]
},
// 审批
approveAction: 'approve',
approveForm: {
reviewId: undefined,
leaderOpinion: '',
leaderSign: ''
},
approveCoilMap: {},
rejectReason: ''
}
},
created() {
this.getList()
},
watch: {
dialogVisible(val) {
if (val) this.loadWarehouseOptions()
}
},
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.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,
leaderOpinion: '',
leaderSign: '',
leaderSignDate: 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,
leaderOpinion: row.leaderOpinion || '',
leaderSign: row.leaderSign || '',
leaderSignDate: row.leaderSignDate || undefined,
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 existingIds = new Set(this.editForm.coilList.map(c => c.coilId))
const newSelected = selected.filter(coil => !existingIds.has(coil.coilId))
const dupCount = selected.length - newSelected.length
if (dupCount > 0) {
this.$message.warning(`已跳过 ${dupCount} 个重复钢卷`)
}
if (newSelected.length === 0) return
const startIdx = this.editForm.coilList.length || 0
const newCoils = newSelected.map((coil, idx) => ({
coilId: coil.coilId,
currentCoilNo: coil.currentCoilNo,
supplierCoilNo: coil.supplierCoilNo || coil.enterCoilNo,
spec: coil.specification,
netWeight: coil.netWeight,
defectDesc: '',
regradeQuality: '',
beforeWarehouseId: coil.warehouseId || null,
beforeWarehouseName: this.getWarehouseName(coil.warehouseId),
targetWarehouseId: null,
targetWarehouseName: '',
groupSeq: startIdx + idx + 1,
beforeQuality: coil.qualityStatus || 'O'
}))
this.editForm.coilList = [...(this.editForm.coilList || []), ...newCoils]
},
getWarehouseName(warehouseId) {
if (!warehouseId) return ''
const wh = this.warehouseOptions.find(w => w.warehouseId === warehouseId)
return wh ? wh.warehouseName : ''
},
handleRemoveCoil(index) {
this.coilList.splice(index, 1)
},
// ===== 库区选择 =====
loadWarehouseOptions() {
listWarehouse().then(res => {
this.warehouseOptions = res.data || []
})
},
onTargetWarehouseChange(row) {
const selected = this.warehouseOptions.find(w => w.warehouseId === row.targetWarehouseId)
row.targetWarehouseName = selected ? selected.warehouseName : ''
},
// ===== 提交送审 =====
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(() => {})
},
// ===== 审批 =====
doApprove() {
this.$confirm('确认审批通过?审批后将直接执行改判,钢卷质量状态将同步更新。', '提示', { type: 'success' }).then(() => {
approveQualityReview(this.currentRow.reviewId).then(() => {
this.$modal.msgSuccess('审批通过')
this.handleRefreshDetail()
})
}).catch(() => {})
},
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)
},
}
}
</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;
}
}
.approve-actions {
background: #fafafa;
border-radius: 4px;
padding: 16px;
margin-top: 8px;
}
.approve-actions .approve-action-body {
margin-top: 12px;
}
.approve-actions .approve-hint {
margin: 0 0 10px;
font-size: 13px;
color: #909399;
}
.text-muted { color: #c0c4cc; }
.timeline-opinion {
font-size: 13px;
color: #606266;
margin-top: 4px;
background: #f5f7fa;
padding: 6px 10px;
border-radius: 4px;
}
}
}
</style>