feat: 新增盘库和维修计划初版
This commit is contained in:
44
klp-ui/src/api/flow/countDiscrepancy.js
Normal file
44
klp-ui/src/api/flow/countDiscrepancy.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询盘库差异记录列表
|
||||
export function listCountDiscrepancy(query) {
|
||||
return request({
|
||||
url: '/flow/countDiscrepancy/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询盘库差异记录详细
|
||||
export function getCountDiscrepancy(discrepancyId) {
|
||||
return request({
|
||||
url: '/flow/countDiscrepancy/' + discrepancyId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增盘库差异记录
|
||||
export function addCountDiscrepancy(data) {
|
||||
return request({
|
||||
url: '/flow/countDiscrepancy',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改盘库差异记录
|
||||
export function updateCountDiscrepancy(data) {
|
||||
return request({
|
||||
url: '/flow/countDiscrepancy',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除盘库差异记录
|
||||
export function delCountDiscrepancy(discrepancyId) {
|
||||
return request({
|
||||
url: '/flow/countDiscrepancy/' + discrepancyId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
klp-ui/src/api/flow/countPlan.js
Normal file
44
klp-ui/src/api/flow/countPlan.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询盘库计划主列表
|
||||
export function listCountPlan(query) {
|
||||
return request({
|
||||
url: '/flow/countPlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询盘库计划主详细
|
||||
export function getCountPlan(planId) {
|
||||
return request({
|
||||
url: '/flow/countPlan/' + planId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增盘库计划主
|
||||
export function addCountPlan(data) {
|
||||
return request({
|
||||
url: '/flow/countPlan',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改盘库计划主
|
||||
export function updateCountPlan(data) {
|
||||
return request({
|
||||
url: '/flow/countPlan',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除盘库计划主
|
||||
export function delCountPlan(planId) {
|
||||
return request({
|
||||
url: '/flow/countPlan/' + planId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
klp-ui/src/api/flow/countPlanWarehouse.js
Normal file
44
klp-ui/src/api/flow/countPlanWarehouse.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询盘库计划-库区关联列表
|
||||
export function listCountPlanWarehouse(query) {
|
||||
return request({
|
||||
url: '/flow/countPlanWarehouse/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询盘库计划-库区关联详细
|
||||
export function getCountPlanWarehouse(relId) {
|
||||
return request({
|
||||
url: '/flow/countPlanWarehouse/' + relId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增盘库计划-库区关联
|
||||
export function addCountPlanWarehouse(data) {
|
||||
return request({
|
||||
url: '/flow/countPlanWarehouse',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改盘库计划-库区关联
|
||||
export function updateCountPlanWarehouse(data) {
|
||||
return request({
|
||||
url: '/flow/countPlanWarehouse',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除盘库计划-库区关联
|
||||
export function delCountPlanWarehouse(relId) {
|
||||
return request({
|
||||
url: '/flow/countPlanWarehouse/' + relId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
klp-ui/src/api/flow/maintenancePlan.js
Normal file
44
klp-ui/src/api/flow/maintenancePlan.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询维修计划主列表
|
||||
export function listMaintenancePlan(query) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询维修计划主详细
|
||||
export function getMaintenancePlan(planId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlan/' + planId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增维修计划主
|
||||
export function addMaintenancePlan(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlan',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改维修计划主
|
||||
export function updateMaintenancePlan(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlan',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除维修计划主
|
||||
export function delMaintenancePlan(planId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlan/' + planId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
klp-ui/src/api/flow/maintenancePlanAbnormal.js
Normal file
44
klp-ui/src/api/flow/maintenancePlanAbnormal.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询维修计划-异常记录关联列表
|
||||
export function listMaintenancePlanAbnormal(query) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanAbnormal/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询维修计划-异常记录关联详细
|
||||
export function getMaintenancePlanAbnormal(relId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanAbnormal/' + relId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增维修计划-异常记录关联
|
||||
export function addMaintenancePlanAbnormal(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanAbnormal',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改维修计划-异常记录关联
|
||||
export function updateMaintenancePlanAbnormal(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanAbnormal',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除维修计划-异常记录关联
|
||||
export function delMaintenancePlanAbnormal(relId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanAbnormal/' + relId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
44
klp-ui/src/api/flow/maintenancePlanDetail.js
Normal file
44
klp-ui/src/api/flow/maintenancePlanDetail.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询维修计划明细列表
|
||||
export function listMaintenancePlanDetail(query) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanDetail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询维修计划明细详细
|
||||
export function getMaintenancePlanDetail(detailId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanDetail/' + detailId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增维修计划明细
|
||||
export function addMaintenancePlanDetail(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改维修计划明细
|
||||
export function updateMaintenancePlanDetail(data) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除维修计划明细
|
||||
export function delMaintenancePlanDetail(detailId) {
|
||||
return request({
|
||||
url: '/flow/maintenancePlanDetail/' + detailId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -16,18 +16,22 @@ export function getPlanExecuteRel(relId) {
|
||||
}
|
||||
|
||||
export function addPlanExecuteRel(data) {
|
||||
// 剔除掉rejectMark字段
|
||||
const { rejectMark, ...payload } = { ...data };
|
||||
return request({
|
||||
url: '/flow/planExecuteRel',
|
||||
method: 'post',
|
||||
data: data
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
export function updatePlanExecuteRel(data) {
|
||||
// 剔除掉rejectMark字段
|
||||
const { rejectMark, ...payload } = { ...data };
|
||||
return request({
|
||||
url: '/flow/planExecuteRel',
|
||||
method: 'put',
|
||||
data: data
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="selected"
|
||||
:placeholder="placeholder"
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
filterable
|
||||
@change="onChange"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.actualWarehouseId"
|
||||
:label="item.actualWarehouseName"
|
||||
:value="item.actualWarehouseId"
|
||||
:disabled="item.isEnabled === 0"
|
||||
>
|
||||
<span :style="{ paddingLeft: item.level === 2 ? '20px' : '0' }">
|
||||
{{ item.actualWarehouseName }}
|
||||
</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { treeActualWarehouseTwoLevel } from '@/api/wms/actualWarehouse';
|
||||
|
||||
export default {
|
||||
name: 'ActualWarehouseL1L2Select',
|
||||
props: {
|
||||
value: {
|
||||
type: [Number, String],
|
||||
default: null
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择实际库区'
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'mini'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
selected: this.value
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.selected = val;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadOptions();
|
||||
},
|
||||
methods: {
|
||||
loadOptions() {
|
||||
var self = this;
|
||||
treeActualWarehouseTwoLevel({}).then(function(response) {
|
||||
var data = response.data || [];
|
||||
self.options = self.flattenTree(data);
|
||||
}).catch(function() {
|
||||
self.options = [];
|
||||
});
|
||||
},
|
||||
flattenTree(nodes, level) {
|
||||
if (level === undefined) { level = 1; }
|
||||
var result = [];
|
||||
if (!nodes || nodes.length === 0) return result;
|
||||
nodes.forEach(function(node) {
|
||||
result.push({
|
||||
actualWarehouseId: node.actualWarehouseId,
|
||||
actualWarehouseName: node.actualWarehouseName,
|
||||
isEnabled: node.isEnabled,
|
||||
level: level
|
||||
});
|
||||
var children = node.children;
|
||||
if (children && children.length > 0 && level < 2) {
|
||||
var childResults = this.flattenTree(children, level + 1);
|
||||
result = result.concat(childResults);
|
||||
}
|
||||
}, this);
|
||||
return result;
|
||||
},
|
||||
onChange(val) {
|
||||
this.$emit('input', val);
|
||||
this.$emit('change', val);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
661
klp-ui/src/views/wms/post/InvCount/approval.vue
Normal file
661
klp-ui/src/views/wms/post/InvCount/approval.vue
Normal file
@@ -0,0 +1,661 @@
|
||||
<template>
|
||||
<div class="app-approval-wrapper">
|
||||
<div class="app-container count-container">
|
||||
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
|
||||
<template #panelA>
|
||||
<div class="left-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-title">
|
||||
<i class="el-icon-s-check"></i>
|
||||
<span>盘库审批</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input v-model="queryParams.planCode" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
|
||||
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="list-body">
|
||||
<div v-for="item in dataList" :key="item.planId" class="list-item"
|
||||
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
|
||||
<div class="item-main">
|
||||
<span class="item-title">{{ item.planCode }}</span>
|
||||
<span class="item-sub">{{ item.planName }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<el-tag size="mini">待审批</el-tag>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handleRowClick(item)"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<span>暂无待审批盘库计划</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-footer">
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #panelB>
|
||||
<div class="right-panel">
|
||||
<div v-if="!currentRow" class="empty-tip">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请在左侧列表中选择一条盘库计划进行审批</span>
|
||||
</div>
|
||||
<div v-else v-loading="detailLoading" class="detail-content">
|
||||
<div class="doc-header">
|
||||
<div class="doc-header-top">
|
||||
<div class="doc-title-group">
|
||||
<div class="doc-title">{{ currentRow.planCode }}</div>
|
||||
<div class="doc-subtitle">Inventory Count Plan · Approval</div>
|
||||
</div>
|
||||
<div class="doc-header-right">
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-status-row">
|
||||
<span class="doc-status-label">Status / 状态:</span>
|
||||
<el-tag size="small">待审批</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-meta">
|
||||
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span>
|
||||
<span v-if="currentRow.countDate"><i class="el-icon-date"></i>盘库日期: {{ parseTime(currentRow.countDate, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.deadlineTime"><i class="el-icon-time"></i>截止: {{ parseTime(currentRow.deadlineTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span v-if="currentRow.countUserName"><i class="el-icon-user-solid"></i>盘点人: {{ currentRow.countUserName }}</span>
|
||||
<span v-if="currentRow.principalUserName"><i class="el-icon-s-custom"></i>负责人: {{ currentRow.principalUserName }}</span>
|
||||
<span v-if="currentRow.participantNames"><i class="el-icon-user"></i>参与人: {{ currentRow.participantNames }}</span>
|
||||
</div>
|
||||
|
||||
<CountFlowSection :planStatus="currentRow.planStatus" />
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>审批操作 <span class="en-sub">· Approval Actions</span></span>
|
||||
</div>
|
||||
<div class="flow-actions">
|
||||
<el-button type="danger" size="small" icon="el-icon-close" :loading="rejectLoading" @click="handleReject">驳回</el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-check" :loading="approveLoading" @click="handleApprove">审批通过</el-button>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>库区盘点明细 <span class="en-sub">· Warehouse Count Details</span></span>
|
||||
</div>
|
||||
<el-table v-loading="warehouseLoading" :data="warehouseList" border size="small" style="width:100%" row-key="relId">
|
||||
<el-table-column label="逻辑库区" align="center" prop="warehouseName" />
|
||||
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
|
||||
<el-table-column label="出入库查询起始" align="center" width="160">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.ioStartTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出入库查询截止" align="center" width="160">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.ioEndTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="section-gap" />
|
||||
|
||||
<!-- 差异列表:审批人逐条标记需处理 -->
|
||||
<div class="section-title">
|
||||
<span>差异项标记 <span class="en-sub">· Mark Items for Resolution</span></span>
|
||||
</div>
|
||||
<el-table v-loading="discLoading" :data="discList" border size="small" style="width:100%">
|
||||
<el-table-column label="差异类型" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.discrepancyType === 1" type="success" size="mini">盘盈</el-tag>
|
||||
<el-tag v-else-if="scope.row.discrepancyType === 2" type="danger" size="mini">盘亏</el-tag>
|
||||
<el-tag v-else-if="scope.row.discrepancyType === 3" type="warning" size="mini">状态不符</el-tag>
|
||||
<el-tag v-else-if="scope.row.discrepancyType === 4" size="mini">重量偏差</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="钢卷号" align="center" prop="enterCoilNo" width="160" />
|
||||
<el-table-column label="差异详情" align="center" prop="discrepancyDetail" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="需处理" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.processStatus === 1" type="warning" size="mini">是</el-tag>
|
||||
<el-tag v-else type="info" size="mini">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditDiscMark(scope.row)">标记</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="discList.length === 0 && !discLoading" style="padding:12px 0;color:#909399;font-size:13px;">暂无差异记录</div>
|
||||
|
||||
<div class="section-gap" />
|
||||
<div class="section-title">备注 <span class="en-sub">· Remarks</span></div>
|
||||
<div class="remark-content">{{ currentRow.remark || '无' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DragResizePanel>
|
||||
</div>
|
||||
|
||||
<!-- 标记需处理对话框 -->
|
||||
<el-dialog title="标记差异项" :visible.sync="markDialogVisible" width="500px" append-to-body>
|
||||
<el-form ref="markForm" :model="markForm" label-width="100px">
|
||||
<el-form-item label="差异类型">
|
||||
<el-tag v-if="markForm.discrepancyType === 1" type="success">盘盈</el-tag>
|
||||
<el-tag v-else-if="markForm.discrepancyType === 2" type="danger">盘亏</el-tag>
|
||||
<el-tag v-else-if="markForm.discrepancyType === 3" type="warning">状态不符</el-tag>
|
||||
<el-tag v-else-if="markForm.discrepancyType === 4">重量偏差</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="钢卷号">{{ markForm.enterCoilNo }}</el-form-item>
|
||||
<el-form-item label="差异详情">{{ markForm.discrepancyDetail }}</el-form-item>
|
||||
<el-form-item label="需要处理">
|
||||
<el-switch v-model="markForm.needResolve" active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审批意见">
|
||||
<el-input v-model="markForm.approveRemark" type="textarea" :rows="3" placeholder="请输入审批意见" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" @click="submitDiscMark">确 定</el-button>
|
||||
<el-button @click="markDialogVisible = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCountPlan, getCountPlan, updateCountPlan } from "@/api/flow/countPlan";
|
||||
import { listCountPlanWarehouse } from "@/api/flow/countPlanWarehouse";
|
||||
import { listCountDiscrepancy, updateCountDiscrepancy } from "@/api/flow/countDiscrepancy";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import CountFlowSection from "./components/CountFlowSection.vue";
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "InvCountApproval",
|
||||
components: { DragResizePanel, CountFlowSection },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
warehouseLoading: false,
|
||||
approveLoading: false,
|
||||
rejectLoading: false,
|
||||
discLoading: false,
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
planCode: undefined,
|
||||
planStatus: 1
|
||||
},
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
warehouseList: [],
|
||||
discList: [],
|
||||
markDialogVisible: false,
|
||||
markForm: {
|
||||
discrepancyId: null,
|
||||
discrepancyType: null,
|
||||
enterCoilNo: '',
|
||||
discrepancyDetail: '',
|
||||
needResolve: false,
|
||||
approveRemark: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
var self = this;
|
||||
listCountPlan(this.queryParams).then(function(response) {
|
||||
self.dataList = response.rows;
|
||||
self.total = response.total;
|
||||
self.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.currentRow = row;
|
||||
this.loadDetail(row.planId);
|
||||
},
|
||||
loadDetail(planId) {
|
||||
this.detailLoading = true;
|
||||
var self = this;
|
||||
getCountPlan(planId).then(function(response) {
|
||||
self.currentRow = response.data;
|
||||
self.loadWarehouseList(planId);
|
||||
}).finally(function() { self.detailLoading = false; });
|
||||
},
|
||||
loadWarehouseList(planId) {
|
||||
this.warehouseLoading = true;
|
||||
var self = this;
|
||||
listCountPlanWarehouse({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.warehouseList = r.rows || [];
|
||||
self.loadDiscList(); // 仓库加载完成后加载差异
|
||||
}).finally(function() { self.warehouseLoading = false; });
|
||||
},
|
||||
loadDiscList() {
|
||||
this.discLoading = true;
|
||||
this.discList = [];
|
||||
var self = this;
|
||||
if (this.warehouseList.length === 0) {
|
||||
this.discLoading = false;
|
||||
return;
|
||||
}
|
||||
var promises = this.warehouseList.map(function(wh) {
|
||||
return listCountDiscrepancy({ relId: wh.relId, pageNum: 1, pageSize: 999 });
|
||||
});
|
||||
Promise.all(promises).then(function(results) {
|
||||
var all = [];
|
||||
results.forEach(function(r) { all = all.concat(r.rows || []); });
|
||||
self.discList = all;
|
||||
}).finally(function() { self.discLoading = false; });
|
||||
},
|
||||
handleRefreshDetail() {
|
||||
if (this.currentRow && this.currentRow.planId) {
|
||||
this.loadDetail(this.currentRow.planId);
|
||||
}
|
||||
},
|
||||
handleEditDiscMark(row) {
|
||||
this.markForm = {
|
||||
discrepancyId: row.discrepancyId,
|
||||
discrepancyType: row.discrepancyType,
|
||||
enterCoilNo: row.enterCoilNo,
|
||||
discrepancyDetail: row.discrepancyDetail,
|
||||
needResolve: row.processStatus === 1,
|
||||
approveRemark: row.remark || ''
|
||||
};
|
||||
this.markDialogVisible = true;
|
||||
},
|
||||
submitDiscMark() {
|
||||
var self = this;
|
||||
var newStatus = this.markForm.needResolve ? 1 : 0;
|
||||
updateCountDiscrepancy({
|
||||
discrepancyId: this.markForm.discrepancyId,
|
||||
processStatus: newStatus,
|
||||
remark: this.markForm.approveRemark
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess('标记已保存');
|
||||
self.markDialogVisible = false;
|
||||
self.loadDiscList();
|
||||
}).catch(function() {
|
||||
self.$modal.msgError('保存失败');
|
||||
});
|
||||
},
|
||||
handleApprove() {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认通过"' + this.currentRow.planCode + '"的审批?通过后盘库计划将进入差异处理阶段。').then(function() {
|
||||
self.approveLoading = true;
|
||||
return updateCountPlan({ planId: self.currentRow.planId, planStatus: 3 });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("审批通过,盘库计划已进入差异处理阶段");
|
||||
self.approveLoading = false;
|
||||
self.currentRow = null;
|
||||
self.getList();
|
||||
}).catch(function() { self.approveLoading = false; });
|
||||
},
|
||||
handleReject() {
|
||||
var self = this;
|
||||
this.$prompt('请输入驳回原因', '驳回审批', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'textarea',
|
||||
inputValidator: function(val) { return val ? true : '驳回原因不能为空'; }
|
||||
}).then(function({ value }) {
|
||||
self.rejectLoading = true;
|
||||
return updateCountPlan({ planId: self.currentRow.planId, planStatus: 0, remark: value });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("已驳回,盘库计划返回草稿状态");
|
||||
self.rejectLoading = false;
|
||||
self.currentRow = null;
|
||||
self.getList();
|
||||
}).catch(function() { self.rejectLoading = false; });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.count-container {
|
||||
height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
/* ========== 左侧面板 ========== */
|
||||
.left-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 14px 8px;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.header-title i {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 14px 10px;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.list-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.list-item.active .item-title {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.item-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.item-sub {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
flex-shrink: 0;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
flex-shrink: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.list-item:hover .item-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.list-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 0;
|
||||
color: #c0c4cc;
|
||||
font-size: 13px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.list-empty i {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.list-footer {
|
||||
border-top: 1px solid #e4e7ed;
|
||||
padding: 2px 8px 0;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
/* ========== 右侧面板 — Word 文档风格 ========== */
|
||||
.right-panel {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 12px 16px;
|
||||
background: #faf8f5;
|
||||
}
|
||||
|
||||
.right-panel .detail-content {
|
||||
margin: 0 auto;
|
||||
background: #ffffff;
|
||||
padding: 28px 32px 36px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.doc-header {
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 2px solid #1a3c6e;
|
||||
}
|
||||
|
||||
.doc-header-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.doc-title-group {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.doc-title {
|
||||
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.3;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.doc-subtitle {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #8c8c8c;
|
||||
font-style: italic;
|
||||
letter-spacing: 0.8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.doc-header-right {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.doc-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.doc-status-label {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #e0dcd6;
|
||||
}
|
||||
|
||||
.detail-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.detail-meta i {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 22px 0 12px 0;
|
||||
padding: 0 0 10px 0;
|
||||
border-bottom: 1px solid #d4d0c8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.section-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.section-title .en-sub {
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.5px;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.flow-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
padding: 12px 16px;
|
||||
background: #faf8f5;
|
||||
border: 1px solid #e8e4de;
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.section-gap {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.right-panel .el-table {
|
||||
border: 1px solid #e8e4de !important;
|
||||
border-radius: 2px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table thead th {
|
||||
background-color: #2c3e50 !important;
|
||||
color: #ffffff !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 11px !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
border-bottom: none !important;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
.right-panel .el-table thead th .cell {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table__body tr:hover > td {
|
||||
background-color: #f7f5f0 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table--border td {
|
||||
border-right: 1px solid #f0ece6 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table--border th {
|
||||
border-right: 1px solid #3a5166 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table td {
|
||||
padding: 6px 4px !important;
|
||||
color: #3a3a3a !important;
|
||||
}
|
||||
|
||||
.right-panel .el-divider--horizontal {
|
||||
margin: 8px 0 4px;
|
||||
background-color: #e0dcd6;
|
||||
}
|
||||
|
||||
.right-panel .el-tag {
|
||||
border-radius: 2px;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div v-if="enabled" class="section-container">
|
||||
<div class="section-title">
|
||||
<i class="el-icon-s-order"></i>
|
||||
<span>流程总览 <span class="en-sub">· Process Overview</span></span>
|
||||
</div>
|
||||
<el-steps :active="activeStep" align-center class="flow-steps">
|
||||
<el-step title="创建计划" icon="el-icon-document" />
|
||||
<el-step title="快照与对比" icon="el-icon-camera" />
|
||||
<el-step title="提交审批" icon="el-icon-s-promotion" />
|
||||
<el-step title="处理差异" icon="el-icon-warning" />
|
||||
<el-step title="完成归档" icon="el-icon-circle-check" />
|
||||
</el-steps>
|
||||
<div class="current-status">
|
||||
<span class="status-label">Current / 当前阶段:</span>
|
||||
<el-tag :type="tagType" size="small">{{ flowStatusText }}</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CountFlowSection',
|
||||
props: {
|
||||
enabled: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
planStatus: {
|
||||
type: [Number, String],
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* el-steps active 从 0 开始。
|
||||
* 步骤:0=创建计划, 1=快照与对比, 2=提交审批, 3=处理差异, 4=完成归档
|
||||
* status 0=草稿 -> active=0 (创建计划)
|
||||
* status 1=待审批 -> active=2 (提交审批)
|
||||
* status 2=执行中 -> active=3 (处理差异)
|
||||
* status 3=差异处理 -> active=3 (处理差异)
|
||||
* status 4=已归档 -> active=5 (全部finish)
|
||||
*/
|
||||
activeStep() {
|
||||
if (this.planStatus == null) return -1;
|
||||
const v = Number(this.planStatus);
|
||||
if (v >= 4) return 5;
|
||||
if (v === 1) return 2;
|
||||
if (v === 2 || v === 3) return 3;
|
||||
return v;
|
||||
},
|
||||
flowStatusText() {
|
||||
const map = {
|
||||
0: '草稿',
|
||||
1: '待审批',
|
||||
2: '执行中',
|
||||
3: '差异处理中',
|
||||
4: '已归档'
|
||||
};
|
||||
return map[this.planStatus] || '未知';
|
||||
},
|
||||
tagType() {
|
||||
const map = {
|
||||
0: 'info',
|
||||
1: '',
|
||||
2: 'warning',
|
||||
3: 'danger',
|
||||
4: 'success'
|
||||
};
|
||||
return map[this.planStatus] || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.section-container {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 0 0 12px 0;
|
||||
padding: 0 0 10px 0;
|
||||
border-bottom: 1px solid #d4d0c8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.section-title .en-sub {
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.5px;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
font-size: 16px;
|
||||
color: #1a3c6e;
|
||||
}
|
||||
|
||||
.flow-steps {
|
||||
padding: 8px 0 4px;
|
||||
}
|
||||
|
||||
.flow-steps >>> .el-step.is-wait .el-step__icon-inner,
|
||||
.flow-steps >>> .el-step.is-wait .el-step__title {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.flow-steps >>> .el-step.is-process .el-step__icon-inner,
|
||||
.flow-steps >>> .el-step.is-process .el-step__title {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.flow-steps >>> .el-step.is-finish .el-step__icon-inner,
|
||||
.flow-steps >>> .el-step.is-finish .el-step__title {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.flow-steps >>> .el-step__description {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.current-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #e0dcd6;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
</style>
|
||||
759
klp-ui/src/views/wms/post/InvCount/execute.vue
Normal file
759
klp-ui/src/views/wms/post/InvCount/execute.vue
Normal file
@@ -0,0 +1,759 @@
|
||||
<template>
|
||||
<div class="app-container count-container">
|
||||
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
|
||||
<template #panelA>
|
||||
<div class="left-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-title">
|
||||
<i class="el-icon-s-data"></i>
|
||||
<span>盘库执行</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||||
</div>
|
||||
<el-select v-model="queryParams.planStatus" placeholder="执行状态" clearable size="mini" @change="handleQuery" class="header-filter">
|
||||
<el-option label="执行中" :value="2" />
|
||||
<el-option label="差异处理中" :value="3" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input v-model="queryParams.planCode" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
|
||||
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="list-body">
|
||||
<div v-for="item in dataList" :key="item.planId" class="list-item"
|
||||
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
|
||||
<div class="item-main">
|
||||
<span class="item-title">{{ item.planCode }}</span>
|
||||
<span class="item-sub">{{ item.planName }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<el-tag v-if="item.planStatus === 2" type="warning" size="mini">执行中</el-tag>
|
||||
<el-tag v-else-if="item.planStatus === 3" type="danger" size="mini">差异处理中</el-tag>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handleRowClick(item)"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<span>暂无执行中的盘库计划</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-footer">
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #panelB>
|
||||
<div class="right-panel">
|
||||
<div v-if="!currentRow" class="empty-tip">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请在左侧列表中选择一条盘库计划执行操作</span>
|
||||
</div>
|
||||
<div v-else v-loading="detailLoading" class="detail-content">
|
||||
<div class="doc-header">
|
||||
<div class="doc-header-top">
|
||||
<div class="doc-title-group">
|
||||
<div class="doc-title">{{ currentRow.planCode }}</div>
|
||||
<div class="doc-subtitle">Inventory Count Plan · Execution</div>
|
||||
</div>
|
||||
<div class="doc-header-right">
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-status-row">
|
||||
<span class="doc-status-label">Status / 状态:</span>
|
||||
<el-tag v-if="currentRow.planStatus === 2" type="warning" size="small">执行中</el-tag>
|
||||
<el-tag v-else-if="currentRow.planStatus === 3" type="danger" size="small">差异处理中</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-meta">
|
||||
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span>
|
||||
<span v-if="currentRow.countDate"><i class="el-icon-date"></i>盘库日期: {{ parseTime(currentRow.countDate, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.deadlineTime"><i class="el-icon-time"></i>截止: {{ parseTime(currentRow.deadlineTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
<span v-if="currentRow.countUserName"><i class="el-icon-user-solid"></i>盘点人: {{ currentRow.countUserName }}</span>
|
||||
<span v-if="currentRow.principalUserName"><i class="el-icon-s-custom"></i>负责人: {{ currentRow.principalUserName }}</span>
|
||||
<span v-if="currentRow.participantNames"><i class="el-icon-user"></i>参与人: {{ currentRow.participantNames }}</span>
|
||||
</div>
|
||||
|
||||
<CountFlowSection :planStatus="currentRow.planStatus" />
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>执行操作 <span class="en-sub">· Execution Actions</span></span>
|
||||
</div>
|
||||
<div class="flow-actions">
|
||||
<el-button v-if="currentRow.planStatus === 2" type="primary" size="small" icon="el-icon-upload2" @click="handleUploadForPlan">上传实盘Excel</el-button>
|
||||
<el-button v-if="currentRow.planStatus === 2" type="warning" size="small" icon="el-icon-s-data" :loading="generateLoading" @click="handleGenerateDiscrepancy">核对并生成差异报告</el-button>
|
||||
<el-button v-if="currentRow.planStatus === 3" type="success" size="small" icon="el-icon-circle-check" :loading="archiveLoading" @click="handleArchive">归档封存</el-button>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>库区盘点明细 <span class="en-sub">· Warehouse Count Details</span></span>
|
||||
</div>
|
||||
<el-table v-loading="warehouseLoading" :data="warehouseList" border size="small" style="width:100%" row-key="relId">
|
||||
<el-table-column type="expand" width="40">
|
||||
<template slot-scope="scope">
|
||||
<div v-loading="discrepancyLoadingMap[scope.row.relId]" style="padding: 8px 16px;">
|
||||
<el-table v-if="discrepancyMap[scope.row.relId] && discrepancyMap[scope.row.relId].length > 0"
|
||||
:data="discrepancyMap[scope.row.relId]" border size="small">
|
||||
<el-table-column label="差异类型" align="center" width="100">
|
||||
<template slot-scope="ds">
|
||||
<el-tag v-if="ds.row.discrepancyType === 1" type="success" size="mini">盘盈</el-tag>
|
||||
<el-tag v-else-if="ds.row.discrepancyType === 2" type="danger" size="mini">盘亏</el-tag>
|
||||
<el-tag v-else-if="ds.row.discrepancyType === 3" type="warning" size="mini">状态不符</el-tag>
|
||||
<el-tag v-else-if="ds.row.discrepancyType === 4" size="mini">重量偏差</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="钢卷号" align="center" prop="enterCoilNo" width="160" />
|
||||
<el-table-column label="差异详情" align="center" prop="discrepancyDetail" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="原因分析" align="center" prop="reasonAnalysis" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="处理建议" align="center" prop="processSuggestion" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="处理结果" align="center" prop="processResult" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="处理状态" align="center" width="100">
|
||||
<template slot-scope="ds">
|
||||
<el-tag v-if="ds.row.processStatus === 0" type="info" size="mini">待处理</el-tag>
|
||||
<el-tag v-else-if="ds.row.processStatus === 1" type="warning" size="mini">处理中</el-tag>
|
||||
<el-tag v-else-if="ds.row.processStatus === 2" type="success" size="mini">已处理</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="处理人" align="center" prop="processUserName" width="100" />
|
||||
<el-table-column label="操作" align="center" width="100" v-if="currentRow.planStatus === 3">
|
||||
<template slot-scope="ds">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditDiscrepancy(ds.row)">处理</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<span v-else style="color:#909399;font-size:13px;">暂无差异记录</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="逻辑库区" align="center" prop="warehouseName" />
|
||||
<el-table-column label="实际库区" align="center" prop="actualWarehouseName" />
|
||||
<el-table-column label="出入库查询起始" align="center" width="140">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.ioStartTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出入库查询截止" align="center" width="140">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.ioEndTime, '{y}-{m}-{d} {h}:{i}') || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="系统钢卷数量" align="center" prop="systemCoilCount" width="100" />
|
||||
<el-table-column label="系统总重量(kg)" align="center" prop="systemTotalWeight" width="110" />
|
||||
<el-table-column label="实盘钢卷数量" align="center" prop="actualCoilCount" width="100" />
|
||||
<el-table-column label="实盘总重量(kg)" align="center" prop="actualTotalWeight" width="110" />
|
||||
<el-table-column label="账实一致" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.isConsistent === 1" type="success" size="mini">一致</el-tag>
|
||||
<el-tag v-else-if="scope.row.isConsistent === 0" type="danger" size="mini">不一致</el-tag>
|
||||
<span v-else style="color:#909399">未盘点</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="120" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="currentRow.planStatus === 2" size="mini" type="text" icon="el-icon-upload2" @click="handleUploadExcel(scope.row)">上传Excel</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click="handleViewDiscrepancy(scope.row)">差异</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="section-gap" />
|
||||
<div class="section-title">备注 <span class="en-sub">· Remarks</span></div>
|
||||
<div class="remark-content">{{ currentRow.remark || '无' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DragResizePanel>
|
||||
|
||||
<el-dialog title="上传实盘Excel" :visible.sync="uploadDialogVisible" width="500px" append-to-body>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
class="upload-demo"
|
||||
drag
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:data="{ relId: uploadingRelId }"
|
||||
:file-list="uploadFileList"
|
||||
:auto-upload="false"
|
||||
accept=".xlsx,.xls">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将实盘Excel文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">仅支持 .xlsx / .xls 格式</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="uploadDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitUpload">上 传</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="差异处理" :visible.sync="discDialogVisible" width="600px" append-to-body>
|
||||
<el-form ref="discForm" :model="discForm" label-width="100px">
|
||||
<el-form-item label="差异类型">
|
||||
<el-tag v-if="discForm.discrepancyType === 1" type="success">盘盈</el-tag>
|
||||
<el-tag v-else-if="discForm.discrepancyType === 2" type="danger">盘亏</el-tag>
|
||||
<el-tag v-else-if="discForm.discrepancyType === 3" type="warning">状态不符</el-tag>
|
||||
<el-tag v-else-if="discForm.discrepancyType === 4">重量偏差</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="钢卷号">
|
||||
<span>{{ discForm.enterCoilNo }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="差异详情">
|
||||
<span>{{ discForm.discrepancyDetail }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="原因分析" prop="reasonAnalysis">
|
||||
<el-input v-model="discForm.reasonAnalysis" type="textarea" :rows="2" placeholder="请输入原因分析" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理建议" prop="processSuggestion">
|
||||
<el-input v-model="discForm.processSuggestion" type="textarea" :rows="2" placeholder="请输入处理建议" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理结果" prop="processResult">
|
||||
<el-input v-model="discForm.processResult" type="textarea" :rows="2" placeholder="请输入处理结果" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="discDialogVisible = false">取 消</el-button>
|
||||
<el-button :loading="discButtonLoading" type="primary" @click="submitDiscrepancyForm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listCountPlan, getCountPlan, updateCountPlan } from "@/api/flow/countPlan";
|
||||
import { listCountPlanWarehouse } from "@/api/flow/countPlanWarehouse";
|
||||
import { listCountDiscrepancy, updateCountDiscrepancy } from "@/api/flow/countDiscrepancy";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import CountFlowSection from "./components/CountFlowSection.vue";
|
||||
import { getToken } from '@/utils/auth'
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "InvCountExecute",
|
||||
components: { DragResizePanel, CountFlowSection },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
warehouseLoading: false,
|
||||
generateLoading: false,
|
||||
archiveLoading: false,
|
||||
discButtonLoading: false,
|
||||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
planCode: undefined,
|
||||
planStatus: undefined
|
||||
},
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
warehouseList: [],
|
||||
discrepancyMap: {},
|
||||
discrepancyLoadingMap: {},
|
||||
// upload
|
||||
uploadDialogVisible: false,
|
||||
uploadFileList: [],
|
||||
uploadingRelId: null,
|
||||
// discrepancy dialog
|
||||
discDialogVisible: false,
|
||||
discForm: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
uploadAction() {
|
||||
return process.env.VUE_APP_BASE_API + "/flow/countPlan/uploadExcel";
|
||||
},
|
||||
uploadHeaders() {
|
||||
return { Authorization: "Bearer " + getToken() };
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
var self = this;
|
||||
listCountPlan(this.queryParams).then(function(response) {
|
||||
self.dataList = response.rows;
|
||||
self.total = response.total;
|
||||
self.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.currentRow = row;
|
||||
this.loadDetail(row.planId);
|
||||
},
|
||||
loadDetail(planId) {
|
||||
this.detailLoading = true;
|
||||
var self = this;
|
||||
getCountPlan(planId).then(function(response) {
|
||||
self.currentRow = response.data;
|
||||
self.loadWarehouseList(planId);
|
||||
}).finally(function() { self.detailLoading = false; });
|
||||
},
|
||||
loadWarehouseList(planId) {
|
||||
this.warehouseLoading = true;
|
||||
var self = this;
|
||||
listCountPlanWarehouse({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.warehouseList = r.rows || [];
|
||||
}).finally(function() { self.warehouseLoading = false; });
|
||||
},
|
||||
handleRefreshDetail() {
|
||||
if (this.currentRow && this.currentRow.planId) {
|
||||
this.loadDetail(this.currentRow.planId);
|
||||
}
|
||||
},
|
||||
handleViewDiscrepancy(row) {
|
||||
var relId = row.relId;
|
||||
if (this.discrepancyMap[relId]) {
|
||||
return;
|
||||
}
|
||||
this.$set(this.discrepancyLoadingMap, relId, true);
|
||||
var self = this;
|
||||
listCountDiscrepancy({ relId: relId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.$set(self.discrepancyMap, relId, r.rows || []);
|
||||
}).finally(function() {
|
||||
self.$set(self.discrepancyLoadingMap, relId, false);
|
||||
});
|
||||
},
|
||||
// ---- Upload ----
|
||||
handleUploadExcel(row) {
|
||||
this.uploadingRelId = row.relId;
|
||||
this.uploadFileList = [];
|
||||
this.uploadDialogVisible = true;
|
||||
},
|
||||
handleUploadForPlan() {
|
||||
if (this.warehouseList.length === 0) {
|
||||
this.$modal.msgWarning("无关联库区,无需上传");
|
||||
return;
|
||||
}
|
||||
this.handleUploadExcel(this.warehouseList[0]);
|
||||
},
|
||||
beforeUpload(file) {
|
||||
var isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
|
||||
if (!isExcel) {
|
||||
this.$modal.msgError("仅支持 .xlsx / .xls 格式文件");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
submitUpload() {
|
||||
this.$refs.upload.submit();
|
||||
},
|
||||
handleUploadSuccess(response, file, fileList) {
|
||||
if (response.code === 200) {
|
||||
this.$modal.msgSuccess("Excel上传成功");
|
||||
this.uploadDialogVisible = false;
|
||||
this.loadWarehouseList(this.currentRow.planId);
|
||||
} else {
|
||||
this.$modal.msgError(response.msg || "上传失败");
|
||||
}
|
||||
},
|
||||
handleUploadError(err, file, fileList) {
|
||||
this.$modal.msgError("文件上传失败,请重试");
|
||||
},
|
||||
// ---- Workflow actions ----
|
||||
handleGenerateDiscrepancy() {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认将系统库存快照与实盘Excel数据进行核对?系统将自动逐项比对并找出差异。').then(function() {
|
||||
self.generateLoading = true;
|
||||
return updateCountPlan({ planId: self.currentRow.planId, planStatus: 3 });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("差异报告已生成");
|
||||
self.generateLoading = false;
|
||||
self.loadDetail(self.currentRow.planId);
|
||||
self.getList();
|
||||
}).catch(function() { self.generateLoading = false; });
|
||||
},
|
||||
handleArchive() {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认将盘库计划"' + this.currentRow.planCode + '"归档封存?归档后数据将不可修改。').then(function() {
|
||||
self.archiveLoading = true;
|
||||
return updateCountPlan({ planId: self.currentRow.planId, planStatus: 4 });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("盘库计划已归档");
|
||||
self.archiveLoading = false;
|
||||
self.currentRow = null;
|
||||
self.getList();
|
||||
}).catch(function() { self.archiveLoading = false; });
|
||||
},
|
||||
// ---- Discrepancy handling ----
|
||||
handleEditDiscrepancy(row) {
|
||||
this.discForm = Object.assign({}, row);
|
||||
this.discDialogVisible = true;
|
||||
},
|
||||
submitDiscrepancyForm() {
|
||||
var self = this;
|
||||
this.discButtonLoading = true;
|
||||
updateCountDiscrepancy({
|
||||
discrepancyId: this.discForm.discrepancyId,
|
||||
reasonAnalysis: this.discForm.reasonAnalysis,
|
||||
processSuggestion: this.discForm.processSuggestion,
|
||||
processResult: this.discForm.processResult,
|
||||
processStatus: this.discForm.processResult ? 2 : this.discForm.processStatus
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("差异处理保存成功");
|
||||
self.discDialogVisible = false;
|
||||
var relId = self.discForm.relId;
|
||||
if (relId) {
|
||||
self.$set(self.discrepancyMap, relId, undefined);
|
||||
self.handleViewDiscrepancy({ relId: relId });
|
||||
}
|
||||
}).finally(function() { self.discButtonLoading = false; });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.count-container {
|
||||
height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
/* ========== 左侧面板 ========== */
|
||||
.left-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #f5f7fa;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 14px 8px;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.header-title i {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.header-filter {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.search-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 14px 10px;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.list-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.list-item.active .item-title {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.item-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.item-sub {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.item-meta {
|
||||
flex-shrink: 0;
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
flex-shrink: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.list-item:hover .item-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.list-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 0;
|
||||
color: #c0c4cc;
|
||||
font-size: 13px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.list-empty i {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.list-footer {
|
||||
border-top: 1px solid #e4e7ed;
|
||||
padding: 2px 8px 0;
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
/* ========== 右侧面板 — Word 文档风格 ========== */
|
||||
.right-panel {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 12px 16px;
|
||||
background: #faf8f5;
|
||||
}
|
||||
|
||||
.right-panel .detail-content {
|
||||
margin: 0 auto;
|
||||
background: #ffffff;
|
||||
padding: 28px 32px 36px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04);
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.doc-header {
|
||||
margin-bottom: 18px;
|
||||
padding-bottom: 14px;
|
||||
border-bottom: 2px solid #1a3c6e;
|
||||
}
|
||||
|
||||
.doc-header-top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.doc-title-group {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.doc-title {
|
||||
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.3;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.doc-subtitle {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #8c8c8c;
|
||||
font-style: italic;
|
||||
letter-spacing: 0.8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.doc-header-right {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.doc-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.doc-status-label {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-size: 11px;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #e0dcd6;
|
||||
}
|
||||
|
||||
.detail-meta span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.detail-meta i {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
||||
width: 100%;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
margin: 22px 0 12px 0;
|
||||
padding: 0 0 10px 0;
|
||||
border-bottom: 1px solid #d4d0c8;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.section-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.section-title .en-sub {
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
color: #8c8c8c;
|
||||
letter-spacing: 0.5px;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.flow-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
padding: 12px 16px;
|
||||
background: #faf8f5;
|
||||
border: 1px solid #e8e4de;
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.section-gap {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.right-panel .el-table {
|
||||
border: 1px solid #e8e4de !important;
|
||||
border-radius: 2px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table thead th {
|
||||
background-color: #2c3e50 !important;
|
||||
color: #ffffff !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 11px !important;
|
||||
letter-spacing: 0.5px !important;
|
||||
border-bottom: none !important;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
.right-panel .el-table thead th .cell {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table__body tr:hover > td {
|
||||
background-color: #f7f5f0 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table--border td {
|
||||
border-right: 1px solid #f0ece6 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table--border th {
|
||||
border-right: 1px solid #3a5166 !important;
|
||||
}
|
||||
|
||||
.right-panel .el-table td {
|
||||
padding: 6px 4px !important;
|
||||
color: #3a3a3a !important;
|
||||
}
|
||||
|
||||
.right-panel .el-divider--horizontal {
|
||||
margin: 8px 0 4px;
|
||||
background-color: #e0dcd6;
|
||||
}
|
||||
|
||||
.right-panel .el-tag {
|
||||
border-radius: 2px;
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
2062
klp-ui/src/views/wms/post/InvCount/index.vue
Normal file
2062
klp-ui/src/views/wms/post/InvCount/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
282
klp-ui/src/views/wms/post/eqp/approval.vue
Normal file
282
klp-ui/src/views/wms/post/eqp/approval.vue
Normal file
@@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="app-container count-container">
|
||||
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
|
||||
<template #panelA>
|
||||
<div class="left-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-title">
|
||||
<i class="el-icon-s-check"></i>
|
||||
<span>维修审批</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input v-model="queryParams.planNo" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
|
||||
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="list-body">
|
||||
<div v-for="item in dataList" :key="item.planId" class="list-item"
|
||||
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
|
||||
<div class="item-main">
|
||||
<span class="item-title">{{ item.planNo }}</span>
|
||||
<span class="item-sub">{{ item.planName }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<el-tag size="mini">待审批</el-tag>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handleRowClick(item)"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<span>暂无待审批维修计划</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-footer">
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #panelB>
|
||||
<div class="right-panel">
|
||||
<div v-if="!currentRow" class="empty-tip">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请在左侧列表中选择一条维修计划进行审批</span>
|
||||
</div>
|
||||
<div v-else v-loading="detailLoading" class="detail-content">
|
||||
<div class="doc-header">
|
||||
<div class="doc-header-top">
|
||||
<div class="doc-title-group">
|
||||
<div class="doc-title">{{ currentRow.planNo }}</div>
|
||||
<div class="doc-subtitle">Maintenance Plan · Approval</div>
|
||||
</div>
|
||||
<div class="doc-header-right">
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-status-row">
|
||||
<span class="doc-status-label">Approval / 状态:</span>
|
||||
<el-tag size="small">待审批</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-meta">
|
||||
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span>
|
||||
<span v-if="currentRow.repairType === 1">定期保养</span>
|
||||
<span v-else-if="currentRow.repairType === 2">安全整改</span>
|
||||
<span v-else-if="currentRow.repairType === 3">专项检修</span>
|
||||
<span v-else-if="currentRow.repairType === 4">故障维修</span>
|
||||
<span v-if="currentRow.priorityLevel === 2"><el-tag size="mini" type="danger">重要</el-tag></span>
|
||||
<span v-if="currentRow.plannedStartTime"><i class="el-icon-time"></i>开始: {{ parseTime(currentRow.plannedStartTime, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.plannedEndTime"><i class="el-icon-time"></i>结束: {{ parseTime(currentRow.plannedEndTime, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.dutyDept"><i class="el-icon-s-home"></i>{{ currentRow.dutyDept }}</span>
|
||||
<span v-if="currentRow.planOwner"><i class="el-icon-user-solid"></i>{{ currentRow.planOwner }}</span>
|
||||
<span v-if="currentRow.budgetAmount"><i class="el-icon-money"></i>¥{{ currentRow.budgetAmount }}</span>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>计划说明 <span class="en-sub">· Description</span></span>
|
||||
</div>
|
||||
<div class="remark-content">{{ currentRow.planDescription || '无' }}</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>审批操作 <span class="en-sub">· Approval Actions</span></span>
|
||||
</div>
|
||||
<div class="flow-actions">
|
||||
<el-button type="danger" size="small" icon="el-icon-close" :loading="rejectLoading" @click="handleReject">驳回</el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-check" :loading="approveLoading" @click="handleApprove">审批通过</el-button>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<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>
|
||||
<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>
|
||||
<div v-if="detailList.length === 0" class="empty-data" style="margin-top:8px;">暂无维修明细</div>
|
||||
|
||||
<div class="section-gap" />
|
||||
<div class="section-title">备注 <span class="en-sub">· Remarks</span></div>
|
||||
<div class="remark-content">{{ currentRow.remark || '无' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DragResizePanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listMaintenancePlan, getMaintenancePlan, updateMaintenancePlan } from "@/api/flow/maintenancePlan";
|
||||
import { listMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "EqpApproval",
|
||||
components: { DragResizePanel },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
approveLoading: false,
|
||||
rejectLoading: false,
|
||||
total: 0,
|
||||
queryParams: { pageNum: 1, pageSize: 10, planNo: undefined, approvalStatus: 1 },
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
detailList: []
|
||||
};
|
||||
},
|
||||
created() { this.getList(); },
|
||||
methods: {
|
||||
parseTime,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
var self = this;
|
||||
listMaintenancePlan(this.queryParams).then(function(response) {
|
||||
self.dataList = response.rows;
|
||||
self.total = response.total;
|
||||
self.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.currentRow = row;
|
||||
this.loadDetail(row.planId);
|
||||
},
|
||||
loadDetail(planId) {
|
||||
this.detailLoading = true;
|
||||
var self = this;
|
||||
getMaintenancePlan(planId).then(function(response) {
|
||||
self.currentRow = response.data;
|
||||
self.loadDetailList(planId);
|
||||
}).finally(function() { self.detailLoading = false; });
|
||||
},
|
||||
loadDetailList(planId) {
|
||||
var self = this;
|
||||
listMaintenancePlanDetail({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.detailList = r.rows || [];
|
||||
});
|
||||
},
|
||||
handleRefreshDetail() {
|
||||
if (this.currentRow && this.currentRow.planId) this.loadDetail(this.currentRow.planId);
|
||||
},
|
||||
handleApprove() {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认通过"' + this.currentRow.planNo + '"的审批?').then(function() {
|
||||
self.approveLoading = true;
|
||||
return updateMaintenancePlan({ planId: self.currentRow.planId, approvalStatus: 2, planStatus: 1 });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("审批通过,维修计划已进入待维修状态");
|
||||
self.approveLoading = false;
|
||||
self.currentRow = null;
|
||||
self.getList();
|
||||
}).catch(function() { self.approveLoading = false; });
|
||||
},
|
||||
handleReject() {
|
||||
var self = this;
|
||||
this.$prompt('请输入驳回原因', '驳回审批', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'textarea',
|
||||
inputValidator: function(val) { return val ? true : '驳回原因不能为空'; }
|
||||
}).then(function({ value }) {
|
||||
self.rejectLoading = true;
|
||||
return updateMaintenancePlan({ planId: self.currentRow.planId, approvalStatus: 3, remark: value });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("已驳回");
|
||||
self.rejectLoading = false;
|
||||
self.currentRow = null;
|
||||
self.getList();
|
||||
}).catch(function() { self.rejectLoading = false; });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.count-container { height: calc(100vh - 84px); }
|
||||
.left-panel { display: flex; flex-direction: column; height: 100%; background: #f5f7fa; border-right: 1px solid #e4e7ed; }
|
||||
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; background: #f5f7fa; }
|
||||
.header-title { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #303133; }
|
||||
.header-title i { color: #409eff; font-size: 16px; }
|
||||
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; }
|
||||
.list-body { flex: 1; overflow-y: auto; padding: 0 6px; }
|
||||
.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; }
|
||||
.list-item.active .item-title { color: #409eff; font-weight: 600; }
|
||||
.item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
||||
.item-title { font-size: 13px; font-weight: 500; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.item-sub { font-size: 12px; color: #909399; }
|
||||
.item-meta { flex-shrink: 0; margin: 0 8px; }
|
||||
.item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; }
|
||||
.list-item:hover .item-actions { opacity: 1; }
|
||||
.list-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 0; color: #c0c4cc; font-size: 13px; gap: 8px; }
|
||||
.list-empty i { font-size: 32px; }
|
||||
.list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; }
|
||||
|
||||
.right-panel { height: 100%; overflow-y: auto; padding: 12px 16px; background: #faf8f5; }
|
||||
.right-panel .detail-content { margin: 0 auto; background: #ffffff; padding: 28px 32px 36px; box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04); min-height: 100%; }
|
||||
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
|
||||
|
||||
.doc-header { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 2px solid #1a3c6e; }
|
||||
.doc-header-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.doc-title-group { flex: 1; min-width: 0; }
|
||||
.doc-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; font-size: 24px; font-weight: 700; color: #1a1a1a; line-height: 1.3; letter-spacing: 0.5px; }
|
||||
.doc-subtitle { font-family: 'Georgia', 'Times New Roman', serif; font-size: 12px; font-weight: 400; color: #8c8c8c; font-style: italic; letter-spacing: 0.8px; margin-top: 2px; }
|
||||
.doc-header-right { flex-shrink: 0; }
|
||||
.doc-status-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
|
||||
.doc-status-label { font-family: 'Georgia', 'Times New Roman', serif; font-size: 11px; color: #8c8c8c; letter-spacing: 0.3px; }
|
||||
|
||||
.detail-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 12px; color: #909399; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #e0dcd6; }
|
||||
.detail-meta span { display: inline-flex; align-items: center; gap: 4px; }
|
||||
.detail-meta i { font-size: 13px; }
|
||||
|
||||
.section-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; width: 100%; font-size: 15px; font-weight: 700; color: #1a1a1a; margin: 22px 0 12px 0; padding: 0 0 10px 0; border-bottom: 1px solid #d4d0c8; display: flex; align-items: center; gap: 10px; letter-spacing: 0.3px; }
|
||||
.section-title:first-child { margin-top: 0; }
|
||||
.section-title .en-sub { font-size: 11px; font-weight: 400; color: #8c8c8c; letter-spacing: 0.5px; font-family: 'Georgia', 'Times New Roman', serif; font-style: italic; }
|
||||
|
||||
.flow-actions { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.remark-content { padding: 12px 16px; background: #faf8f5; border: 1px solid #e8e4de; border-radius: 2px; font-size: 13px; line-height: 1.8; color: #1a1a1a; }
|
||||
.section-gap { height: 16px; }
|
||||
.empty-data { color: #8c8c8c; font-size: 13px; font-style: italic; }
|
||||
|
||||
.right-panel .el-table { border: 1px solid #e8e4de !important; border-radius: 2px !important; font-size: 12px !important; }
|
||||
.right-panel .el-table thead th { background-color: #2c3e50 !important; color: #ffffff !important; font-weight: 600 !important; font-size: 11px !important; letter-spacing: 0.5px !important; border-bottom: none !important; font-family: 'Georgia', 'Times New Roman', serif; }
|
||||
.right-panel .el-table thead th .cell { color: #ffffff !important; }
|
||||
.right-panel .el-table__body tr:hover > td { background-color: #f7f5f0 !important; }
|
||||
.right-panel .el-table--border td { border-right: 1px solid #f0ece6 !important; }
|
||||
.right-panel .el-table--border th { border-right: 1px solid #3a5166 !important; }
|
||||
.right-panel .el-table td { padding: 6px 4px !important; color: #3a3a3a !important; }
|
||||
.right-panel .el-divider--horizontal { margin: 8px 0 4px; background-color: #e0dcd6; }
|
||||
.right-panel .el-tag { border-radius: 2px; font-family: 'Georgia', 'Times New Roman', serif; letter-spacing: 0.3px; }
|
||||
</style>
|
||||
342
klp-ui/src/views/wms/post/eqp/execute.vue
Normal file
342
klp-ui/src/views/wms/post/eqp/execute.vue
Normal file
@@ -0,0 +1,342 @@
|
||||
<template>
|
||||
<div class="app-container count-container">
|
||||
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
|
||||
<template #panelA>
|
||||
<div class="left-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-title">
|
||||
<i class="el-icon-s-promotion"></i>
|
||||
<span>维修执行</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||||
</div>
|
||||
<el-select v-model="queryParams.planStatus" placeholder="执行状态" clearable size="mini" @change="handleQuery" class="header-filter">
|
||||
<el-option label="待维修" :value="1" />
|
||||
<el-option label="维修中" :value="2" />
|
||||
<el-option label="已完成" :value="3" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input v-model="queryParams.planNo" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
|
||||
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="list-body">
|
||||
<div v-for="item in dataList" :key="item.planId" class="list-item"
|
||||
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
|
||||
<div class="item-main">
|
||||
<span class="item-title">{{ item.planNo }}</span>
|
||||
<span class="item-sub">{{ item.planName }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<el-tag v-if="item.planStatus === 1" type="info" size="mini">待维修</el-tag>
|
||||
<el-tag v-else-if="item.planStatus === 2" type="warning" size="mini">维修中</el-tag>
|
||||
<el-tag v-else-if="item.planStatus === 3" type="success" size="mini">已完成</el-tag>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-view" @click.stop="handleRowClick(item)"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<span>暂无执行中的维修计划</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-footer">
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #panelB>
|
||||
<div class="right-panel">
|
||||
<div v-if="!currentRow" class="empty-tip">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请在左侧列表中选择一条维修计划执行操作</span>
|
||||
</div>
|
||||
<div v-else v-loading="detailLoading" class="detail-content">
|
||||
<div class="doc-header">
|
||||
<div class="doc-header-top">
|
||||
<div class="doc-title-group">
|
||||
<div class="doc-title">{{ currentRow.planNo }}</div>
|
||||
<div class="doc-subtitle">Maintenance Plan · Execution</div>
|
||||
</div>
|
||||
<div class="doc-header-right">
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-status-row">
|
||||
<span class="doc-status-label">Status / 状态:</span>
|
||||
<el-tag v-if="currentRow.planStatus === 1" type="info" size="small">待维修</el-tag>
|
||||
<el-tag v-else-if="currentRow.planStatus === 2" type="warning" size="small">维修中</el-tag>
|
||||
<el-tag v-else-if="currentRow.planStatus === 3" type="success" size="small">已完成</el-tag>
|
||||
<el-button v-if="canComplete" size="mini" type="success" plain icon="el-icon-circle-check" style="margin-left:12px;" @click="handleCompletePlan">完成计划</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-meta">
|
||||
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span>
|
||||
<span v-if="currentRow.repairType === 1">定期保养</span>
|
||||
<span v-else-if="currentRow.repairType === 2">安全整改</span>
|
||||
<span v-else-if="currentRow.repairType === 3">专项检修</span>
|
||||
<span v-else-if="currentRow.repairType === 4">故障维修</span>
|
||||
<span v-if="currentRow.dutyDept"><i class="el-icon-s-home"></i>{{ currentRow.dutyDept }}</span>
|
||||
<span v-if="currentRow.planOwner"><i class="el-icon-user-solid"></i>{{ currentRow.planOwner }}</span>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<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>
|
||||
<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="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;" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.detailStatus === 0" type="info" size="mini">未开始</el-tag>
|
||||
<el-tag v-else-if="scope.row.detailStatus === 1" type="warning" size="mini">进行中</el-tag>
|
||||
<el-tag v-else-if="scope.row.detailStatus === 2" type="success" size="mini">已完成</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际成本" align="center" width="100">
|
||||
<template slot-scope="scope">¥{{ scope.row.actualCost || 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="80" fixed="right" v-if="currentRow.planStatus !== 3">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditProgress(scope.row)">进度</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="detailList.length === 0" class="empty-data" style="margin-top:8px;">暂无维修明细</div>
|
||||
|
||||
<div class="section-gap" />
|
||||
<div class="section-title">备注 <span class="en-sub">· Remarks</span></div>
|
||||
<div class="remark-content">{{ currentRow.remark || '无' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DragResizePanel>
|
||||
|
||||
<!-- 进度编辑弹窗 -->
|
||||
<el-dialog title="更新维修进度" :visible.sync="progressDialogVisible" width="550px" append-to-body>
|
||||
<el-form ref="progressForm" :model="progressForm" label-width="100px">
|
||||
<el-form-item label="设备部件">
|
||||
<span>{{ progressForm.componentName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="维修内容">
|
||||
<span>{{ progressForm.repairContent }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="完成进度" prop="progressRate">
|
||||
<el-slider v-model="progressForm.progressRate" :min="0" :max="100" :step="5" show-input style="padding:0 10px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="完成状态" prop="detailStatus">
|
||||
<el-select v-model="progressForm.detailStatus" placeholder="请选择" style="width:100%">
|
||||
<el-option label="未开始" :value="0" />
|
||||
<el-option label="进行中" :value="1" />
|
||||
<el-option label="已完成" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="实际成本(元)" prop="actualCost">
|
||||
<el-input-number v-model="progressForm.actualCost" :min="0" :precision="2" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责人" prop="repairUser">
|
||||
<el-input v-model="progressForm.repairUser" placeholder="请输入维修负责人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="完成时间" prop="completeTime" v-if="progressForm.detailStatus === 2">
|
||||
<el-date-picker clearable v-model="progressForm.completeTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择完成时间" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="progressDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="progressButtonLoading" @click="submitProgressForm">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listMaintenancePlan, getMaintenancePlan, updateMaintenancePlan } from "@/api/flow/maintenancePlan";
|
||||
import { listMaintenancePlanDetail, updateMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "EqpExecute",
|
||||
components: { DragResizePanel },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
progressButtonLoading: false,
|
||||
total: 0,
|
||||
queryParams: { pageNum: 1, pageSize: 10, planNo: undefined, approvalStatus: 2, planStatus: undefined },
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
detailList: [],
|
||||
progressDialogVisible: false,
|
||||
progressForm: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
canComplete() {
|
||||
if (!this.currentRow || this.currentRow.planStatus === 3) return false;
|
||||
if (this.detailList.length === 0) return false;
|
||||
return this.detailList.every(function(d) { return d.detailStatus === 2; });
|
||||
}
|
||||
},
|
||||
created() { this.getList(); },
|
||||
methods: {
|
||||
parseTime,
|
||||
getList() {
|
||||
this.loading = true;
|
||||
var self = this;
|
||||
listMaintenancePlan(this.queryParams).then(function(response) {
|
||||
self.dataList = response.rows;
|
||||
self.total = response.total;
|
||||
self.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.currentRow = row;
|
||||
this.loadDetail(row.planId);
|
||||
},
|
||||
loadDetail(planId) {
|
||||
this.detailLoading = true;
|
||||
var self = this;
|
||||
getMaintenancePlan(planId).then(function(response) {
|
||||
self.currentRow = response.data;
|
||||
self.loadDetailList(planId);
|
||||
}).finally(function() { self.detailLoading = false; });
|
||||
},
|
||||
loadDetailList(planId) {
|
||||
var self = this;
|
||||
listMaintenancePlanDetail({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.detailList = r.rows || [];
|
||||
});
|
||||
},
|
||||
handleRefreshDetail() {
|
||||
if (this.currentRow && this.currentRow.planId) this.loadDetail(this.currentRow.planId);
|
||||
},
|
||||
// ---- Progress editing ----
|
||||
handleEditProgress(row) {
|
||||
this.progressForm = Object.assign({}, row, {
|
||||
completeTime: row.completeTime || undefined
|
||||
});
|
||||
this.progressDialogVisible = true;
|
||||
},
|
||||
submitProgressForm() {
|
||||
var self = this;
|
||||
var data = {
|
||||
detailId: this.progressForm.detailId,
|
||||
progressRate: this.progressForm.progressRate,
|
||||
detailStatus: this.progressForm.detailStatus,
|
||||
actualCost: this.progressForm.actualCost,
|
||||
repairUser: this.progressForm.repairUser
|
||||
};
|
||||
if (this.progressForm.detailStatus === 2) {
|
||||
data.completeTime = this.progressForm.completeTime || this.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}');
|
||||
}
|
||||
this.progressButtonLoading = true;
|
||||
updateMaintenancePlanDetail(data).then(function() {
|
||||
self.$modal.msgSuccess("进度更新成功");
|
||||
self.progressDialogVisible = false;
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
}).finally(function() { self.progressButtonLoading = false; });
|
||||
},
|
||||
// ---- Complete plan ----
|
||||
handleCompletePlan() {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认完成维修计划"' + this.currentRow.planNo + '"?').then(function() {
|
||||
return updateMaintenancePlan({
|
||||
planId: self.currentRow.planId,
|
||||
planStatus: 3,
|
||||
actualCompleteTime: self.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
});
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("维修计划已完成");
|
||||
self.loadDetail(self.currentRow.planId);
|
||||
self.getList();
|
||||
}).catch(function() { });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.count-container { height: calc(100vh - 84px); }
|
||||
.left-panel { display: flex; flex-direction: column; height: 100%; background: #f5f7fa; border-right: 1px solid #e4e7ed; }
|
||||
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; background: #f5f7fa; }
|
||||
.header-title { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #303133; }
|
||||
.header-title i { color: #409eff; font-size: 16px; }
|
||||
.header-filter { width: 120px; }
|
||||
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; }
|
||||
.list-body { flex: 1; overflow-y: auto; padding: 0 6px; }
|
||||
.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; }
|
||||
.list-item.active .item-title { color: #409eff; font-weight: 600; }
|
||||
.item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
||||
.item-title { font-size: 13px; font-weight: 500; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.item-sub { font-size: 12px; color: #909399; }
|
||||
.item-meta { flex-shrink: 0; margin: 0 8px; }
|
||||
.item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; }
|
||||
.list-item:hover .item-actions { opacity: 1; }
|
||||
.list-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 0; color: #c0c4cc; font-size: 13px; gap: 8px; }
|
||||
.list-empty i { font-size: 32px; }
|
||||
.list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; }
|
||||
|
||||
.right-panel { height: 100%; overflow-y: auto; padding: 12px 16px; background: #faf8f5; }
|
||||
.right-panel .detail-content { margin: 0 auto; background: #ffffff; padding: 28px 32px 36px; box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04); min-height: 100%; }
|
||||
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
|
||||
|
||||
.doc-header { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 2px solid #1a3c6e; }
|
||||
.doc-header-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.doc-title-group { flex: 1; min-width: 0; }
|
||||
.doc-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; font-size: 24px; font-weight: 700; color: #1a1a1a; line-height: 1.3; letter-spacing: 0.5px; }
|
||||
.doc-subtitle { font-family: 'Georgia', 'Times New Roman', serif; font-size: 12px; font-weight: 400; color: #8c8c8c; font-style: italic; letter-spacing: 0.8px; margin-top: 2px; }
|
||||
.doc-header-right { flex-shrink: 0; }
|
||||
.doc-status-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
|
||||
.doc-status-label { font-family: 'Georgia', 'Times New Roman', serif; font-size: 11px; color: #8c8c8c; letter-spacing: 0.3px; }
|
||||
|
||||
.detail-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 12px; color: #909399; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #e0dcd6; }
|
||||
.detail-meta span { display: inline-flex; align-items: center; gap: 4px; }
|
||||
.detail-meta i { font-size: 13px; }
|
||||
|
||||
.section-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; width: 100%; font-size: 15px; font-weight: 700; color: #1a1a1a; margin: 22px 0 12px 0; padding: 0 0 10px 0; border-bottom: 1px solid #d4d0c8; display: flex; align-items: center; gap: 10px; letter-spacing: 0.3px; }
|
||||
.section-title:first-child { margin-top: 0; }
|
||||
.section-title .en-sub { font-size: 11px; font-weight: 400; color: #8c8c8c; letter-spacing: 0.5px; font-family: 'Georgia', 'Times New Roman', serif; font-style: italic; }
|
||||
|
||||
.remark-content { padding: 12px 16px; background: #faf8f5; border: 1px solid #e8e4de; border-radius: 2px; font-size: 13px; line-height: 1.8; color: #1a1a1a; }
|
||||
.section-gap { height: 16px; }
|
||||
.empty-data { color: #8c8c8c; font-size: 13px; font-style: italic; }
|
||||
|
||||
.right-panel .el-table { border: 1px solid #e8e4de !important; border-radius: 2px !important; font-size: 12px !important; }
|
||||
.right-panel .el-table thead th { background-color: #2c3e50 !important; color: #ffffff !important; font-weight: 600 !important; font-size: 11px !important; letter-spacing: 0.5px !important; border-bottom: none !important; font-family: 'Georgia', 'Times New Roman', serif; }
|
||||
.right-panel .el-table thead th .cell { color: #ffffff !important; }
|
||||
.right-panel .el-table__body tr:hover > td { background-color: #f7f5f0 !important; }
|
||||
.right-panel .el-table--border td { border-right: 1px solid #f0ece6 !important; }
|
||||
.right-panel .el-table--border th { border-right: 1px solid #3a5166 !important; }
|
||||
.right-panel .el-table td { padding: 6px 4px !important; color: #3a3a3a !important; }
|
||||
.right-panel .el-divider--horizontal { margin: 8px 0 4px; background-color: #e0dcd6; }
|
||||
.right-panel .el-tag { border-radius: 2px; font-family: 'Georgia', 'Times New Roman', serif; letter-spacing: 0.3px; }
|
||||
</style>
|
||||
666
klp-ui/src/views/wms/post/eqp/index.vue
Normal file
666
klp-ui/src/views/wms/post/eqp/index.vue
Normal file
@@ -0,0 +1,666 @@
|
||||
<template>
|
||||
<div class="app-container count-container">
|
||||
<DragResizePanel :initialSize="280" :minSize="280" :maxSize="600">
|
||||
<template #panelA>
|
||||
<div class="left-panel">
|
||||
<div class="panel-header">
|
||||
<div class="header-title">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span>维修计划</span>
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="getList" style="margin-left:4px;" title="刷新列表"></el-button>
|
||||
</div>
|
||||
<el-select v-model="queryParams.approvalStatus" placeholder="审批状态" clearable size="mini" @change="handleQuery" class="header-filter">
|
||||
<el-option label="草稿" :value="0" />
|
||||
<el-option label="已驳回" :value="3" />
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="search-row">
|
||||
<el-input v-model="queryParams.planNo" placeholder="搜索计划编号..." clearable prefix-icon="el-icon-search"
|
||||
size="small" @keyup.enter.native="handleQuery" @clear="handleQuery" />
|
||||
<el-button type="primary" size="small" @click="handleAdd">
|
||||
<i class="el-icon-plus"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-loading="loading" class="list-body">
|
||||
<div v-for="item in dataList" :key="item.planId" class="list-item"
|
||||
:class="{ active: currentRow && currentRow.planId === item.planId }" @click="handleRowClick(item)">
|
||||
<div class="item-main">
|
||||
<span class="item-title">{{ item.planNo }}</span>
|
||||
<span class="item-sub">{{ item.planName }}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<el-tag v-if="item.approvalStatus === 0" type="info" size="mini">草稿</el-tag>
|
||||
<el-tag v-else-if="item.approvalStatus === 3" type="danger" size="mini">已驳回</el-tag>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleUpdate(item)"></el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDelete(item)"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataList.length === 0 && !loading" class="list-empty">
|
||||
<i class="el-icon-folder-opened"></i>
|
||||
<span>暂无维修计划数据</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-footer">
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #panelB>
|
||||
<div class="right-panel">
|
||||
<div v-if="!currentRow" class="empty-tip">
|
||||
<i class="el-icon-info"></i>
|
||||
<span>请在左侧列表中选择一条维修计划查看详情</span>
|
||||
</div>
|
||||
<div v-else v-loading="detailLoading" class="detail-content">
|
||||
<div class="doc-header">
|
||||
<div class="doc-header-top">
|
||||
<div class="doc-title-group">
|
||||
<div class="doc-title">{{ currentRow.planNo }}</div>
|
||||
<div class="doc-subtitle">Maintenance Plan</div>
|
||||
</div>
|
||||
<div class="doc-header-right">
|
||||
<el-button size="mini" type="text" icon="el-icon-refresh" @click="handleRefreshDetail" title="刷新详情">刷新</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(currentRow)" v-if="currentRow.approvalStatus !== 2">编辑</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(currentRow)" v-if="currentRow.approvalStatus !== 2">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-status-row">
|
||||
<span class="doc-status-label">Approval / 状态:</span>
|
||||
<el-tag v-if="currentRow.approvalStatus === 0" type="info" size="small">草稿</el-tag>
|
||||
<el-tag v-else-if="currentRow.approvalStatus === 1" size="small">待审批</el-tag>
|
||||
<el-tag v-else-if="currentRow.approvalStatus === 2" type="success" size="small">已审批</el-tag>
|
||||
<el-tag v-else-if="currentRow.approvalStatus === 3" type="danger" size="small">已驳回</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-meta">
|
||||
<span><i class="el-icon-document"></i>{{ currentRow.planName }}</span>
|
||||
<span v-if="currentRow.repairType === 1">定期保养</span>
|
||||
<span v-else-if="currentRow.repairType === 2">安全整改</span>
|
||||
<span v-else-if="currentRow.repairType === 3">专项检修</span>
|
||||
<span v-else-if="currentRow.repairType === 4">故障维修</span>
|
||||
<span v-if="currentRow.priorityLevel === 1"><el-tag size="mini" type="info">普通</el-tag></span>
|
||||
<span v-if="currentRow.priorityLevel === 2"><el-tag size="mini" type="danger">重要</el-tag></span>
|
||||
<span v-if="currentRow.plannedStartTime"><i class="el-icon-time"></i>开始: {{ parseTime(currentRow.plannedStartTime, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.plannedEndTime"><i class="el-icon-time"></i>结束: {{ parseTime(currentRow.plannedEndTime, '{y}-{m}-{d}') }}</span>
|
||||
<span v-if="currentRow.dutyDept"><i class="el-icon-s-home"></i>{{ currentRow.dutyDept }}</span>
|
||||
<span v-if="currentRow.planOwner"><i class="el-icon-user-solid"></i>{{ currentRow.planOwner }}</span>
|
||||
<span v-if="currentRow.budgetAmount"><i class="el-icon-money"></i>预算: ¥{{ currentRow.budgetAmount }}</span>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>计划说明 <span class="en-sub">· Description</span></span>
|
||||
</div>
|
||||
<div class="remark-content">{{ currentRow.planDescription || '无' }}</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">
|
||||
<span>关联异常记录 <span class="en-sub">· Related Abnormal Records</span></span>
|
||||
<el-button v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" size="mini" type="primary" plain icon="el-icon-plus" style="margin-left:8px;" @click="handleSelectAbnormalRecords">选择记录</el-button>
|
||||
</div>
|
||||
<el-table :data="abnormalList" border size="small" style="width:100%" v-loading="abnormalLoading">
|
||||
<el-table-column label="设备部件" align="center" prop="partName" width="140" />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="120" />
|
||||
<el-table-column label="班次" align="center" width="80">
|
||||
<template slot-scope="scope">{{ scope.row.shift == 1 ? '白班' : '夜班' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="巡检时间" align="center" width="150">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d} {h}:{i}') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="异常描述" align="center" prop="abnormalDesc" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="70" v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleRemoveAbnormal(scope.row)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="abnormalList.length === 0 && !abnormalLoading" class="empty-data" style="margin-top:8px;">暂无关联异常记录</div>
|
||||
|
||||
<div class="section-gap" />
|
||||
|
||||
<div class="section-title">
|
||||
<span>维修明细 <span class="en-sub">· Maintenance Details</span></span>
|
||||
<el-button v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" size="mini" type="primary" plain icon="el-icon-plus" style="margin-left:8px;" @click="handleAddDetail">添加</el-button>
|
||||
</div>
|
||||
<el-table :data="detailList" border size="small" style="width:100%" v-loading="detailLoading">
|
||||
<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>
|
||||
<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" v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3">
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="detailList.length === 0 && !detailLoading" class="empty-data" style="margin-top:8px;">暂无维修明细,请关联异常记录后自动生成或手动添加</div>
|
||||
|
||||
<div class="section-gap" />
|
||||
|
||||
<div v-if="currentRow.approvalStatus === 0 || currentRow.approvalStatus === 3" class="form-actions">
|
||||
<el-button type="primary" :loading="submitLoading" icon="el-icon-s-promotion" @click="handleSubmitApproval">提交审批</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</DragResizePanel>
|
||||
|
||||
<!-- 新增/编辑计划弹窗 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="650px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
|
||||
<el-form-item label="计划名称" prop="planName">
|
||||
<el-input v-model="form.planName" placeholder="请输入计划名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="维修类型" prop="repairType">
|
||||
<el-select v-model="form.repairType" placeholder="请选择" style="width:100%">
|
||||
<el-option label="定期保养" :value="1" />
|
||||
<el-option label="安全整改" :value="2" />
|
||||
<el-option label="专项检修" :value="3" />
|
||||
<el-option label="故障维修" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="优先级" prop="priorityLevel">
|
||||
<el-select v-model="form.priorityLevel" placeholder="请选择" style="width:100%">
|
||||
<el-option label="普通" :value="1" />
|
||||
<el-option label="重要" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划开始时间" prop="plannedStartTime">
|
||||
<el-date-picker clearable v-model="form.plannedStartTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划结束时间" prop="plannedEndTime">
|
||||
<el-date-picker clearable v-model="form.plannedEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="负责部门" prop="dutyDept">
|
||||
<el-input v-model="form.dutyDept" placeholder="请输入负责部门" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划负责人" prop="planOwner">
|
||||
<el-input v-model="form.planOwner" placeholder="请输入负责人" />
|
||||
</el-form-item>
|
||||
<el-form-item label="预算金额(元)" prop="budgetAmount">
|
||||
<el-input-number v-model="form.budgetAmount" :min="0" :precision="2" style="width:100%" placeholder="请输入预算金额" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划说明" prop="planDescription">
|
||||
<el-input v-model="form.planDescription" type="textarea" :rows="3" placeholder="请输入计划说明" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button :loading="buttonLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 选择异常巡检记录弹窗 -->
|
||||
<el-dialog title="选择异常巡检记录" :visible.sync="abnormalSelectorVisible" width="800px" append-to-body>
|
||||
<el-form :model="abnormalQuery" size="small" :inline="true" style="margin-bottom:12px;">
|
||||
<el-form-item label="产线">
|
||||
<el-select v-model="abnormalQuery.productionLine" placeholder="请选择" clearable @change="loadAbnormalRecords" style="width:140px;">
|
||||
<el-option v-for="item in lineList" :key="item.lineId" :label="item.lineName" :value="item.lineId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="巡检人">
|
||||
<el-input v-model="abnormalQuery.inspector" placeholder="搜索" clearable style="width:120px;" @keyup.enter.native="loadAbnormalRecords" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="mini" @click="loadAbnormalRecords">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table ref="abnormalTable" :data="abnormalRecordList" border size="small" @selection-change="handleAbnormalSelectionChange" max-height="400">
|
||||
<el-table-column type="selection" width="45" />
|
||||
<el-table-column label="设备部件" align="center" prop="partName" width="130" />
|
||||
<el-table-column label="产线" align="center" prop="productionLine" width="110" />
|
||||
<el-table-column label="班次" align="center" width="70">
|
||||
<template slot-scope="scope">{{ scope.row.shift == 1 ? '白班' : '夜班' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="巡检时间" align="center" prop="inspectTime" width="150">
|
||||
<template slot-scope="scope">{{ parseTime(scope.row.inspectTime, '{y}-{m}-{d} {h}:{i}') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="异常描述" align="center" prop="abnormalDesc" min-width="160" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="abnormalSelectorVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmAbnormalSelection">确 定(已选 {{ abnormalSelectedRecords.length }} 条)</el-button>
|
||||
</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>
|
||||
|
||||
<script>
|
||||
import { listMaintenancePlan, getMaintenancePlan, addMaintenancePlan, updateMaintenancePlan, delMaintenancePlan } from "@/api/flow/maintenancePlan";
|
||||
import { listMaintenancePlanDetail, addMaintenancePlanDetail, updateMaintenancePlanDetail, delMaintenancePlanDetail } from "@/api/flow/maintenancePlanDetail";
|
||||
import { listMaintenancePlanAbnormal, addMaintenancePlanAbnormal, delMaintenancePlanAbnormal } from "@/api/flow/maintenancePlanAbnormal";
|
||||
import { listEquipmentInspectionRecord } from "@/api/mes/eqp/equipmentInspectionRecord";
|
||||
import { listProductionLine } from "@/api/wms/productionLine";
|
||||
import DragResizePanel from "@/components/DragResizePanel/index.vue";
|
||||
import { parseTime } from '@/utils/klp'
|
||||
|
||||
export default {
|
||||
name: "EqpMaintenance",
|
||||
components: { DragResizePanel },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
detailLoading: false,
|
||||
buttonLoading: false,
|
||||
submitLoading: false,
|
||||
abnormalLoading: false,
|
||||
detailButtonLoading: false,
|
||||
total: 0,
|
||||
dataList: [],
|
||||
currentRow: null,
|
||||
title: "",
|
||||
open: false,
|
||||
form: {},
|
||||
rules: {
|
||||
planName: [{ required: true, message: "请输入计划名称", trigger: "blur" }],
|
||||
repairType: [{ required: true, message: "请选择维修类型", trigger: "change" }]
|
||||
},
|
||||
queryParams: { pageNum: 1, pageSize: 10, planNo: undefined, approvalStatus: undefined },
|
||||
// Abnormal records
|
||||
abnormalList: [],
|
||||
abnormalSelectorVisible: false,
|
||||
abnormalQuery: { productionLine: undefined, inspector: undefined },
|
||||
abnormalRecordList: [],
|
||||
abnormalSelectedRecords: [],
|
||||
lineList: [],
|
||||
// Details
|
||||
detailList: [],
|
||||
detailDialogVisible: false,
|
||||
detailDialogTitle: "",
|
||||
detailForm: {},
|
||||
editingDetailId: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.loadLineList();
|
||||
},
|
||||
methods: {
|
||||
parseTime,
|
||||
async loadLineList() {
|
||||
try {
|
||||
const res = await listProductionLine({ pageSize: 999 });
|
||||
if (res.rows) this.lineList = res.rows;
|
||||
this.getList();
|
||||
} catch (e) { console.error('加载产线列表失败', e); }
|
||||
},
|
||||
getList() {
|
||||
this.loading = true;
|
||||
var self = this;
|
||||
listMaintenancePlan(this.queryParams).then(function(response) {
|
||||
self.dataList = response.rows;
|
||||
self.total = response.total;
|
||||
self.loading = false;
|
||||
});
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.getList();
|
||||
},
|
||||
handleRowClick(row) {
|
||||
this.currentRow = row;
|
||||
this.loadDetail(row.planId);
|
||||
},
|
||||
loadDetail(planId) {
|
||||
this.detailLoading = true;
|
||||
var self = this;
|
||||
getMaintenancePlan(planId).then(function(response) {
|
||||
self.currentRow = response.data;
|
||||
self.loadAbnormalList(planId);
|
||||
self.loadDetailList(planId);
|
||||
}).finally(function() { self.detailLoading = false; });
|
||||
},
|
||||
loadAbnormalList(planId) {
|
||||
this.abnormalLoading = true;
|
||||
var self = this;
|
||||
listMaintenancePlanAbnormal({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
var rows = r.rows || [];
|
||||
// Enrich with inspection record info — query each record
|
||||
self.abnormalList = rows;
|
||||
self.abnormalLoading = false;
|
||||
}).catch(function() { self.abnormalLoading = false; });
|
||||
},
|
||||
loadDetailList(planId) {
|
||||
var self = this;
|
||||
listMaintenancePlanDetail({ planId: planId, pageNum: 1, pageSize: 999 }).then(function(r) {
|
||||
self.detailList = r.rows || [];
|
||||
});
|
||||
},
|
||||
handleRefreshDetail() {
|
||||
if (this.currentRow && this.currentRow.planId) {
|
||||
this.loadDetail(this.currentRow.planId);
|
||||
}
|
||||
},
|
||||
// ---- Plan CRUD ----
|
||||
handleAdd() {
|
||||
this.reset();
|
||||
this.open = true;
|
||||
this.title = "新增维修计划";
|
||||
},
|
||||
handleUpdate(row) {
|
||||
this.reset();
|
||||
var self = this;
|
||||
getMaintenancePlan(row.planId).then(function(response) {
|
||||
self.form = response.data;
|
||||
self.open = true;
|
||||
self.title = "修改维修计划";
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
this.form = {
|
||||
planId: undefined, planNo: undefined, planName: undefined,
|
||||
repairType: undefined, priorityLevel: 1,
|
||||
plannedStartTime: undefined, plannedEndTime: undefined,
|
||||
dutyDept: undefined, planOwner: undefined,
|
||||
budgetAmount: undefined, planDescription: undefined,
|
||||
approvalStatus: 0, remark: undefined
|
||||
};
|
||||
this.resetForm("form");
|
||||
},
|
||||
cancel() {
|
||||
this.open = false;
|
||||
this.reset();
|
||||
},
|
||||
submitForm() {
|
||||
var self = this;
|
||||
this.$refs["form"].validate(function(valid) {
|
||||
if (valid) {
|
||||
self.buttonLoading = true;
|
||||
self.form.planNo = self.form.planNo || self.form.planName;
|
||||
if (self.form.planId != null) {
|
||||
updateMaintenancePlan(self.form).then(function() {
|
||||
self.$modal.msgSuccess("修改成功");
|
||||
self.open = false;
|
||||
self.getList();
|
||||
if (self.currentRow && self.currentRow.planId === self.form.planId) {
|
||||
self.loadDetail(self.currentRow.planId);
|
||||
}
|
||||
}).finally(function() { self.buttonLoading = false; });
|
||||
} else {
|
||||
addMaintenancePlan(self.form).then(function(response) {
|
||||
self.$modal.msgSuccess("新增成功");
|
||||
self.open = false;
|
||||
self.getList();
|
||||
}).finally(function() { self.buttonLoading = false; });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDelete(row) {
|
||||
var self = this;
|
||||
this.$modal.confirm('是否确认删除维修计划"' + row.planNo + '"?').then(function() {
|
||||
self.loading = true;
|
||||
return delMaintenancePlan(row.planId);
|
||||
}).then(function() {
|
||||
self.loading = false;
|
||||
self.getList();
|
||||
if (self.currentRow && self.currentRow.planId === row.planId) {
|
||||
self.currentRow = null;
|
||||
self.abnormalList = [];
|
||||
self.detailList = [];
|
||||
}
|
||||
self.$modal.msgSuccess("删除成功");
|
||||
}).catch(function() { }).finally(function() { self.loading = false; });
|
||||
},
|
||||
// ---- Submit for approval ----
|
||||
handleSubmitApproval() {
|
||||
var self = this;
|
||||
if (this.detailList.length === 0) {
|
||||
this.$modal.msgWarning("请至少添加一条维修明细");
|
||||
return;
|
||||
}
|
||||
this.$modal.confirm('确认提交维修计划"' + this.currentRow.planNo + '"进行审批?').then(function() {
|
||||
self.submitLoading = true;
|
||||
return updateMaintenancePlan({ planId: self.currentRow.planId, approvalStatus: 1 });
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("提交审批成功");
|
||||
self.submitLoading = false;
|
||||
self.loadDetail(self.currentRow.planId);
|
||||
self.getList();
|
||||
}).catch(function() { self.submitLoading = false; });
|
||||
},
|
||||
// ---- Abnormal records selection ----
|
||||
handleSelectAbnormalRecords() {
|
||||
this.abnormalSelectedRecords = [];
|
||||
this.abnormalQuery = { productionLine: undefined, inspector: undefined };
|
||||
this.loadAbnormalRecords();
|
||||
this.abnormalSelectorVisible = true;
|
||||
},
|
||||
loadAbnormalRecords() {
|
||||
var self = this;
|
||||
var params = {
|
||||
pageNum: 1, pageSize: 999,
|
||||
productionLine: this.abnormalQuery.productionLine,
|
||||
inspector: this.abnormalQuery.inspector,
|
||||
runStatus: 0 // 异常记录
|
||||
};
|
||||
listEquipmentInspectionRecord(params).then(function(response) {
|
||||
self.abnormalRecordList = response.rows || [];
|
||||
});
|
||||
},
|
||||
handleAbnormalSelectionChange(selection) {
|
||||
this.abnormalSelectedRecords = selection;
|
||||
},
|
||||
confirmAbnormalSelection() {
|
||||
if (this.abnormalSelectedRecords.length === 0) {
|
||||
this.$modal.msgWarning("请至少选择一条异常记录");
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var planId = this.currentRow.planId;
|
||||
var addPromises = this.abnormalSelectedRecords.map(function(rec) {
|
||||
return addMaintenancePlanAbnormal({ planId: planId, recordId: rec.recordId });
|
||||
});
|
||||
// Also auto-create detail rows from selected records
|
||||
this.$modal.loading("正在关联异常记录并生成维修明细...");
|
||||
Promise.all(addPromises).then(function() {
|
||||
// Create detail rows
|
||||
var detailPromises = self.abnormalSelectedRecords.map(function(rec) {
|
||||
return addMaintenancePlanDetail({
|
||||
planId: planId,
|
||||
recordId: rec.recordId,
|
||||
componentName: rec.partName || '',
|
||||
productionLine: rec.productionLine || '',
|
||||
maintenanceCategory: 1,
|
||||
detailStatus: 0
|
||||
});
|
||||
});
|
||||
return Promise.all(detailPromises);
|
||||
}).then(function() {
|
||||
self.$modal.closeLoading();
|
||||
self.$modal.msgSuccess("关联成功,已生成维修明细");
|
||||
self.abnormalSelectorVisible = false;
|
||||
self.loadAbnormalList(planId);
|
||||
self.loadDetailList(planId);
|
||||
}).catch(function() {
|
||||
self.$modal.closeLoading();
|
||||
self.$modal.msgError("操作失败");
|
||||
});
|
||||
},
|
||||
handleRemoveAbnormal(row) {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认移除该异常记录关联?').then(function() {
|
||||
return delMaintenancePlanAbnormal(row.relId);
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("移除成功");
|
||||
self.loadAbnormalList(self.currentRow.planId);
|
||||
}).catch(function() { });
|
||||
},
|
||||
// ---- Detail management ----
|
||||
handleAddDetail() {
|
||||
this.detailForm = {
|
||||
planId: this.currentRow.planId,
|
||||
componentName: '', 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("请输入设备部件名称");
|
||||
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; });
|
||||
}
|
||||
},
|
||||
handleDeleteDetail(row) {
|
||||
var self = this;
|
||||
this.$modal.confirm('确认删除该维修明细?').then(function() {
|
||||
return delMaintenancePlanDetail(row.detailId);
|
||||
}).then(function() {
|
||||
self.$modal.msgSuccess("删除成功");
|
||||
self.loadDetailList(self.currentRow.planId);
|
||||
}).catch(function() { });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.count-container { height: calc(100vh - 84px); }
|
||||
|
||||
/* ========== 左侧面板 ========== */
|
||||
.left-panel { display: flex; flex-direction: column; height: 100%; background: #f5f7fa; border-right: 1px solid #e4e7ed; }
|
||||
.panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px 8px; background: #f5f7fa; }
|
||||
.header-title { display: flex; align-items: center; gap: 6px; font-size: 14px; font-weight: 600; color: #303133; }
|
||||
.header-title i { color: #409eff; font-size: 16px; }
|
||||
.header-filter { width: 120px; }
|
||||
.search-row { display: flex; align-items: center; gap: 6px; padding: 0 14px 10px; background: #f5f7fa; }
|
||||
.list-body { flex: 1; overflow-y: auto; padding: 0 6px; }
|
||||
.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; }
|
||||
.list-item.active .item-title { color: #409eff; font-weight: 600; }
|
||||
.item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
||||
.item-title { font-size: 13px; font-weight: 500; color: #303133; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.item-sub { font-size: 12px; color: #909399; }
|
||||
.item-meta { flex-shrink: 0; margin: 0 8px; }
|
||||
.item-actions { flex-shrink: 0; opacity: 0; transition: opacity 0.15s; }
|
||||
.list-item:hover .item-actions { opacity: 1; }
|
||||
.list-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 60px 0; color: #c0c4cc; font-size: 13px; gap: 8px; }
|
||||
.list-empty i { font-size: 32px; }
|
||||
.list-footer { border-top: 1px solid #e4e7ed; padding: 2px 8px 0; background: #f5f7fa; }
|
||||
|
||||
/* ========== 右侧面板 — Word 文档风格 ========== */
|
||||
.right-panel { height: 100%; overflow-y: auto; padding: 12px 16px; background: #faf8f5; }
|
||||
.right-panel .detail-content { margin: 0 auto; background: #ffffff; padding: 28px 32px 36px; box-shadow: 0 1px 4px rgba(0,0,0,0.06), 0 2px 12px rgba(0,0,0,0.04); min-height: 100%; }
|
||||
.empty-tip { display: flex; align-items: center; justify-content: center; height: 100%; color: #909399; font-size: 14px; gap: 8px; }
|
||||
|
||||
.doc-header { margin-bottom: 18px; padding-bottom: 14px; border-bottom: 2px solid #1a3c6e; }
|
||||
.doc-header-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
|
||||
.doc-title-group { flex: 1; min-width: 0; }
|
||||
.doc-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; font-size: 24px; font-weight: 700; color: #1a1a1a; line-height: 1.3; letter-spacing: 0.5px; }
|
||||
.doc-subtitle { font-family: 'Georgia', 'Times New Roman', serif; font-size: 12px; font-weight: 400; color: #8c8c8c; font-style: italic; letter-spacing: 0.8px; margin-top: 2px; }
|
||||
.doc-header-right { flex-shrink: 0; }
|
||||
.doc-status-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
|
||||
.doc-status-label { font-family: 'Georgia', 'Times New Roman', serif; font-size: 11px; color: #8c8c8c; letter-spacing: 0.3px; }
|
||||
|
||||
.detail-meta { display: flex; flex-wrap: wrap; gap: 16px; font-size: 12px; color: #909399; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid #e0dcd6; }
|
||||
.detail-meta span { display: inline-flex; align-items: center; gap: 4px; }
|
||||
.detail-meta i { font-size: 13px; }
|
||||
|
||||
.section-title { font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif; width: 100%; font-size: 15px; font-weight: 700; color: #1a1a1a; margin: 22px 0 12px 0; padding: 0 0 10px 0; border-bottom: 1px solid #d4d0c8; display: flex; align-items: center; gap: 10px; letter-spacing: 0.3px; }
|
||||
.section-title:first-child { margin-top: 0; }
|
||||
.section-title .en-sub { font-size: 11px; font-weight: 400; color: #8c8c8c; letter-spacing: 0.5px; font-family: 'Georgia', 'Times New Roman', serif; font-style: italic; }
|
||||
.section-title i { font-size: 16px; color: #1a3c6e; }
|
||||
|
||||
.remark-content { padding: 12px 16px; background: #faf8f5; border: 1px solid #e8e4de; border-radius: 2px; font-size: 13px; line-height: 1.8; color: #1a1a1a; }
|
||||
.section-gap { height: 16px; }
|
||||
.empty-data { color: #8c8c8c; font-size: 13px; font-style: italic; }
|
||||
|
||||
.form-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 16px 0; border-top: 1px solid #e0dcd6; margin-top: 8px; }
|
||||
|
||||
.right-panel .el-table { border: 1px solid #e8e4de !important; border-radius: 2px !important; font-size: 12px !important; }
|
||||
.right-panel .el-table thead th { background-color: #2c3e50 !important; color: #ffffff !important; font-weight: 600 !important; font-size: 11px !important; letter-spacing: 0.5px !important; border-bottom: none !important; font-family: 'Georgia', 'Times New Roman', serif; }
|
||||
.right-panel .el-table thead th .cell { color: #ffffff !important; }
|
||||
.right-panel .el-table__body tr:hover > td { background-color: #f7f5f0 !important; }
|
||||
.right-panel .el-table--border td { border-right: 1px solid #f0ece6 !important; }
|
||||
.right-panel .el-table--border th { border-right: 1px solid #3a5166 !important; }
|
||||
.right-panel .el-table td { padding: 6px 4px !important; color: #3a3a3a !important; }
|
||||
.right-panel .el-divider--horizontal { margin: 8px 0 4px; background-color: #e0dcd6; }
|
||||
.right-panel .el-tag { border-radius: 2px; font-family: 'Georgia', 'Times New Roman', serif; letter-spacing: 0.3px; }
|
||||
.right-panel .el-tag--mini { padding: 0 6px; line-height: 20px; height: 20px; }
|
||||
.right-panel .el-tag--small { padding: 0 8px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user