Merge remote-tracking branch 'origin/0.8.X' into 0.8.X
# Conflicts: # klp-ui/src/views/micro/pages/acid/components/Model.vue # klp-ui/src/views/micro/pages/acid/index.vue
This commit is contained in:
@@ -35,27 +35,30 @@ export function updatePurchaseRequisition(data) {
|
||||
})
|
||||
}
|
||||
|
||||
// 提交审批
|
||||
export function submitApproval(reqId) {
|
||||
// 提交审批(指定一审人)
|
||||
export function submitApproval(reqId, firstApproverId, firstApproverName) {
|
||||
return request({
|
||||
url: '/erp/purchaseRequisition/' + reqId + '/submit',
|
||||
method: 'put'
|
||||
method: 'put',
|
||||
params: { firstApproverId, firstApproverName }
|
||||
})
|
||||
}
|
||||
|
||||
// 审批通过
|
||||
export function approvePurchase(reqId) {
|
||||
export function approvePurchase(reqId, approvalOpinion) {
|
||||
return request({
|
||||
url: '/erp/purchaseRequisition/' + reqId + '/approve',
|
||||
method: 'put'
|
||||
method: 'put',
|
||||
params: { approvalOpinion }
|
||||
})
|
||||
}
|
||||
|
||||
// 驳回
|
||||
export function rejectPurchase(reqId) {
|
||||
export function rejectPurchase(reqId, approvalOpinion) {
|
||||
return request({
|
||||
url: '/erp/purchaseRequisition/' + reqId + '/reject',
|
||||
method: 'put'
|
||||
method: 'put',
|
||||
params: { approvalOpinion }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,3 +79,11 @@ export function exportPurchaseRequisition(query) {
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询请购审批人列表(角色标识由后端配置)
|
||||
export function selectApproverUsers() {
|
||||
return request({
|
||||
url: '/system/user/approverUsers/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,3 +42,12 @@ export function delInspectionMain(mainId) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量新增检验主及明细,并更新申请单detailIds
|
||||
export function batchInsertMainWithDetails(data) {
|
||||
return request({
|
||||
url: '/wms/inspectionMain/batchInsertWithDetails',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,3 +42,12 @@ export function delInspectionTensileDetail(detailId) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量插入明细并更新申请单detailIds
|
||||
export function batchInsertTensileDetails(data) {
|
||||
return request({
|
||||
url: '/wms/inspectionTensileDetail/batchInsertDetails',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
154
klp-ui/src/api/pltcm/model.js
Normal file
154
klp-ui/src/api/pltcm/model.js
Normal file
@@ -0,0 +1,154 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 厚度分类
|
||||
// ═══════════════════════════════════════
|
||||
export function listThickClass(query) {
|
||||
return request({ url: '/pltcm/thickClass/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getThickClass(classId) {
|
||||
return request({ url: '/pltcm/thickClass/' + classId, method: 'get' })
|
||||
}
|
||||
|
||||
export function addThickClass(data) {
|
||||
return request({ url: '/pltcm/thickClass', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateThickClass(data) {
|
||||
return request({ url: '/pltcm/thickClass', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delThickClass(classIds) {
|
||||
return request({ url: '/pltcm/thickClass/' + classIds, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 宽度分类
|
||||
// ═══════════════════════════════════════
|
||||
export function listWidthClass(query) {
|
||||
return request({ url: '/pltcm/widthClass/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getWidthClass(classId) {
|
||||
return request({ url: '/pltcm/widthClass/' + classId, method: 'get' })
|
||||
}
|
||||
|
||||
export function addWidthClass(data) {
|
||||
return request({ url: '/pltcm/widthClass', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateWidthClass(data) {
|
||||
return request({ url: '/pltcm/widthClass', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delWidthClass(classIds) {
|
||||
return request({ url: '/pltcm/widthClass/' + classIds, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 屈服强度分类
|
||||
// ═══════════════════════════════════════
|
||||
export function listYieldStressClass(query) {
|
||||
return request({ url: '/pltcm/yieldStressClass/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getYieldStressClass(classId) {
|
||||
return request({ url: '/pltcm/yieldStressClass/' + classId, method: 'get' })
|
||||
}
|
||||
|
||||
export function addYieldStressClass(data) {
|
||||
return request({ url: '/pltcm/yieldStressClass', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateYieldStressClass(data) {
|
||||
return request({ url: '/pltcm/yieldStressClass', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delYieldStressClass(classIds) {
|
||||
return request({ url: '/pltcm/yieldStressClass/' + classIds, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 策略关联
|
||||
// ═══════════════════════════════════════
|
||||
export function listStrategy(query) {
|
||||
return request({ url: '/pltcm/strategy/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getStrategy(id) {
|
||||
return request({ url: '/pltcm/strategy/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
export function addStrategy(data) {
|
||||
return request({ url: '/pltcm/strategy', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateStrategy(data) {
|
||||
return request({ url: '/pltcm/strategy', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delStrategy(ids) {
|
||||
return request({ url: '/pltcm/strategy/' + ids, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 屈服强度与钢种对应关系
|
||||
// ═══════════════════════════════════════
|
||||
export function listSteelgrade(query) {
|
||||
return request({ url: '/pltcm/steelgrade/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getSteelgrade(steelGrade) {
|
||||
return request({ url: '/pltcm/steelgrade/' + steelGrade, method: 'get' })
|
||||
}
|
||||
|
||||
export function addSteelgrade(data) {
|
||||
return request({ url: '/pltcm/steelgrade', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateSteelgrade(data) {
|
||||
return request({ url: '/pltcm/steelgrade', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delSteelgrade(steelGrades) {
|
||||
return request({ url: '/pltcm/steelgrade/' + steelGrades, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 比例标准表
|
||||
// ═══════════════════════════════════════
|
||||
export function listRatio(query) {
|
||||
return request({ url: '/pltcm/ratio/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getRatio(id) {
|
||||
return request({ url: '/pltcm/ratio/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
export function addRatio(data) {
|
||||
return request({ url: '/pltcm/ratio', method: 'post', data })
|
||||
}
|
||||
|
||||
export function updateRatio(data) {
|
||||
return request({ url: '/pltcm/ratio', method: 'put', data })
|
||||
}
|
||||
|
||||
export function delRatio(ids) {
|
||||
return request({ url: '/pltcm/ratio/' + ids, method: 'delete' })
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// 设备启用状态
|
||||
// ═══════════════════════════════════════
|
||||
export function listDeviceEnable(query) {
|
||||
return request({ url: '/pltcm/deviceEnable/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
export function getDeviceEnable(id) {
|
||||
return request({ url: '/pltcm/deviceEnable/' + id, method: 'get' })
|
||||
}
|
||||
|
||||
export function updateDeviceEnable(data) {
|
||||
return request({ url: '/pltcm/deviceEnable', method: 'put', data })
|
||||
}
|
||||
@@ -84,7 +84,10 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="业务员" prop="saleName" v-if="orderBy">
|
||||
<el-input v-model="queryParams.saleName" placeholder="请输入业务员" clearable size="small" />
|
||||
<el-select v-model="queryParams.saleName" placeholder="请选择业务员" clearable size="small" filterable allow-create>
|
||||
<el-option v-for="item in dict.type.wip_pack_saleman" :key="item.value" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="合同号" prop="contractNo" v-if="orderBy">
|
||||
@@ -303,7 +306,7 @@ export default {
|
||||
DragResizePanel,
|
||||
OrderDetail
|
||||
},
|
||||
dicts: ['coil_itemname', 'coil_material', 'coil_manufacturer', 'coil_quality_status'],
|
||||
dicts: ['coil_itemname', 'coil_material', 'coil_manufacturer', 'coil_quality_status', 'wip_pack_saleman'],
|
||||
props: {
|
||||
// 非触发器模式下,外部控制显隐(触发器模式下无效)
|
||||
visible: {
|
||||
|
||||
1615
klp-ui/src/components/HomeModules/QuickAccessGroup.vue
Normal file
1615
klp-ui/src/components/HomeModules/QuickAccessGroup.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,28 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selectedValue"
|
||||
:multiple="multiple"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearch"
|
||||
:loading="loading"
|
||||
:placeholder="placeholder"
|
||||
style="width: 100%"
|
||||
@change="handleChange"
|
||||
:clearable="clearable"
|
||||
>
|
||||
<el-option
|
||||
v-for="user in filteredUsers"
|
||||
:key="user.userId"
|
||||
:label="`${user.nickName}(${user.dept && user.dept.deptName ? user.dept.deptName : '无部门'})`"
|
||||
:value="user.userId"
|
||||
<div class="user-select-wrapper" :class="{ 'has-value': selectedValue && (Array.isArray(selectedValue) ? selectedValue.length > 0 : true) }">
|
||||
<el-select
|
||||
v-model="selectedValue"
|
||||
:multiple="multiple"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="handleSearch"
|
||||
:loading="loading"
|
||||
:placeholder="placeholder"
|
||||
style="width: 100%"
|
||||
@change="handleChange"
|
||||
:clearable="clearable"
|
||||
>
|
||||
<span>{{ user.nickName }}</span>
|
||||
<span style="color: #999; font-size: 12px; margin-left: 8px;">{{ user.dept && user.dept.deptName ? user.dept.deptName : '无部门' }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-option
|
||||
v-for="user in filteredUsers"
|
||||
:key="user.userId"
|
||||
:label="`${user.nickName}(${user.dept && user.dept.deptName ? user.dept.deptName : '无部门'})`"
|
||||
:value="user.userId"
|
||||
>
|
||||
<span>{{ user.nickName }}</span>
|
||||
<span style="color: #999; font-size: 12px; margin-left: 8px;">{{ user.dept && user.dept.deptName ? user.dept.deptName : '无部门' }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -109,4 +111,62 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-select-wrapper {
|
||||
position: relative;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 2px 8px;
|
||||
transition: border-color 0.2s, box-shadow 0.2s;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.user-select-wrapper:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
|
||||
.user-select-wrapper.has-value {
|
||||
border-color: #5F7BA0;
|
||||
box-shadow: 0 0 0 1px rgba(95, 123, 160, 0.1);
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-select__tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
position: relative !important;
|
||||
top: auto !important;
|
||||
left: auto !important;
|
||||
transform: none !important;
|
||||
overflow: visible;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-select__tags .el-tag {
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-input {
|
||||
width: auto;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-select .el-select__input {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.user-select-wrapper /deep/ .el-select__tags-text {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Hide the original el-select border since wrapper provides it */
|
||||
.user-select-wrapper /deep/ .el-select .el-input__inner {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
padding-left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,6 +58,16 @@ export default {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 下拉最多展示条数,默认6条
|
||||
maxSuggestions: {
|
||||
type: Number,
|
||||
default: 6,
|
||||
validator: (value) => value > 0
|
||||
},
|
||||
inputFormatter: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
@@ -118,7 +128,7 @@ export default {
|
||||
});
|
||||
}
|
||||
|
||||
cb(results); // 回调返回匹配结果
|
||||
cb(results.slice(0, this.maxSuggestions)); // 回调返回匹配结果(限制条数)
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -126,10 +136,11 @@ export default {
|
||||
* @param {object} item - 选中的项 {value: 'xxx'}
|
||||
*/
|
||||
handleSelect(item) {
|
||||
this.cacheInputValue(item.value);
|
||||
this.inputValue = item.value;
|
||||
const v = this.formatValue(item.value);
|
||||
this.cacheInputValue(v);
|
||||
this.inputValue = v;
|
||||
// 触发自定义事件,通知父组件选中结果
|
||||
this.$emit('input', item.value);
|
||||
this.$emit('input', v);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -138,13 +149,22 @@ export default {
|
||||
*/
|
||||
handleInputChange(value) {
|
||||
if (value && value.trim() !== '') {
|
||||
this.cacheInputValue(value.trim());
|
||||
const v = this.formatValue(value.trim());
|
||||
this.cacheInputValue(v);
|
||||
// 触发自定义事件,通知父组件输入结果
|
||||
this.$emit('input', value.trim());
|
||||
this.$emit('input', v);
|
||||
} else {
|
||||
this.$emit('input', '');
|
||||
}
|
||||
},
|
||||
formatValue(value) {
|
||||
const v = value === null || value === undefined ? '' : String(value);
|
||||
if (!v.trim()) return '';
|
||||
if (typeof this.inputFormatter === 'function') {
|
||||
return this.inputFormatter(v);
|
||||
}
|
||||
return v.trim();
|
||||
},
|
||||
|
||||
/**
|
||||
* 缓存输入值到localStorage
|
||||
@@ -190,4 +210,4 @@ export default {
|
||||
.inline-input {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<span class="top-menu-title">{{ (menu.meta && menu.meta.title) || menu.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="reference" class="sidebar-section-title" :class="{ 'is-collapse': isCollapse }">
|
||||
<div ref="topMenuTrigger" slot="reference" class="sidebar-section-title" :class="{ 'is-collapse': isCollapse }" tabindex="0">
|
||||
<div class="section-title-inner">
|
||||
<div class="section-icon">
|
||||
<svg-icon v-if="activeTopMenuIcon" :icon-class="activeTopMenuIcon" />
|
||||
@@ -141,6 +141,11 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
sectionPopoverVisible: {
|
||||
handler(val) {
|
||||
if (!val) this.releasePopoverFocus()
|
||||
}
|
||||
},
|
||||
$route: {
|
||||
immediate: true,
|
||||
handler(newRoute) {
|
||||
@@ -165,8 +170,16 @@ export default {
|
||||
this.$router.push('/')
|
||||
this.$nextTick(() => {
|
||||
this.sectionPopoverVisible = false
|
||||
this.releasePopoverFocus()
|
||||
})
|
||||
},
|
||||
releasePopoverFocus() {
|
||||
if (typeof document === 'undefined') return
|
||||
const el = document.activeElement
|
||||
if (el && typeof el.blur === 'function') el.blur()
|
||||
const trigger = this.$refs && this.$refs.topMenuTrigger
|
||||
if (trigger && typeof trigger.focus === 'function') trigger.focus()
|
||||
},
|
||||
autoSelectTopMenu(currentPath) {
|
||||
const topMenus = this.$store.state.permission.topMenuList
|
||||
if (topMenus.length === 0) return
|
||||
|
||||
@@ -12,4 +12,16 @@ export const MemoInputStorageKey = {
|
||||
packagingRequirement: 'packagingRequirement', // 打包要求
|
||||
coilSpec: 'coilSpec', // 钢卷规格
|
||||
coatingType: 'coatingType', // 钢卷镀层种类
|
||||
}
|
||||
transferNo: 'transferNo', // 调拨单号
|
||||
transferName: 'transferName', // 调拨单名称
|
||||
maintenanceRepairContent: 'maintenanceRepairContent', // 维修明细-具体工作内容
|
||||
maintenanceLineExecutor: 'maintenanceLineExecutor', // 维修明细-产线执行人员
|
||||
maintenanceEquipmentExecutor: 'maintenanceEquipmentExecutor', // 维修明细-设备执行人员
|
||||
maintenancePlannedCompleteTime: 'maintenancePlannedCompleteTime', // 维修明细-预计完成时间
|
||||
maintenanceActualCompleteTime: 'maintenanceActualCompleteTime', // 维修明细-实际完成时间
|
||||
maintenancePlannedStartTime: 'maintenancePlannedStartTime', // 维修明细-预计开始时间
|
||||
maintenanceActualStartTime: 'maintenanceActualStartTime', // 维修明细-实际开始时间
|
||||
maintenanceTotalHours: 'maintenanceTotalHours', // 维修明细-总耗时
|
||||
maintenanceAcceptanceUser: 'maintenanceAcceptanceUser', // 维修明细-验收人
|
||||
maintenanceRemark: 'maintenanceRemark', // 维修明细-备注
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<el-table-column label="逻辑库位" align="center" prop="warehouseName" />
|
||||
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
|
||||
<el-table-column label="物料类型" align="center" prop="materialType" />
|
||||
<el-table-column label="产品类型" align="center" width="180">
|
||||
<el-table-column label="产品类型" align="center" width="140">
|
||||
<template slot-scope="scope">
|
||||
<ProductInfo v-if="scope.row.itemType == 'product'" :product="scope.row" />
|
||||
<RawMaterialInfo v-else-if="scope.row.itemType === 'raw_material'" :material="scope.row" />
|
||||
|
||||
@@ -1,51 +1,91 @@
|
||||
<template>
|
||||
<div class="contract-tabs">
|
||||
<div v-if="orderId" class="tabs-content">
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<el-tab-pane label="财务状态" name="finance" v-hasPermi="['crm:order:finance']">
|
||||
<div class="order-finance" v-if="activeTab === 'finance'">
|
||||
<!-- 财务状态内容 -->
|
||||
<ReceiveTable :order="currentOrder" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!-- 自定义Tab头部 -->
|
||||
<div class="tab-header">
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'contract' }"
|
||||
@click="activeTab = 'contract'"
|
||||
>合同信息</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'finance' }"
|
||||
@click="activeTab = 'finance'"
|
||||
>财务状态</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'dispute' }"
|
||||
@click="activeTab = 'dispute'"
|
||||
>订单异议</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'product' }"
|
||||
@click="activeTab = 'product'"
|
||||
>生产成果</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'coil' }"
|
||||
@click="activeTab = 'coil'"
|
||||
>发货配卷</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'delivery' }"
|
||||
@click="activeTab = 'delivery'"
|
||||
>发货单据</span>
|
||||
<span
|
||||
class="tab-item"
|
||||
:class="{ active: activeTab === 'attachment' }"
|
||||
@click="activeTab = 'attachment'"
|
||||
>合同附件</span>
|
||||
</div>
|
||||
|
||||
<el-tab-pane label="订单异议" name="dispute" v-hasPermi="['crm:order:objection']">
|
||||
<div class="order-dispute" v-if="activeTab === 'dispute'">
|
||||
<!-- 订单异议内容 -->
|
||||
<OrderObjection :order="currentOrder" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="生产成果" name="product">
|
||||
<div class="order-record" v-if="activeTab === 'product'" v-loading="productCoilLoading">
|
||||
<!-- 生产成果内容 -->
|
||||
<CoilTable ref="productCoilTable" :data="productCoilList || []" :showSelection="true"
|
||||
:pagination="productPagination" :total-statistics="productTotalStatistics"
|
||||
@selection-change="handleProductSelectionChange"
|
||||
@page-change="handleProductPageChange">
|
||||
<template slot="filter-actions" slot-scope="{ selectedRows }">
|
||||
<el-button type="primary" size="mini" icon="el-icon-refresh"
|
||||
:disabled="!selectedRows.length" @click="handleBatchTransferContract(selectedRows)">
|
||||
批量转单
|
||||
</el-button>
|
||||
</template>
|
||||
</CoilTable>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="发货配卷" name="coil">
|
||||
<div class="order-record" v-if="activeTab === 'coil'" v-loading="deliveryCoilLoading">
|
||||
<!-- 发货配卷内容 -->
|
||||
<CoilTable :data="deliveryCoilList || []"
|
||||
:pagination="deliveryPagination" :total-statistics="deliveryTotalStatistics"
|
||||
@page-change="handleDeliveryPageChange" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="发货单据" name="delivery">
|
||||
<div class="order-record" v-if="activeTab === 'delivery'">
|
||||
<!-- 发货单内容 -->
|
||||
<DeliveryTable :data="deliveryWaybillList || []" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="合同附件" name="attachment" v-hasPermi="['crm:order:record']">
|
||||
<!-- Tab内容区域(可滚动) -->
|
||||
<div class="tab-body">
|
||||
<!-- 合同信息 -->
|
||||
<div v-show="activeTab === 'contract'">
|
||||
<ContractPreview :contract="currentOrder" @updateStatus="$emit('updateStatus', $event)" />
|
||||
</div>
|
||||
|
||||
<!-- 财务状态 -->
|
||||
<div v-show="activeTab === 'finance'">
|
||||
<ReceiveTable :order="currentOrder" />
|
||||
</div>
|
||||
|
||||
<!-- 订单异议 -->
|
||||
<div v-show="activeTab === 'dispute'">
|
||||
<OrderObjection :order="currentOrder" />
|
||||
</div>
|
||||
|
||||
<!-- 生产成果 -->
|
||||
<div v-show="activeTab === 'product'" v-loading="productCoilLoading">
|
||||
<CoilTable ref="productCoilTable" :data="productCoilList || []" :showSelection="true"
|
||||
:pagination="productPagination" :total-statistics="productTotalStatistics"
|
||||
@selection-change="handleProductSelectionChange"
|
||||
@page-change="handleProductPageChange">
|
||||
<template slot="filter-actions" slot-scope="{ selectedRows }">
|
||||
<el-button type="primary" size="mini" icon="el-icon-refresh"
|
||||
:disabled="!selectedRows.length" @click="handleBatchTransferContract(selectedRows)">
|
||||
批量转单
|
||||
</el-button>
|
||||
</template>
|
||||
</CoilTable>
|
||||
</div>
|
||||
|
||||
<!-- 发货配卷 -->
|
||||
<div v-show="activeTab === 'coil'" v-loading="deliveryCoilLoading">
|
||||
<CoilTable :data="deliveryCoilList || []"
|
||||
:pagination="deliveryPagination" :total-statistics="deliveryTotalStatistics"
|
||||
@page-change="handleDeliveryPageChange" />
|
||||
</div>
|
||||
|
||||
<!-- 发货单据 -->
|
||||
<div v-show="activeTab === 'delivery'">
|
||||
<DeliveryTable :data="deliveryWaybillList || []" />
|
||||
</div>
|
||||
|
||||
<!-- 合同附件 -->
|
||||
<div v-show="activeTab === 'attachment'">
|
||||
<div class="attachment-item">
|
||||
<h4>商务附件</h4>
|
||||
<FileList :oss-ids="contractAttachment" />
|
||||
@@ -62,8 +102,8 @@
|
||||
<h4>其他附件</h4>
|
||||
<FileList :oss-ids="form.annexFiles" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 批量转单 - 选择目标合同弹窗 -->
|
||||
<el-dialog title="批量转单" :visible.sync="batchTransferDialogVisible" width="500px" append-to-body
|
||||
@@ -79,7 +119,7 @@
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div v-else class="no-selection" style="display: flex; align-items: center; justify-content: center; height: 100%;">
|
||||
<div v-else class="no-selection">
|
||||
<el-empty description="请先选择合同" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,6 +131,7 @@ import ContractSelect from "@/components/KLPService/ContractSelect";
|
||||
import { batchUpdateContractCoil } from "@/api/wms/coil";
|
||||
import FileList from "@/components/FileList";
|
||||
import DeliveryTable from "../../components/DeliveryTable.vue";
|
||||
import ContractPreview from "./ContractPreview.vue";
|
||||
|
||||
// 导入可能需要的组件
|
||||
import OrderDetail from "../../components/OrderDetail.vue";
|
||||
@@ -108,6 +149,7 @@ export default {
|
||||
ContractSelect,
|
||||
FileList,
|
||||
DeliveryTable,
|
||||
ContractPreview,
|
||||
OrderDetail,
|
||||
OrderEdit,
|
||||
ReceiveTable,
|
||||
@@ -153,7 +195,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
// 活动tab
|
||||
activeTab: "finance",
|
||||
activeTab: "contract",
|
||||
selectedProductRows: [],
|
||||
batchTransferDialogVisible: false,
|
||||
batchTargetContractId: '',
|
||||
@@ -304,4 +346,57 @@ export default {
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contract-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
border-bottom: 2px solid #e4e7ed;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
transition: color .3s, border-color .3s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.tab-item:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
color: #409eff;
|
||||
border-bottom-color: #409eff;
|
||||
}
|
||||
|
||||
.tab-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.no-selection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,24 +11,11 @@
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="right-panel" v-if="form.orderId" style="flex: 1; display: flex; flex-direction: column;">
|
||||
<!-- 右侧上方:合同内容信息预览 -->
|
||||
<div class="preview-panel" ref="previewPanel"
|
||||
style="flex: 1; overflow-y: auto; border-bottom: 1px solid #e4e7ed;">
|
||||
<ContractPreview :contract="form" @updateStatus="handleStatusChange" />
|
||||
</div>
|
||||
|
||||
<!-- 拖拽调整条 -->
|
||||
<div class="resize-handle" ref="resizeHandle"
|
||||
style="height: 8px; background-color: #f5f7fa; cursor: row-resize; display: flex; align-items: center; justify-content: center;"
|
||||
@mousedown="startResize">
|
||||
<div style="width: 40px; height: 2px; background-color: #dcdfe6;"></div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧下方:Tab标签页 -->
|
||||
<div class="tab-panel" ref="tabPanel" style="flex: 1; overflow-y: auto;">
|
||||
<div class="tab-panel" style="flex: 1; overflow: hidden;">
|
||||
<ContractTabs :orderId="form.orderId" :deliveryWaybillList="wmsDeliveryWaybills"
|
||||
:contract-attachment="form.businessAnnex" :technical-agreement="form.techAnnex"
|
||||
:other-attachment="form.productionSchedule" :currentOrder="form" />
|
||||
:other-attachment="form.productionSchedule" :currentOrder="form"
|
||||
@updateStatus="handleStatusChange" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else style="flex: 1; display: flex; flex-direction: column;">
|
||||
@@ -234,7 +221,6 @@ import { addCustomer } from "@/api/crm/customer";
|
||||
import { listDeliveryWaybill } from "@/api/wms/deliveryWaybill";
|
||||
import dayjs from "dayjs";
|
||||
import ContractList from "./components/ContractList.vue";
|
||||
import ContractPreview from "./components/ContractPreview.vue";
|
||||
import ContractTabs from "./components/ContractTabs.vue";
|
||||
import ContractExportDialog from "./components/ContractExportDialog.vue";
|
||||
import ProductContent from "./components/ProductContent.vue";
|
||||
@@ -246,7 +232,6 @@ export default {
|
||||
name: "Contract",
|
||||
components: {
|
||||
ContractList,
|
||||
ContractPreview,
|
||||
ContractTabs,
|
||||
ContractExportDialog,
|
||||
ProductContent,
|
||||
@@ -706,34 +691,6 @@ export default {
|
||||
this.customerOpen = false;
|
||||
this.customerForm = {};
|
||||
},
|
||||
/** 开始拖拽调整 */
|
||||
startResize(e) {
|
||||
e.preventDefault();
|
||||
const previewPanel = this.$refs.previewPanel;
|
||||
const tabPanel = this.$refs.tabPanel;
|
||||
const startY = e.clientY;
|
||||
const startPreviewHeight = previewPanel.offsetHeight;
|
||||
const parentHeight = previewPanel.parentElement.offsetHeight;
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
const deltaY = event.clientY - startY;
|
||||
const newPreviewHeight = startPreviewHeight + deltaY;
|
||||
const newTabHeight = parentHeight - newPreviewHeight - 8; // 8是拖拽条的高度
|
||||
|
||||
if (newPreviewHeight > 100 && newTabHeight > 100) { // 最小高度限制
|
||||
previewPanel.style.flex = `0 0 ${newPreviewHeight}px`;
|
||||
tabPanel.style.flex = `0 0 ${newTabHeight}px`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -756,11 +713,7 @@ export default {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-panel {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tab-panel {
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -15,6 +15,7 @@
|
||||
<el-option label="采购处理中" value="3" />
|
||||
<el-option label="已完成" value="4" />
|
||||
<el-option label="已驳回" value="5" />
|
||||
<el-option label="一审通过待二审" value="6" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -62,11 +63,12 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="160">
|
||||
<template slot-scope="scope">
|
||||
<div style="display:flex;flex-wrap:wrap;">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)" v-hasPermi="['erp:purchaseRequisition:query']" style="width:50%;margin:0;text-align:center">查看</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)" v-if="canView" style="width:50%;margin:0;text-align:center">查看</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-if="scope.row.formStatus === '0'" v-hasPermi="['erp:purchaseRequisition:edit']" style="width:50%;margin:0;text-align:center">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-if="scope.row.formStatus === '0'" v-hasPermi="['erp:purchaseRequisition:remove']" style="width:50%;margin:0;text-align:center">删除</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-upload2" @click="handleSubmitOne(scope.row)" v-if="scope.row.formStatus === '0'" v-hasPermi="['erp:purchaseRequisition:approve']" style="width:50%;margin:0;text-align:center">提交</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-circle-check" style="color:#67c23a;width:50%;margin:0;text-align:center" @click="openApproval(scope.row)" v-if="scope.row.formStatus === '1'" v-hasPermi="['erp:purchaseRequisition:approve']">审批</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-circle-check" style="color:#67c23a;width:50%;margin:0;text-align:center" @click="openApproval(scope.row)" v-if="scope.row.formStatus === '1'" v-hasRole="['glc']">一审审批</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-circle-check" style="color:#67c23a;width:50%;margin:0;text-align:center" @click="openApproval(scope.row)" v-if="scope.row.formStatus === '6' && currentNickName === '陈清鑫'">二审审批</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -225,6 +227,7 @@
|
||||
<el-select v-model="form.formStatus" size="mini" style="width:100%">
|
||||
<el-option label="请购草稿" value="0" />
|
||||
<el-option label="审批中" value="1" />
|
||||
<el-option label="一审通过待二审" value="6" />
|
||||
<el-option label="已通过" value="2" />
|
||||
<el-option label="采购处理中" value="3" />
|
||||
<el-option label="已完成" value="4" />
|
||||
@@ -357,9 +360,23 @@
|
||||
<div class="pf-td pf-val pf-val-read">{{ viewForm.remark || '—' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审批进度 -->
|
||||
<div class="pv-section-title" style="margin-top:14px">审批进度</div>
|
||||
<div style="padding:8px 0;font-size:13px">
|
||||
<div style="margin-bottom:10px">
|
||||
<div><label style="color:#909399">一审人:</label><span>{{ viewForm.signPurchaseHandler || '待审批' }}</span></div>
|
||||
<div v-if="viewForm.firstApprovalOpinion" style="margin:4px 0 0 62px;color:#606266;font-size:12px;padding:4px 10px;background:#f5f7fa;border-radius:3px;display:inline-block">意见:{{ viewForm.firstApprovalOpinion }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div><label style="color:#909399">二审人(陈清鑫):</label><span>{{ viewForm.signRequestGm || '待审批' }}</span></div>
|
||||
<div v-if="viewForm.secondApprovalOpinion" style="margin:4px 0 0 124px;color:#606266;font-size:12px;padding:4px 10px;background:#f5f7fa;border-radius:3px;display:inline-block">意见:{{ viewForm.secondApprovalOpinion }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button icon="el-icon-circle-check" style="color:#67c23a;float:left" @click="openApproval(viewForm)" v-if="viewForm.formStatus === '1'" v-hasPermi="['erp:purchaseRequisition:approve']">审批</el-button>
|
||||
<el-button icon="el-icon-circle-check" style="color:#67c23a;float:left" @click="openApproval(viewForm)" v-if="viewForm.formStatus === '1'" v-hasRole="['glc']">一审审批</el-button>
|
||||
<el-button icon="el-icon-circle-check" style="color:#67c23a;float:left" @click="openApproval(viewForm)" v-else-if="viewForm.formStatus === '6' && currentNickName === '陈清鑫'">二审审批</el-button>
|
||||
<el-button type="primary" icon="el-icon-download" :loading="printing" @click="exportPdf">导出PDF</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
@@ -395,6 +412,26 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 提交审批 — 选择一审人 -->
|
||||
<el-dialog title="提交审批" :visible.sync="submitDialogVisible" width="480px" append-to-body :close-on-click-modal="false">
|
||||
<div style="padding: 12px 8px">
|
||||
<el-form size="small" label-width="100px">
|
||||
<el-form-item label="选择一审审批人">
|
||||
<el-select v-model="selectedApproverId" placeholder="请选择一审审批人" style="width:100%" clearable filterable>
|
||||
<el-option v-for="u in approverUsers" :key="u.userId" :label="u.nickName" :value="u.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div style="color:#909399;font-size:12px;margin-top:-6px;padding-left:100px">
|
||||
请选择管理层审批人进行一审
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="submitDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="doSubmitApproval" :disabled="!selectedApproverId">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- A4 打印模板(屏幕外渲染,供 html2canvas 截图) -->
|
||||
<div class="pr-print-wrap" v-show="printing">
|
||||
<div class="pr-print" ref="printTemplate">
|
||||
@@ -554,7 +591,8 @@ import {
|
||||
delPurchaseRequisition,
|
||||
submitApproval,
|
||||
approvePurchase,
|
||||
rejectPurchase
|
||||
rejectPurchase,
|
||||
selectApproverUsers
|
||||
} from '@/api/erp/purchaseRequisition'
|
||||
|
||||
export default {
|
||||
@@ -589,9 +627,30 @@ export default {
|
||||
approvalReq: {},
|
||||
approvalRemark: '',
|
||||
approvalLoading: false,
|
||||
// 提交审批选人
|
||||
submitDialogVisible: false,
|
||||
submitLoading: false,
|
||||
approverUsers: [],
|
||||
selectedApproverId: null,
|
||||
submittingReqId: null,
|
||||
rules: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentNickName() {
|
||||
return this.$store.getters.nickName || ''
|
||||
},
|
||||
canView() {
|
||||
const permissions = this.$store.getters.permissions || []
|
||||
const roles = this.$store.getters.roles || []
|
||||
const nickName = this.$store.getters.nickName || ''
|
||||
// 有查看权限 或 有glc角色 或是陈清鑫
|
||||
if (permissions.some(p => p === '*:*:*' || p === 'erp:purchaseRequisition:query')) return true
|
||||
if (roles.some(r => r === 'admin' || r === 'glc')) return true
|
||||
if (nickName === '陈清鑫') return true
|
||||
return false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
},
|
||||
@@ -674,14 +733,35 @@ export default {
|
||||
this.viewForm = res.data || {}
|
||||
})
|
||||
},
|
||||
// 提交审批(单个操作列)
|
||||
// 提交审批 — 打开选人弹窗
|
||||
handleSubmitOne(row) {
|
||||
this.$modal.confirm('确认提交该请购单进行审批?').then(() => {
|
||||
return submitApproval(row.reqId)
|
||||
}).then(() => {
|
||||
this.submittingReqId = row.reqId
|
||||
this.selectedApproverId = null
|
||||
this.submitLoading = true
|
||||
this.submitDialogVisible = true
|
||||
// 加载管理层审批人(角色标识由后端 erp.approver.role-key 配置)
|
||||
selectApproverUsers().then(res => {
|
||||
this.approverUsers = res.data || []
|
||||
}).catch(() => {
|
||||
this.approverUsers = []
|
||||
}).finally(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
},
|
||||
// 确认提交审批
|
||||
doSubmitApproval() {
|
||||
if (!this.selectedApproverId) return
|
||||
this.submitLoading = true
|
||||
// 获取选中的审批人昵称
|
||||
const user = this.approverUsers.find(u => u.userId === this.selectedApproverId)
|
||||
const nickName = user ? user.nickName : ''
|
||||
submitApproval(this.submittingReqId, this.selectedApproverId, nickName).then(() => {
|
||||
this.$modal.msgSuccess('已提交审批')
|
||||
this.submitDialogVisible = false
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
}).catch(() => {}).finally(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
},
|
||||
// 打开审批弹窗
|
||||
openApproval(row) {
|
||||
@@ -692,7 +772,7 @@ export default {
|
||||
// 审批通过
|
||||
doApprove() {
|
||||
this.approvalLoading = true
|
||||
approvePurchase(this.approvalReq.reqId).then(() => {
|
||||
approvePurchase(this.approvalReq.reqId, this.approvalRemark).then(() => {
|
||||
this.$modal.msgSuccess('审批通过')
|
||||
this.approvalOpen = false
|
||||
this.getList()
|
||||
@@ -701,7 +781,7 @@ export default {
|
||||
// 驳回
|
||||
doReject() {
|
||||
this.approvalLoading = true
|
||||
rejectPurchase(this.approvalReq.reqId).then(() => {
|
||||
rejectPurchase(this.approvalReq.reqId, this.approvalRemark).then(() => {
|
||||
this.$modal.msgSuccess('已驳回')
|
||||
this.approvalOpen = false
|
||||
this.getList()
|
||||
@@ -804,11 +884,11 @@ export default {
|
||||
},
|
||||
// 状态
|
||||
statusText(s) {
|
||||
const map = { '0': '请购草稿', '1': '审批中', '2': '已通过', '3': '采购处理中', '4': '已完成', '5': '已驳回' }
|
||||
const map = { '0': '请购草稿', '1': '审批中', '6': '一审通过待二审', '2': '已通过', '3': '采购处理中', '4': '已完成', '5': '已驳回' }
|
||||
return map[s] || s || '—'
|
||||
},
|
||||
statusType(s) {
|
||||
const map = { '0': 'info', '1': 'warning', '2': 'success', '3': 'warning', '4': 'success', '5': 'danger' }
|
||||
const map = { '0': 'info', '1': 'warning', '6': 'primary', '2': 'success', '3': 'warning', '4': 'success', '5': 'danger' }
|
||||
return map[s] || 'info'
|
||||
},
|
||||
// 品检条件 — 数字代码 -> 可读描述(查看弹窗用)
|
||||
@@ -1047,6 +1127,7 @@ $sub: #909399;
|
||||
&.s3 { color: #d6a256; border-color: #ecd4a6; background: #fdf6ec; }
|
||||
&.s4 { color: $accent; border-color: #b9d2e6; background: #eef3f8; }
|
||||
&.s5 { color: #c45656; border-color: #e6c4c4; background: #fbf0f0; }
|
||||
&.s6 { color: #5b8db8; border-color: #b9d2e6; background: #eef3f8; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<statistic-group />
|
||||
<quick-access-group />
|
||||
</el-col>
|
||||
|
||||
<!-- 右栏 -->
|
||||
@@ -37,10 +37,6 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- <div>
|
||||
<statistic-group />
|
||||
</div> -->
|
||||
|
||||
<!-- <AllApplications />
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="18">
|
||||
@@ -54,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatisticGroup from '@/components/HomeModules/StatisticGroup.vue'
|
||||
import QuickAccessGroup from '@/components/HomeModules/QuickAccessGroup.vue'
|
||||
// import AllApplications from '@/components/HomeModules/AllApplications.vue'
|
||||
// import MiniCalendar from '@/components/HomeModules/MiniCalendar.vue'
|
||||
|
||||
@@ -62,7 +58,7 @@ export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
// PanelGroup,
|
||||
StatisticGroup,
|
||||
QuickAccessGroup,
|
||||
// AllApplications,
|
||||
// MiniCalendar,
|
||||
},
|
||||
@@ -193,4 +189,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
<script>
|
||||
import { listInspectionMain, addInspectionMain, updateInspectionMain, delInspectionMain } from '@/api/mes/qc/inspectionMain'
|
||||
import { listInspectionDetail, addInspectionDetail, updateInspectionDetail } from '@/api/mes/qc/inspectionDetail'
|
||||
import { listInspectionDetail } from '@/api/mes/qc/inspectionDetail'
|
||||
import { listInspectionItemTemplate, getInfoByInspectionItem } from '@/api/mes/qc/inspectionItemTemplate'
|
||||
|
||||
export default {
|
||||
@@ -344,27 +344,24 @@ export default {
|
||||
|
||||
loadMainDetails() {
|
||||
if (this.mainList.length === 0) return
|
||||
const promises = this.mainList.map(main => {
|
||||
return listInspectionDetail({ mainId: main.mainId, pageNum: 1, pageSize: 9999 }).then(response => {
|
||||
const details = response.rows || []
|
||||
details.forEach(detail => {
|
||||
if (main._items[detail.itemName]) {
|
||||
main._items[detail.itemName] = {
|
||||
detailId: detail.detailId,
|
||||
mainId: detail.mainId,
|
||||
itemName: detail.itemName,
|
||||
itemValue: detail.itemValue || '',
|
||||
itemUnit: detail.itemUnit || main._items[detail.itemName].itemUnit,
|
||||
upperLimit: detail.upperLimit,
|
||||
lowerLimit: detail.lowerLimit,
|
||||
rangeDesc: detail.rangeDesc
|
||||
}
|
||||
this.mainList.forEach(main => {
|
||||
const details = main.detailList || []
|
||||
details.forEach(detail => {
|
||||
if (main._items[detail.itemName]) {
|
||||
main._items[detail.itemName] = {
|
||||
detailId: detail.detailId,
|
||||
mainId: detail.mainId,
|
||||
itemName: detail.itemName,
|
||||
itemValue: detail.itemValue || '',
|
||||
itemUnit: detail.itemUnit || main._items[detail.itemName].itemUnit,
|
||||
upperLimit: detail.upperLimit,
|
||||
lowerLimit: detail.lowerLimit,
|
||||
rangeDesc: detail.rangeDesc
|
||||
}
|
||||
})
|
||||
main._originalItems = JSON.parse(JSON.stringify(main._items))
|
||||
}).catch(() => {})
|
||||
}
|
||||
})
|
||||
main._originalItems = JSON.parse(JSON.stringify(main._items))
|
||||
})
|
||||
return Promise.all(promises)
|
||||
},
|
||||
|
||||
handleAddMain() {
|
||||
@@ -394,36 +391,28 @@ export default {
|
||||
|
||||
handleSaveMain(row) {
|
||||
row._saving = true
|
||||
const detailList = this.checkItems.map(checkItem => {
|
||||
const itemData = row._items[checkItem.itemName]
|
||||
if (!itemData || (!itemData.itemValue && !itemData.detailId)) return null
|
||||
return {
|
||||
detailId: itemData.detailId,
|
||||
mainId: row.mainId,
|
||||
itemName: itemData.itemName,
|
||||
itemValue: itemData.itemValue || '',
|
||||
itemUnit: itemData.itemUnit || '',
|
||||
upperLimit: itemData.upperLimit,
|
||||
lowerLimit: itemData.lowerLimit,
|
||||
rangeDesc: itemData.rangeDesc || ''
|
||||
}
|
||||
}).filter(Boolean)
|
||||
updateInspectionMain({
|
||||
mainId: row.mainId,
|
||||
sampleName: row.sampleName,
|
||||
sampleNo: row.sampleNo,
|
||||
batchNo: row.batchNo,
|
||||
inspectionDate: row.inspectionDate,
|
||||
remark: row.remark
|
||||
}).then(() => {
|
||||
const items = this.checkItems.map(checkItem => {
|
||||
const itemData = row._items[checkItem.itemName]
|
||||
return itemData || { itemName: checkItem.itemName }
|
||||
})
|
||||
const promises = items.map(item => {
|
||||
const data = {
|
||||
mainId: row.mainId,
|
||||
itemName: item.itemName,
|
||||
itemValue: item.itemValue,
|
||||
itemUnit: item.itemUnit,
|
||||
upperLimit: item.upperLimit,
|
||||
lowerLimit: item.lowerLimit
|
||||
}
|
||||
if (item.detailId) {
|
||||
data.detailId = item.detailId
|
||||
return updateInspectionDetail(data)
|
||||
} else if (item.itemValue) {
|
||||
return addInspectionDetail(data)
|
||||
}
|
||||
return Promise.resolve()
|
||||
})
|
||||
return Promise.all(promises)
|
||||
remark: row.remark,
|
||||
detailList: detailList
|
||||
}).then(() => {
|
||||
this.$message.success('保存成功')
|
||||
return listInspectionDetail({ mainId: row.mainId, pageNum: 1, pageSize: 9999 })
|
||||
|
||||
@@ -519,7 +519,7 @@
|
||||
|
||||
<script>
|
||||
import { listInspectionTensile, getInspectionTensile, addInspectionTensile, updateInspectionTensile } from '@/api/mes/qc/inspectionTensile'
|
||||
import { listInspectionTensileDetail, addInspectionTensileDetail, updateInspectionTensileDetail, delInspectionTensileDetail } from '@/api/mes/qc/inspectionTensileDetail'
|
||||
import { listInspectionTensileDetail, addInspectionTensileDetail, updateInspectionTensileDetail, delInspectionTensileDetail, batchInsertTensileDetails } from '@/api/mes/qc/inspectionTensileDetail'
|
||||
import { listMaterialCoil } from '@/api/wms/coil'
|
||||
import { listChemicalItem } from '@/api/mes/qc/chemicalItem'
|
||||
import { listPhysicalItem } from '@/api/mes/qc/physicalItem'
|
||||
@@ -765,16 +765,12 @@ export default {
|
||||
this.$message.warning('请选择钢卷')
|
||||
return
|
||||
}
|
||||
|
||||
let successCount = 0
|
||||
let failCount = 0
|
||||
this.detailLoading = true
|
||||
|
||||
for (let index = 0; index < coils.length; index++) {
|
||||
const coil = coils[index]
|
||||
const detailList = []
|
||||
for (const coil of coils) {
|
||||
try {
|
||||
const data = await this.fetchLatestChemAndPhys(coil.enterCoilNo)
|
||||
addInspectionTensileDetail({
|
||||
detailList.push({
|
||||
testMainId: this.currentTestId,
|
||||
steelCoilNo: coil.currentCoilNo,
|
||||
material: coil.material,
|
||||
@@ -782,21 +778,27 @@ export default {
|
||||
rawMaterialNo: coil.enterCoilNo,
|
||||
...data
|
||||
})
|
||||
successCount++
|
||||
} catch {
|
||||
failCount++
|
||||
// skip failed coil
|
||||
}
|
||||
}
|
||||
|
||||
this.batchAddDialogVisible = false
|
||||
if (failCount === 0) {
|
||||
this.$message.success(`批量新增成功,共新增 ${successCount} 条明细`)
|
||||
} else if (successCount === 0) {
|
||||
this.$message.error('批量新增失败')
|
||||
} else {
|
||||
this.$message.warning(`批量新增完成,成功 ${successCount} 条,失败 ${failCount} 条`)
|
||||
if (detailList.length === 0) {
|
||||
this.detailLoading = false
|
||||
this.$message.error('批量新增失败,请稍后重试')
|
||||
return
|
||||
}
|
||||
this.getDetailList()
|
||||
updateInspectionTensile({
|
||||
testId: this.currentTestId,
|
||||
detailList: detailList
|
||||
}).then(() => {
|
||||
this.$message.success(`批量新增成功,共新增 ${detailList.length} 条明细`)
|
||||
}).catch(() => {
|
||||
this.$message.error('批量新增失败')
|
||||
}).finally(() => {
|
||||
this.batchAddDialogVisible = false
|
||||
this.detailLoading = false
|
||||
this.getDetailList()
|
||||
})
|
||||
},
|
||||
|
||||
// 保存明细
|
||||
|
||||
@@ -411,7 +411,7 @@ import { listWarehouse } from '@/api/wms/warehouse'
|
||||
export default {
|
||||
name: 'QualityReview',
|
||||
components: { CoilSelector },
|
||||
dicts: ['quality_review_status', 'regrade_quality_type', 'coil_quality_status'],
|
||||
dicts: ['quality_review_status', 'coil_quality_status'],
|
||||
data() {
|
||||
return {
|
||||
// 列表
|
||||
|
||||
@@ -93,7 +93,7 @@ import { listQualityReview, approveQualityReview, rejectQualityReview, getQualit
|
||||
|
||||
export default {
|
||||
name: 'QualityReviewTodo',
|
||||
dicts: ['quality_review_status', 'regrade_quality_type', 'coil_quality_status'],
|
||||
dicts: ['quality_review_status', 'coil_quality_status'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -83,7 +83,10 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="业务员" prop="saleName">
|
||||
<el-input v-model="queryParams.saleName" placeholder="请输入业务员" clearable size="small" />
|
||||
<el-select v-model="queryParams.saleName" placeholder="请选择业务员" clearable size="small" filterable allow-create>
|
||||
<el-option v-for="item in dict.type.wip_pack_saleman" :key="item.value" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="合同号" prop="contractNo">
|
||||
@@ -997,7 +1000,7 @@ export default {
|
||||
ContractSelect,
|
||||
ExceptionManager,
|
||||
},
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status', 'wms_next_warehouse', 'coil_business_purpose'],
|
||||
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer', 'coil_quality_status', 'wms_next_warehouse', 'coil_business_purpose', 'wip_pack_saleman'],
|
||||
props: {
|
||||
qrcode: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -497,7 +497,11 @@ export default {
|
||||
loadDialogData() {
|
||||
if (!this.currentWarehouse) return;
|
||||
this.dialogLoading = true;
|
||||
const baseParams = { warehouseId: this.currentWarehouse.warehouseId, dataType: 1, status: 0 };
|
||||
const baseParams = {
|
||||
warehouseId: this.currentWarehouse.warehouseId,
|
||||
qualityStatusCsv: this.queryParams.qualityStatusCsv || '',
|
||||
dataType: 1, status: 0,
|
||||
};
|
||||
Promise.all([
|
||||
listLightCoil({ ...baseParams, pageSize: 99999, pageNum: 1 }),
|
||||
listMaterialCoil({ ...baseParams, pageNum: this.dialogPageNum, pageSize: this.dialogPageSize }),
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="调拨单号" prop="transferNo">
|
||||
<el-input v-model="queryParams.transferNo" placeholder="请输入调拨单号" clearable @keyup.enter.native="handleQuery" />
|
||||
<MemoInput storageKey="transferNo" v-model="queryParams.transferNo" placeholder="请输入调拨单号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨单名称" prop="transferName">
|
||||
<el-input v-model="queryParams.transferName" placeholder="请输入调拨单名称,用于帮助记忆与沟通" clearable
|
||||
@keyup.enter.native="handleQuery" />
|
||||
<MemoInput storageKey="transferName" v-model="queryParams.transferName" placeholder="请输入调拨单名称,用于帮助记忆与沟通" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨时间" prop="transferTime">
|
||||
<el-date-picker clearable v-model="queryParams.transferTime" type="date" value-format="yyyy-MM-dd"
|
||||
@@ -41,7 +40,7 @@
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple"
|
||||
@click="handleDelete">删除</el-button>
|
||||
v-hasRole="['glc']" @click="handleDelete">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
||||
@@ -80,8 +79,8 @@
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)">查看</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
|
||||
:disabled="!canEdit(scope.row)">修改</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
|
||||
:disabled="!canDelete(scope.row)">删除</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" v-hasRole="['glc']"
|
||||
@click="handleDelete(scope.row)">删除</el-button>
|
||||
<el-button size="mini" plain type="text" @click="handleSubmitapprove(scope.row)"
|
||||
v-if="(scope.row.approveStatus || '0') == '0'" icon="el-icon-check">提交</el-button>
|
||||
<el-button size="mini" plain type="text" @click="handleWithdrawapprove(scope.row)"
|
||||
@@ -103,10 +102,10 @@
|
||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="调拨单号" prop="transferNo">
|
||||
<el-input v-model="form.transferNo" placeholder="请输入调拨单号" />
|
||||
<MemoInput storageKey="transferNo" v-model="form.transferNo" placeholder="请输入调拨单号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨单名称" prop="transferName">
|
||||
<el-input v-model="form.transferName" placeholder="请输入调拨单名称,用于帮助记忆与沟通" />
|
||||
<MemoInput storageKey="transferName" v-model="form.transferName" placeholder="请输入调拨单名称,用于帮助记忆与沟通" />
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨时间" prop="transferTime">
|
||||
<el-date-picker clearable v-model="form.transferTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@@ -297,12 +296,6 @@ export default {
|
||||
const status = row.approveStatus || '0';
|
||||
return status === '0' || status === '1' || status === '3';
|
||||
},
|
||||
/** 判断是否可以删除 */
|
||||
canDelete(row) {
|
||||
// 如果没有状态字段,则视为未提交
|
||||
const status = row.approveStatus || '0';
|
||||
return status === '0';
|
||||
},
|
||||
/** 提交审批 */
|
||||
handleSubmitapprove(row) {
|
||||
this.$confirm('确定要提交审批吗?', '提示', {
|
||||
@@ -604,14 +597,10 @@ export default {
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const orderIds = row.orderId || this.ids;
|
||||
this.$modal.confirm('是否确认删除调拨单主编号为"' + orderIds + '"的数据项?').then(() => {
|
||||
this.loading = true;
|
||||
return delTransferOrder(orderIds);
|
||||
}).then(() => {
|
||||
this.loading = false;
|
||||
this.loading = true;
|
||||
delTransferOrder(orderIds).then(() => {
|
||||
this.getList();
|
||||
this.$modal.msgSuccess("删除成功");
|
||||
}).catch(() => {
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
@@ -167,45 +167,29 @@ export default {
|
||||
if (newVal !== oldVal) {
|
||||
// 预处理数据
|
||||
this.tableData = newVal.map(item => {
|
||||
const isEmpty = this.isEmpty(item);
|
||||
if (isEmpty) {
|
||||
return {
|
||||
...item,
|
||||
oBefore: {
|
||||
manufacturer: item.manufacturerBefore,
|
||||
specification: item.specificationBefore,
|
||||
material: item.materialBefore,
|
||||
itemName: item.materialNameBefore,
|
||||
surfaceTreatmentDesc: item.surfaceTreatmentBefore,
|
||||
zincLayer: item.zincLayerBefore
|
||||
},
|
||||
oAfter: {
|
||||
manufacturer: item.manufacturerAfter,
|
||||
specification: item.specificationAfter,
|
||||
material: item.materialAfter,
|
||||
itemName: item.materialNameAfter,
|
||||
surfaceTreatmentDesc: item.surfaceTreatmentAfter,
|
||||
zincLayer: item.zincLayerAfter
|
||||
},
|
||||
isConfirmed: this.isConfirmed(item)
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...item,
|
||||
oBefore: {
|
||||
manufacturer: item.manufacturerBefore,
|
||||
specification: item.specificationBefore,
|
||||
material: item.materialBefore,
|
||||
itemName: item.materialNameBefore,
|
||||
surfaceTreatmentDesc: item.surfaceTreatmentBefore,
|
||||
zincLayer: item.zincLayerBefore
|
||||
},
|
||||
// 将三个after字段设置为与before相同
|
||||
itemIdAfter: item.itemIdBefore,
|
||||
warehouseIdAfter: item.warehouseIdBefore,
|
||||
materialTypeAfter: item.materialTypeBefore,
|
||||
isConfirmed: this.isConfirmed(item)
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
oBefore: {
|
||||
manufacturer: item.manufacturerBefore,
|
||||
specification: item.specificationBefore,
|
||||
material: item.materialBefore,
|
||||
itemName: item.materialNameBefore,
|
||||
surfaceTreatmentDesc: item.surfaceTreatmentBefore,
|
||||
zincLayer: item.zincLayerBefore
|
||||
},
|
||||
oAfter: {
|
||||
manufacturer: item.manufacturerAfter ?? item.manufacturerBefore,
|
||||
specification: item.specificationAfter ?? item.specificationBefore,
|
||||
material: item.materialAfter ?? item.materialBefore,
|
||||
itemName: item.materialNameAfter ?? item.materialNameBefore,
|
||||
surfaceTreatmentDesc: item.surfaceTreatmentAfter ?? item.surfaceTreatmentBefore,
|
||||
zincLayer: item.zincLayerAfter ?? item.zincLayerBefore
|
||||
},
|
||||
// 每个字段独立判断:如果after有值则用after,否则回退到before
|
||||
itemIdAfter: item.itemIdAfter ?? item.itemIdBefore,
|
||||
warehouseIdAfter: item.warehouseIdAfter ?? item.warehouseIdBefore,
|
||||
materialTypeAfter: item.materialTypeAfter ?? item.materialTypeBefore,
|
||||
isConfirmed: this.isConfirmed(item)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -226,10 +210,6 @@ export default {
|
||||
// return item && item.itemIdAfter && item.warehouseIdAfter && item.materialTypeAfter
|
||||
return item.isTransferred == 1
|
||||
},
|
||||
// isEmpty 是否为空
|
||||
isEmpty(item) {
|
||||
return item && item.itemIdAfter && item.warehouseIdAfter && item.materialTypeAfter
|
||||
},
|
||||
handleUpdate(row) {
|
||||
updateTransferOrderItem(row).then(res => {
|
||||
this.$message({
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
</WarehouseSelect>
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨单号">
|
||||
<el-select v-model="queryParams.transferNo" filterable remote clearable
|
||||
<el-select v-model="queryParams.transferId" filterable remote clearable
|
||||
remote-method="remoteSearchTransferOrder" :remote-loading="transferOrderLoading" placeholder="请输入调拨单号搜索"
|
||||
@change="handleRegularSearch" style="width:220px">
|
||||
<el-option v-for="item in transferOrderOptions" :key="item.orderId" :label="item.transferNo"
|
||||
:value="item.transferNo" />
|
||||
:value="item.orderId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效状态">
|
||||
@@ -32,7 +32,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" plain @click="handleRegularSearch">查询</el-button>
|
||||
<el-button type="success" icon="el-icon-check" plain @click="handleConfirmBatch">批量执行</el-button>
|
||||
<el-button type="success" icon="el-icon-check" plain :loading="batchConfirmLoading" @click="handleConfirmBatch">批量执行</el-button>
|
||||
<el-button @click="handleReset">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -46,7 +46,7 @@
|
||||
<!-- 调拨表格, 显示调拨记录, 常规筛选作用下生效 -->
|
||||
<TransferItemTable :data="transferList" :canEdit="false" :canConfirm="true" v-loading="loading"
|
||||
ref="transferItemTable" :refreshLabel="refreshLabel" :transferRemark="transferRemark"
|
||||
@refreshData="handleRegularSearch"></TransferItemTable>
|
||||
@refreshData="handleBatchConfirmDone"></TransferItemTable>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="handleRegularSearch" />
|
||||
@@ -103,7 +103,7 @@ export default {
|
||||
currentCoilNo: '',
|
||||
warehouseIdBefore: '',
|
||||
warehouseIdAfter: '',
|
||||
transferNo: '',
|
||||
transferId: '',
|
||||
isTransferred: '0',
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
@@ -114,6 +114,7 @@ export default {
|
||||
transferOrderOptions: [],
|
||||
transferOrderLoading: false,
|
||||
loading: false,
|
||||
batchConfirmLoading: false,
|
||||
total: 0,
|
||||
dialogVisible: false,
|
||||
};
|
||||
@@ -171,7 +172,7 @@ export default {
|
||||
currentCoilNo: '',
|
||||
warehouseIdBefore: '',
|
||||
warehouseIdAfter: '',
|
||||
transferNo: '',
|
||||
transferId: '',
|
||||
isTransferred: '0',
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
@@ -192,7 +193,18 @@ export default {
|
||||
}
|
||||
},
|
||||
handleConfirmBatch() {
|
||||
this.$refs.transferItemTable.handleBatchConfirm();
|
||||
this.$confirm('确定要批量执行调拨吗?', '确认操作', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.batchConfirmLoading = true;
|
||||
this.$refs.transferItemTable.handleBatchConfirm();
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleBatchConfirmDone() {
|
||||
this.batchConfirmLoading = false;
|
||||
this.handleRegularSearch();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
</WarehouseSelect>
|
||||
</el-form-item>
|
||||
<el-form-item label="调拨单号">
|
||||
<el-select v-model="queryParams.transferNo" filterable remote clearable
|
||||
<el-select v-model="queryParams.transferId" filterable remote clearable
|
||||
remote-method="remoteSearchTransferOrder" :remote-loading="transferOrderLoading"
|
||||
placeholder="请输入调拨单号搜索" @change="handleRegularSearch" style="width:220px">
|
||||
<el-option v-for="item in transferOrderOptions" :key="item.orderId"
|
||||
:label="item.transferNo" :value="item.transferNo" />
|
||||
:label="item.transferNo" :value="item.orderId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效状态">
|
||||
@@ -105,7 +105,7 @@ export default {
|
||||
currentCoilNo: '',
|
||||
warehouseIdBefore: '',
|
||||
warehouseIdAfter: '',
|
||||
transferNo: '',
|
||||
transferId: '',
|
||||
isTransferred: '',
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
@@ -173,7 +173,7 @@ export default {
|
||||
currentCoilNo: '',
|
||||
warehouseIdBefore: '',
|
||||
warehouseIdAfter: '',
|
||||
transferNo: '',
|
||||
transferId: '',
|
||||
isTransferred: '',
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
|
||||
@@ -103,20 +103,25 @@
|
||||
<span>维修明细 <span class="en-sub">· Maintenance Details</span></span>
|
||||
</div>
|
||||
<el-table :data="detailList" border size="small" style="width:100%">
|
||||
<el-table-column label="设备部件" align="center" prop="componentName" width="130" />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="110" />
|
||||
<el-table-column label="类型" align="center" width="70">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.maintenanceCategory === 0" size="mini">保养</el-tag>
|
||||
<el-tag v-else-if="scope.row.maintenanceCategory === 1" size="mini" type="warning">维修</el-tag>
|
||||
</template>
|
||||
<el-table-column label="编号" align="center" width="60">
|
||||
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="维修内容" align="center" prop="repairContent" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="负责人" align="center" prop="repairUser" width="90" />
|
||||
<el-table-column label="计划日期" align="center" width="100">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}-{m}-{d}') }}</template>
|
||||
<el-table-column label="时间" align="center" width="130">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}/{m}/{d}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目标厂家" align="center" prop="targetManufacturer" width="110" />
|
||||
<el-table-column label="具体工作内容" align="center" prop="repairContent" min-width="220" show-overflow-tooltip />
|
||||
<el-table-column label="产线执行人员" align="center" prop="lineExecutor" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="设备执行人员" align="center" prop="equipmentExecutor" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="预计开始时间" align="center" prop="plannedStartTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="预计结束时间" align="center" prop="plannedCompleteTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="实际开始时间" align="center" prop="actualStartTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="实际结束时间" align="center" prop="actualCompleteTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="总耗时(h)" align="center" prop="totalHours" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="验收人" align="center" prop="acceptanceUser" min-width="110" show-overflow-tooltip />
|
||||
<el-table-column label="是否完成" align="center" width="100">
|
||||
<template slot-scope="scope">{{ scope.row.detailStatus === 2 ? '是' : '否' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" prop="remark" min-width="180" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div v-if="detailList.length === 0" class="empty-data" style="margin-top:8px;">暂无维修明细</div>
|
||||
|
||||
|
||||
@@ -92,16 +92,21 @@
|
||||
<span>维修明细进度 <span class="en-sub">· Detail Progress</span></span>
|
||||
</div>
|
||||
<el-table :data="detailList" border size="small" style="width:100%">
|
||||
<el-table-column label="设备部件" align="center" prop="componentName" width="120" />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="100" />
|
||||
<el-table-column label="类型" align="center" width="60">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.maintenanceCategory === 0" size="mini">保养</el-tag>
|
||||
<el-tag v-else-if="scope.row.maintenanceCategory === 1" size="mini" type="warning">维修</el-tag>
|
||||
</template>
|
||||
<el-table-column label="编号" align="center" width="60">
|
||||
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="维修内容" align="center" prop="repairContent" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="负责人" align="center" prop="repairUser" width="80" />
|
||||
<el-table-column label="时间" align="center" width="130">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}/{m}/{d}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="具体工作内容" align="center" prop="repairContent" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="产线执行人员" align="center" prop="lineExecutor" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="设备执行人员" align="center" prop="equipmentExecutor" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="预计开始时间" align="center" prop="plannedStartTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="预计结束时间" align="center" prop="plannedCompleteTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="实际开始时间" align="center" prop="actualStartTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="实际结束时间" align="center" prop="actualCompleteTime" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="总耗时(h)" align="center" prop="totalHours" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="验收人" align="center" prop="acceptanceUser" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="进度" align="center" width="160">
|
||||
<template slot-scope="scope">
|
||||
<el-progress :percentage="scope.row.progressRate || 0" :status="scope.row.detailStatus === 2 ? 'success' : ''" :stroke-width="12" style="padding: 4px 0;" />
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<span class="section-title">检修记录 <span class="en-sub">· Inspection Records</span></span>
|
||||
<span class="section-count" v-if="inspectionRecords.length">共 {{ inspectionTotal }} 条</span>
|
||||
</div>
|
||||
<el-table :data="inspectionRecords" border size="small" v-loading="inspectionLoading" max-height="300"
|
||||
<el-table :data="inspectionRecords" border size="small" v-loading="inspectionLoading" height="100%"
|
||||
@selection-change="handleInspectionSelectionChange" ref="inspectionTable">
|
||||
<el-table-column type="selection" width="40" />
|
||||
<el-table-column label="设备部件" align="center" prop="partName" min-width="120" show-overflow-tooltip />
|
||||
@@ -113,25 +113,169 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="detailList" border size="small" v-loading="detailLoading" max-height="300">
|
||||
<el-table-column label="设备部件" align="center" prop="componentName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="110" />
|
||||
<el-table-column label="类型" align="center" width="70">
|
||||
<el-table :data="detailList" border size="small" v-loading="detailLoading" height="100%">
|
||||
<el-table-column label="编号" align="center" width="60">
|
||||
<template slot-scope="scope">{{ scope.$index + 1 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时间" align="center" width="130">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.maintenanceCategory === 0" size="mini">保养</el-tag>
|
||||
<el-tag v-else-if="scope.row.maintenanceCategory === 1" size="mini" type="warning">维修</el-tag>
|
||||
<el-date-picker
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.itemPlanDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="选择日期"
|
||||
style="width: 118px;" />
|
||||
<span v-else>{{ parseTime(scope.row.itemPlanDate, '{y}/{m}/{d}') || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="维修内容" align="center" prop="repairContent" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="负责人" align="center" prop="repairUser" width="90" />
|
||||
<el-table-column label="计划日期" align="center" width="100">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.itemPlanDate, '{y}-{m}-{d}') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目标厂家" align="center" prop="targetManufacturer" width="110" />
|
||||
<el-table-column label="操作" align="center" width="100" fixed="right">
|
||||
<el-table-column label="具体工作内容" align="center" min-width="220">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditDetail(scope.row)"></el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteDetail(scope.row)"></el-button>
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.repairContent"
|
||||
storageKey="maintenanceRepairContent"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="请输入具体工作内容" />
|
||||
<div v-else class="cell-pre-wrap">{{ scope.row.repairContent || '-' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="产线执行人员" align="center" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.lineExecutor"
|
||||
storageKey="maintenanceLineExecutor"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="请输入产线执行人员" />
|
||||
<div v-else class="cell-pre-wrap">{{ scope.row.lineExecutor || '-' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设备执行人员" align="center" min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.equipmentExecutor"
|
||||
storageKey="maintenanceEquipmentExecutor"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="请输入设备执行人员" />
|
||||
<div v-else class="cell-pre-wrap">{{ scope.row.equipmentExecutor || '-' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计开始时间" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.plannedStartTime"
|
||||
storageKey="maintenancePlannedStartTime"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
:inputFormatter="normalizeHm"
|
||||
placeholder="如 07:00" />
|
||||
<span v-else>{{ scope.row.plannedStartTime || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计结束时间" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.plannedCompleteTime"
|
||||
storageKey="maintenancePlannedCompleteTime"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
:inputFormatter="normalizeHm"
|
||||
placeholder="如 18:30" />
|
||||
<span v-else>{{ scope.row.plannedCompleteTime || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际开始时间" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.actualStartTime"
|
||||
storageKey="maintenanceActualStartTime"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
:inputFormatter="normalizeHm"
|
||||
placeholder="如 07:00" />
|
||||
<span v-else>{{ scope.row.actualStartTime || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际结束时间" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.actualCompleteTime"
|
||||
storageKey="maintenanceActualCompleteTime"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
:inputFormatter="normalizeHm"
|
||||
placeholder="如 18:30" />
|
||||
<span v-else>{{ scope.row.actualCompleteTime || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总耗时(h)" align="center" min-width="110">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.totalHours"
|
||||
storageKey="maintenanceTotalHours"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="如 9.5小时" />
|
||||
<span v-else>{{ scope.row.totalHours || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="验收人" align="center" min-width="110">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.acceptanceUser"
|
||||
storageKey="maintenanceAcceptanceUser"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="请输入验收人" />
|
||||
<span v-else>{{ scope.row.acceptanceUser || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否完成" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-select
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.detailStatus"
|
||||
placeholder="请选择"
|
||||
style="width: 80px;">
|
||||
<el-option label="否" :value="0" />
|
||||
<el-option label="是" :value="2" />
|
||||
</el-select>
|
||||
<span v-else>{{ scope.row.detailStatus === 2 ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" align="center" min-width="180">
|
||||
<template slot-scope="scope">
|
||||
<MemoInput
|
||||
v-if="scope.row._editing"
|
||||
v-model="scope.row.remark"
|
||||
storageKey="maintenanceRemark"
|
||||
matchRule="contain"
|
||||
triggerMode="focus"
|
||||
placeholder="请输入备注" />
|
||||
<div v-else class="cell-pre-wrap">{{ scope.row.remark || '-' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="140" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="scope.row._editing">
|
||||
<el-button size="mini" type="text" icon="el-icon-check" @click="saveDetailRow(scope.row)">保存</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-close" @click="cancelEditDetail(scope.row)">取消</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditDetail(scope.row)">编辑</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteDetail(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -196,41 +340,6 @@
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 明细编辑弹窗 -->
|
||||
<el-dialog :title="detailDialogTitle" :visible.sync="detailDialogVisible" width="550px" append-to-body>
|
||||
<el-form ref="detailForm" :model="detailForm" label-width="110px">
|
||||
<el-form-item label="设备部件" prop="componentName">
|
||||
<el-input v-model="detailForm.componentName" placeholder="请输入设备部件名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="产线" prop="productionLine">
|
||||
<el-select v-model="detailForm.productionLine" placeholder="请选择" clearable style="width:100%">
|
||||
<el-option v-for="item in lineList" :key="item.lineId" :label="item.lineName" :value="item.lineName" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="明细类型" prop="maintenanceCategory">
|
||||
<el-select v-model="detailForm.maintenanceCategory" placeholder="请选择" style="width:100%">
|
||||
<el-option label="保养" :value="0" />
|
||||
<el-option label="维修" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="维修内容" prop="repairContent">
|
||||
<el-input v-model="detailForm.repairContent" type="textarea" :rows="2" placeholder="请输入维修内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="repairUser">
|
||||
<el-input v-model="detailForm.repairUser" placeholder="请输入维修负责人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划日期" prop="itemPlanDate">
|
||||
<el-date-picker clearable v-model="detailForm.itemPlanDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标厂家" prop="targetManufacturer">
|
||||
<el-input v-model="detailForm.targetManufacturer" placeholder="请输入目标厂家" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="detailDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="detailButtonLoading" @click="submitDetailForm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -240,18 +349,18 @@ import { listMaintenancePlanDetail, addMaintenancePlanDetail, updateMaintenanceP
|
||||
import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord";
|
||||
import { listProductionLine } from "@/api/wms/productionLine";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import MemoInput from "@/components/MemoInput/index.vue";
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "EqpMaintenance",
|
||||
components: { DragResizePanel },
|
||||
components: { DragResizePanel, MemoInput },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
buttonLoading: false,
|
||||
inspectionLoading: false,
|
||||
detailButtonLoading: false,
|
||||
total: 0,
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
@@ -273,11 +382,7 @@ export default {
|
||||
inspectionQueried: false,
|
||||
selectedInspectionRows: [],
|
||||
// 维修明细
|
||||
detailList: [],
|
||||
detailDialogVisible: false,
|
||||
detailDialogTitle: "",
|
||||
detailForm: {},
|
||||
editingDetailId: null
|
||||
detailList: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -285,6 +390,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
normalizeHm(value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
var s = String(value).trim();
|
||||
if (!s) return '';
|
||||
var m = s.match(/^(\d{1,2})(?::(\d{1,2}))?$/);
|
||||
if (!m) return s;
|
||||
var hh = parseInt(m[1], 10);
|
||||
var mm = m[2] === undefined ? 0 : parseInt(m[2], 10);
|
||||
if (isNaN(hh) || isNaN(mm) || hh < 0 || hh > 23 || mm < 0 || mm > 59) return s;
|
||||
return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0');
|
||||
},
|
||||
async loadLineList() {
|
||||
try {
|
||||
const res = await listProductionLine({ pageSize: 999 });
|
||||
@@ -324,7 +440,9 @@ export default {
|
||||
loadDetailList(planId) {
|
||||
var self = this;
|
||||
listMaintenancePlanDetail({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.detailList = r.rows || [];
|
||||
self.detailList = (r.rows || []).map(function(item) {
|
||||
return self.decorateDetailRow(item);
|
||||
});
|
||||
});
|
||||
},
|
||||
// ---- 检修记录查询 ----
|
||||
@@ -373,9 +491,19 @@ export default {
|
||||
componentName: rec.partName || '',
|
||||
productionLine: self.currentRow.productionLine || rec.productionLine || '',
|
||||
maintenanceCategory: 1,
|
||||
itemPlanDate: rec.inspectTime ? parseTime(rec.inspectTime, '{y}-{m}-{d}') : parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
repairContent: rec.abnormalDesc || '',
|
||||
lineExecutor: rec.inspector || '',
|
||||
equipmentExecutor: '',
|
||||
plannedStartTime: '',
|
||||
plannedCompleteTime: '',
|
||||
actualStartTime: '',
|
||||
actualCompleteTime: '',
|
||||
totalHours: '',
|
||||
acceptanceUser: '',
|
||||
repairUser: rec.inspector || '',
|
||||
detailStatus: 0
|
||||
detailStatus: 0,
|
||||
remark: rec.remark || ''
|
||||
});
|
||||
});
|
||||
Promise.all(promises).then(function() {
|
||||
@@ -464,56 +592,147 @@ export default {
|
||||
},
|
||||
// ---- 明细管理 ----
|
||||
handleAddDetail() {
|
||||
this.detailForm = {
|
||||
planId: this.currentRow.planId,
|
||||
componentName: '',
|
||||
productionLine: this.currentRow.productionLine || '',
|
||||
maintenanceCategory: 1,
|
||||
repairContent: '',
|
||||
repairUser: '',
|
||||
itemPlanDate: undefined,
|
||||
targetManufacturer: '',
|
||||
detailStatus: 0
|
||||
};
|
||||
this.editingDetailId = null;
|
||||
this.detailDialogTitle = "添加维修明细";
|
||||
this.detailDialogVisible = true;
|
||||
},
|
||||
handleEditDetail(row) {
|
||||
this.detailForm = Object.assign({}, row);
|
||||
this.editingDetailId = row.detailId;
|
||||
this.detailDialogTitle = "编辑维修明细";
|
||||
this.detailDialogVisible = true;
|
||||
},
|
||||
submitDetailForm() {
|
||||
if (!this.detailForm.componentName) {
|
||||
this.$modal.msgWarning("请输入设备部件名称");
|
||||
if (!this.currentRow || !this.currentRow.planId) return;
|
||||
if (this.getEditingDetailRow()) {
|
||||
this.$modal.msgWarning("请先保存或取消当前编辑行");
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
this.detailButtonLoading = true;
|
||||
if (this.editingDetailId) {
|
||||
updateMaintenancePlanDetail(this.detailForm).then(function() {
|
||||
self.$modal.msgSuccess("修改成功");
|
||||
self.detailDialogVisible = false;
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
}).finally(function() { self.detailButtonLoading = false; });
|
||||
} else {
|
||||
addMaintenancePlanDetail(this.detailForm).then(function() {
|
||||
self.$modal.msgSuccess("添加成功");
|
||||
self.detailDialogVisible = false;
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
}).finally(function() { self.detailButtonLoading = false; });
|
||||
var row = this.decorateDetailRow(this.getDefaultDetailRow());
|
||||
row._isNew = true;
|
||||
row._editing = true;
|
||||
row._origin = null;
|
||||
this.detailList.unshift(row);
|
||||
},
|
||||
handleEditDetail(row) {
|
||||
if (!row) return;
|
||||
var editing = this.getEditingDetailRow();
|
||||
if (editing && editing !== row) {
|
||||
this.$modal.msgWarning("请先保存或取消当前编辑行");
|
||||
return;
|
||||
}
|
||||
this.beginDetailEdit(row);
|
||||
},
|
||||
beginDetailEdit(row) {
|
||||
if (!row) return;
|
||||
row._origin = JSON.parse(JSON.stringify({
|
||||
itemPlanDate: row.itemPlanDate,
|
||||
repairContent: row.repairContent,
|
||||
lineExecutor: row.lineExecutor,
|
||||
equipmentExecutor: row.equipmentExecutor,
|
||||
plannedStartTime: row.plannedStartTime,
|
||||
plannedCompleteTime: row.plannedCompleteTime,
|
||||
actualStartTime: row.actualStartTime,
|
||||
actualCompleteTime: row.actualCompleteTime,
|
||||
totalHours: row.totalHours,
|
||||
acceptanceUser: row.acceptanceUser,
|
||||
detailStatus: row.detailStatus,
|
||||
remark: row.remark
|
||||
}));
|
||||
row._editing = true;
|
||||
},
|
||||
cancelEditDetail(row) {
|
||||
if (!row) return;
|
||||
if (row._isNew) {
|
||||
this.detailList = this.detailList.filter(function(r) { return r !== row; });
|
||||
return;
|
||||
}
|
||||
if (row._origin) {
|
||||
row.itemPlanDate = row._origin.itemPlanDate;
|
||||
row.repairContent = row._origin.repairContent;
|
||||
row.lineExecutor = row._origin.lineExecutor;
|
||||
row.equipmentExecutor = row._origin.equipmentExecutor;
|
||||
row.plannedStartTime = row._origin.plannedStartTime;
|
||||
row.plannedCompleteTime = row._origin.plannedCompleteTime;
|
||||
row.actualStartTime = row._origin.actualStartTime;
|
||||
row.actualCompleteTime = row._origin.actualCompleteTime;
|
||||
row.totalHours = row._origin.totalHours;
|
||||
row.acceptanceUser = row._origin.acceptanceUser;
|
||||
row.detailStatus = row._origin.detailStatus;
|
||||
row.remark = row._origin.remark;
|
||||
}
|
||||
row._editing = false;
|
||||
row._origin = null;
|
||||
},
|
||||
handleDeleteDetail(row) {
|
||||
var self = this;
|
||||
if (!row) return;
|
||||
if (row._isNew) {
|
||||
this.detailList = this.detailList.filter(function(r) { return r !== row; });
|
||||
return;
|
||||
}
|
||||
this.$modal.confirm('确认删除该维修明细?').then(function() {
|
||||
return delMaintenancePlanDetail(row.detailId);
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("删除成功");
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
}).catch(function() { });
|
||||
},
|
||||
getEditingDetailRow() {
|
||||
return this.detailList.find(function(r) { return r && r._editing; }) || null;
|
||||
},
|
||||
decorateDetailRow(row) {
|
||||
var r = row || {};
|
||||
if (!r._rowKey) r._rowKey = r.detailId ? ("detail_" + r.detailId) : ("tmp_" + Date.now() + "_" + Math.random().toString(16).slice(2));
|
||||
if (typeof r._editing !== 'boolean') r._editing = false;
|
||||
if (typeof r._isNew !== 'boolean') r._isNew = false;
|
||||
if (!r._origin) r._origin = null;
|
||||
if (r.detailStatus === undefined || r.detailStatus === null) r.detailStatus = 0;
|
||||
return r;
|
||||
},
|
||||
getDefaultDetailRow() {
|
||||
return {
|
||||
planId: this.currentRow.planId,
|
||||
componentName: '',
|
||||
productionLine: this.currentRow.productionLine || '',
|
||||
maintenanceCategory: 1,
|
||||
itemPlanDate: parseTime(new Date(), '{y}-{m}-{d}'),
|
||||
repairContent: '',
|
||||
lineExecutor: '',
|
||||
equipmentExecutor: '',
|
||||
plannedStartTime: '',
|
||||
plannedCompleteTime: '',
|
||||
actualStartTime: '',
|
||||
actualCompleteTime: '',
|
||||
totalHours: '',
|
||||
acceptanceUser: '',
|
||||
detailStatus: 0,
|
||||
remark: ''
|
||||
};
|
||||
},
|
||||
buildDetailPayload(row) {
|
||||
return {
|
||||
detailId: row.detailId,
|
||||
planId: this.currentRow.planId,
|
||||
componentName: row.componentName,
|
||||
productionLine: this.currentRow.productionLine || row.productionLine || '',
|
||||
maintenanceCategory: row.maintenanceCategory === undefined ? 1 : row.maintenanceCategory,
|
||||
itemPlanDate: row.itemPlanDate,
|
||||
repairContent: row.repairContent,
|
||||
lineExecutor: row.lineExecutor,
|
||||
equipmentExecutor: row.equipmentExecutor,
|
||||
plannedStartTime: row.plannedStartTime,
|
||||
plannedCompleteTime: row.plannedCompleteTime,
|
||||
actualStartTime: row.actualStartTime,
|
||||
actualCompleteTime: row.actualCompleteTime,
|
||||
totalHours: row.totalHours,
|
||||
acceptanceUser: row.acceptanceUser,
|
||||
detailStatus: row.detailStatus,
|
||||
remark: row.remark
|
||||
};
|
||||
},
|
||||
saveDetailRow(row) {
|
||||
if (!row) return;
|
||||
if (!row.repairContent) {
|
||||
this.$modal.msgWarning("请输入具体工作内容");
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var payload = this.buildDetailPayload(row);
|
||||
var api = row._isNew || !row.detailId ? addMaintenancePlanDetail : updateMaintenancePlanDetail;
|
||||
api(payload).then(function() {
|
||||
self.$modal.msgSuccess(row._isNew || !row.detailId ? "添加成功" : "修改成功");
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -530,6 +749,11 @@ export default {
|
||||
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; }
|
||||
.search-row .el-input { flex: 1; }
|
||||
.list-body { flex: 1; overflow-y: auto; padding: 0 6px; }
|
||||
.list-body { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
|
||||
.list-body::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
.list-body::-webkit-scrollbar-track { background: transparent; }
|
||||
.list-body::-webkit-scrollbar-thumb { background: rgba(144, 147, 153, 0.35); border-radius: 6px; }
|
||||
.list-body::-webkit-scrollbar-thumb:hover { background: rgba(144, 147, 153, 0.55); }
|
||||
.list-item { display: flex; align-items: center; padding: 10px 12px; margin-bottom: 2px; cursor: pointer; border-radius: 6px; transition: all 0.15s; }
|
||||
.list-item:hover { background: #ebeef5; }
|
||||
.list-item.active { background: #d9ecff; }
|
||||
@@ -546,7 +770,7 @@ export default {
|
||||
/* ========== 右侧面板 ========== */
|
||||
.right-panel { height: 100%; overflow: hidden; display: flex; flex-direction: column; background: #faf8f5; }
|
||||
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
|
||||
.right-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; padding: 12px 16px; }
|
||||
.right-content { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; padding: 12px 16px; }
|
||||
|
||||
/* 基本信息栏 */
|
||||
.info-bar { display: flex; align-items: center; justify-content: space-between; padding: 8px 16px; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; margin-bottom: 10px; flex-shrink: 0; }
|
||||
@@ -563,10 +787,25 @@ export default {
|
||||
.section-count { font-size: 12px; color: #909399; }
|
||||
.section-actions { display: flex; align-items: center; gap: 6px; }
|
||||
.inspection-section .el-table { flex: 1; overflow-y: auto; }
|
||||
.inspection-section .el-table { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
|
||||
.detail-section .el-table { scrollbar-width: thin; scrollbar-color: rgba(144, 147, 153, 0.45) transparent; }
|
||||
.inspection-section ::v-deep .el-table,
|
||||
.detail-section ::v-deep .el-table { display: flex; flex-direction: column; height: 100%; }
|
||||
.inspection-section ::v-deep .el-table__body-wrapper,
|
||||
.detail-section ::v-deep .el-table__body-wrapper { flex: 1; min-height: 0; }
|
||||
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar,
|
||||
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-track,
|
||||
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-track { background: transparent; }
|
||||
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb,
|
||||
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb { background: rgba(144, 147, 153, 0.35); border-radius: 6px; }
|
||||
.inspection-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb:hover,
|
||||
.detail-section ::v-deep .el-table__body-wrapper::-webkit-scrollbar-thumb:hover { background: rgba(144, 147, 153, 0.55); }
|
||||
|
||||
/* 下半部分:维修明细 */
|
||||
.detail-section { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; }
|
||||
.detail-section .el-table { flex: 1; overflow-y: auto; }
|
||||
.cell-pre-wrap { white-space: pre-wrap; word-break: break-all; line-height: 18px; }
|
||||
|
||||
.empty-data { padding: 24px 0; text-align: center; color: #c0c4cc; font-size: 13px; }
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
<el-dropdown-item command="exportDataMore">导出产出钢卷(多行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossDataMore">导出消耗钢卷(多行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportDataMerged" divided>导出产出钢卷(多行合并)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossDataMerged">导出消耗钢卷(多行合并)</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
@@ -144,6 +146,11 @@ import CoilTable from "@/views/wms/report/components/coilTable";
|
||||
import ColumnsSetting from "@/views/wms/report/components/setting/columns";
|
||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker.vue";
|
||||
import { saveReportFile } from "@/views/wms/report/js/reportFile";
|
||||
import { mergeCoilCells } from "@/views/wms/report/js/mergeExcelCells";
|
||||
import { saveAs } from 'file-saver'
|
||||
import request from '@/utils/request'
|
||||
import { tansParams } from "@/utils/klp";
|
||||
import { Loading } from 'element-ui'
|
||||
|
||||
export default {
|
||||
name: 'MergeTemplate',
|
||||
@@ -362,6 +369,54 @@ export default {
|
||||
abnormalExportCount: 0
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
// 导出产出钢卷(多行合并) — 二次合并钢卷列单元格
|
||||
async exportDataMerged() {
|
||||
if (this.outList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
await this.exportWithMerge('wms/materialCoil/exportAbnormal', {
|
||||
coilIds: this.outList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `abnormalCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 导出消耗钢卷(多行合并) — 二次合并钢卷列单元格
|
||||
async exportLossDataMerged() {
|
||||
if (this.lossList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
await this.exportWithMerge('wms/materialCoil/export', {
|
||||
coilIds: this.lossList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 通用:导出多行 → ExcelJS 二次合并钢卷列 → 保存
|
||||
async exportWithMerge(url, params, filename) {
|
||||
const loading = Loading.service({
|
||||
text: '正在导出并合并单元格,请稍候',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
try {
|
||||
const data = await request.post(url, params, {
|
||||
transformRequest: [(params) => { return tansParams(params) }],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
responseType: 'blob'
|
||||
})
|
||||
// data 是拦截器返回的 res.data(Blob),与 download 函数一致
|
||||
const blob = new Blob([data])
|
||||
const mergedBlob = await mergeCoilCells(blob, 26)
|
||||
saveAs(mergedBlob, filename)
|
||||
} catch (e) {
|
||||
console.error('导出合并失败', e)
|
||||
this.$message.error('导出失败,请联系管理员')
|
||||
} finally {
|
||||
loading.close()
|
||||
}
|
||||
},
|
||||
// 处理命令
|
||||
handleCommand(command) {
|
||||
if (command === 'exportData') {
|
||||
@@ -376,6 +431,10 @@ export default {
|
||||
this.exportDataMore()
|
||||
} else if (command === 'exportLossDataMore') {
|
||||
this.exportLossDataMore()
|
||||
} else if (command === 'exportDataMerged') {
|
||||
this.exportDataMerged()
|
||||
} else if (command === 'exportLossDataMerged') {
|
||||
this.exportLossDataMerged()
|
||||
}
|
||||
},
|
||||
// 加载列设置
|
||||
|
||||
@@ -98,7 +98,7 @@ export default {
|
||||
const newSet = new Set(nv)
|
||||
const oldSet = ov ? new Set(ov) : new Set()
|
||||
this.orderedColumns = this.orderedColumns.filter(f => newSet.has(f))
|
||||
const added = nv.filter(f => !oldSet.has(f))
|
||||
const added = nv.filter(f => !oldSet.has(f) && !this.orderedColumns.includes(f))
|
||||
if (added.length) {
|
||||
this.orderedColumns.push(...added)
|
||||
}
|
||||
@@ -114,8 +114,9 @@ export default {
|
||||
try {
|
||||
const arr = JSON.parse(cached)
|
||||
if (Array.isArray(arr) && arr.length) {
|
||||
this.selectedColumns = [...arr]
|
||||
this.orderedColumns = [...arr]
|
||||
const uniqueArr = [...new Set(arr)]
|
||||
this.selectedColumns = uniqueArr
|
||||
this.orderedColumns = uniqueArr
|
||||
this.$emit('update:visible', true)
|
||||
return
|
||||
}
|
||||
|
||||
74
klp-ui/src/views/wms/report/js/mergeExcelCells.js
Normal file
74
klp-ui/src/views/wms/report/js/mergeExcelCells.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import ExcelJS from 'exceljs'
|
||||
|
||||
/**
|
||||
* 对异常钢卷报表导出的 Excel 进行二次合并处理
|
||||
* 将钢卷信息列(前26列,A-Z)中连续相同值的单元格合并
|
||||
*
|
||||
* 原理:
|
||||
* 1. 用 row.values 数组代替逐格 getCell,避免 9万+ 次对象创建
|
||||
* 2. 合并单元格后用列级别样式统一居中,代替逐格设置
|
||||
*
|
||||
* @param {Blob} blob - 原始 Excel 文件 Blob
|
||||
* @param {number} mergeColumnCount - 需要合并的钢卷列数,默认 26(A-Z列)
|
||||
* @returns {Promise<Blob>} - 合并后的新 Blob
|
||||
*/
|
||||
export async function mergeCoilCells(blob, mergeColumnCount = 26) {
|
||||
const workbook = new ExcelJS.Workbook()
|
||||
const arrayBuffer = await blob.arrayBuffer()
|
||||
await workbook.xlsx.load(arrayBuffer)
|
||||
|
||||
const worksheet = workbook.worksheets[0]
|
||||
const rowCount = worksheet.rowCount
|
||||
|
||||
if (rowCount <= 1) return blob
|
||||
|
||||
// === 第一步:按钢卷身份标识分组 ===
|
||||
// 使用 row.values 数组(1-based),字符拼接一次性比较,避免逐格 getCell
|
||||
const groups = []
|
||||
let groupStart = 2
|
||||
let prevFingerprint = rowFingerprint(worksheet.getRow(2), mergeColumnCount)
|
||||
|
||||
for (let row = 3; row <= rowCount; row++) {
|
||||
const curFingerprint = rowFingerprint(worksheet.getRow(row), mergeColumnCount)
|
||||
if (curFingerprint !== prevFingerprint) {
|
||||
groups.push({ start: groupStart, end: row - 1 })
|
||||
groupStart = row
|
||||
prevFingerprint = curFingerprint
|
||||
}
|
||||
}
|
||||
groups.push({ start: groupStart, end: rowCount })
|
||||
|
||||
// === 第二步:合并单元格 ===
|
||||
for (const group of groups) {
|
||||
if (group.end > group.start) {
|
||||
for (let col = 1; col <= mergeColumnCount; col++) {
|
||||
worksheet.mergeCells(group.start, col, group.end, col)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 第三步:列级别统一居中(替换逐格设置,26 次 vs 9万次) ===
|
||||
for (let col = 1; col <= mergeColumnCount; col++) {
|
||||
const column = worksheet.getColumn(col)
|
||||
column.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true }
|
||||
}
|
||||
|
||||
// === 第四步:写回 Blob ===
|
||||
const buffer = await workbook.xlsx.writeBuffer()
|
||||
return new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成行的"指纹"字符串,用于快速比较两行钢卷列是否相同
|
||||
* row.values 是 ExcelJS 的 1-based 数组:values[1]=A列, values[2]=B列 ...
|
||||
*/
|
||||
function rowFingerprint(row, colCount) {
|
||||
const vals = row.values || []
|
||||
let fp = ''
|
||||
for (let i = 1; i <= colCount; i++) {
|
||||
fp += String(vals[i] ?? '') + '\x00'
|
||||
}
|
||||
return fp
|
||||
}
|
||||
Reference in New Issue
Block a user