迁移物料监控模块
Some checks failed
Release pipeline / publish (push) Has been cancelled
Release pipeline / Always run job (push) Has been cancelled

- 新增计划与生产物料监控 V2 页面

- 新增 WIP 物料监控接口、旧路径路由和中英文文案

- 更新迁移任务列表中的物料监控状态
This commit is contained in:
sheng
2026-06-22 16:12:59 +08:00
parent 6bb2d1285c
commit 9863bf1113
6 changed files with 457 additions and 3 deletions

View File

@@ -3,8 +3,8 @@
> 根据 `后台Webman界面截图对照表.md` 生成。状态以当前 V2 项目中已落地的页面目录为准。
- 总功能数79
- 已迁移25
- 未迁移54
- 已迁移26
- 未迁移53
| 状态 | 一级模块 | 二级模块 | 三级模块 | 功能说明 | V2 目标路径 |
|:---:|---|---|---|---|---|
@@ -47,7 +47,7 @@
| ✅ | 计划与生产 (Planning & Production) | 生产批次管理 (Batch Management) | 批次托盘 (Tray Tracking) | 托盘跟踪与操作 | `src/views/planning-production/batch-management/tray-tracking/` |
| ✅ | 计划与生产 (Planning & Production) | 生产批次管理 (Batch Management) | 生产批次不良报表 (Batch Defect Report) | 不良报表 | `src/views/planning-production/batch-management/batch-defect-report/` |
| ✅ | 计划与生产 (Planning & Production) | 预警中心 (Alert Center) | 预警中心 (Alert Center) | 预警中心 | `src/views/planning-production/alert-center/` |
| | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 物料监控 (Material Monitoring) | 物料监控 | 待确认 |
| | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 物料监控 (Material Monitoring) | 物料监控 | `src/views/planning-production/production-monitoring/material-monitoring/` |
| ⬜ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 电池复投管理 (Rework Management) | 返工管理 | 待确认 |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 托盘管理 (Tray Management) | 托盘管理 | `src/views/planning-production/production-monitoring/tray-management/` |
| ✅ | 计划与生产 (Planning & Production) | 生产监控 (Production Monitoring) | 托盘登录 (Tray Registration) | 托盘登记 | `src/views/planning-production/production-monitoring/tray-registration/` |

View File

@@ -0,0 +1,27 @@
import { request } from '@/api/_service'
const BASE = 'planning_production/wip/wip_management/'
function apiParams (method, data = {}) {
return {
method,
platform: 'background',
...data
}
}
export function getWipDataList (data) {
return request({
url: BASE + 'list',
method: 'get',
params: apiParams('planning_production_wip_wip_management_list', data)
})
}
export function createWipData (data) {
return request({
url: BASE + 'create',
method: 'post',
data: apiParams('planning_production_wip_wip_management_create', data)
})
}

View File

@@ -1077,6 +1077,40 @@
"operation_success": "Operation successful",
"operation_failed": "Operation failed",
"format_rule": "Format rule: fixed characters are literal; use * for variable characters, e.g. A** or ******."
},
"material_monitoring": {
"query": "Search",
"reset": "Reset",
"batch_no": "Batch Number",
"workingsubclass": "Process Unit",
"semi_finished_id": "Semi-finished ID",
"semi_product_code": "Semi-finished Product Code",
"output_quantity": "Output Quantity",
"production_device": "Production Equipment",
"last_status": "Material Status",
"finish_time": "Output Time",
"finished": "Completed",
"status_unused": "Unused",
"input_semi_product_data": "Enter Semi-finished Data",
"select_batch_id": "Please select production batch",
"batch_id": "Production Batch",
"select_workingsubclass": "Please select process unit",
"select_device_code": "Please select equipment code",
"device_code": "Device Code",
"enter_output_quantity": "Please enter output quantity",
"output_date": "Output Date",
"verify_failed": "Validation failed",
"operation_success": "Operation successful",
"confirm_operation": "Are you sure you want to proceed?",
"prompt": "Notice",
"confirm": "Confirm",
"cancel": "Cancel",
"select_process_flow": "Please select process flow",
"edit_batch": "Edit Batch",
"enter_batch_no": "Enter batch number",
"enter_semi_finished_id": "Enter semi-finished ID",
"start_time": "Start Time",
"end_time": "End Time"
}
},
"alert_center": {

View File

@@ -1077,6 +1077,40 @@
"operation_success": "操作成功",
"operation_failed": "操作失败",
"format_rule": "格式规则:固定字符直接填写,可变字符使用 * 代替,例如 A** 或 ******。"
},
"material_monitoring": {
"query": "查询",
"reset": "重置",
"batch_no": "批次号",
"workingsubclass": "工序单元",
"semi_finished_id": "半成品ID",
"semi_product_code": "半成品编码",
"output_quantity": "产出数量",
"production_device": "生产设备",
"last_status": "物料状态",
"finish_time": "下料时间",
"finished": "已完成",
"status_unused": "未使用",
"input_semi_product_data": "录入半成品数据",
"select_batch_id": "请选择生产批次",
"batch_id": "生产批次",
"select_workingsubclass": "请选择工序单元",
"select_device_code": "请选择生产设备编码",
"device_code": "设备编码",
"enter_output_quantity": "请输入产出数量",
"output_date": "产出日期",
"verify_failed": "校验失败",
"operation_success": "操作成功",
"confirm_operation": "确定要执行该操作吗?",
"prompt": "提示",
"confirm": "确定",
"cancel": "取消",
"select_process_flow": "请选择工艺流程",
"edit_batch": "编辑批次",
"enter_batch_no": "请输入批次号",
"enter_semi_finished_id": "请输入半成品ID",
"start_time": "开始时间",
"end_time": "结束时间"
}
},
"alert_center": {

View File

@@ -32,6 +32,12 @@ export default {
meta: { ...meta, cache: true, title: '生产批次不良报表' },
component: _import('planning-production/batch-management/batch-defect-report')
},
{
path: 'wip/wip_management/mock',
name: `${pre}wip-wip_management-mock`,
meta: { ...meta, cache: true, title: '物料监控' },
component: _import('planning-production/production-monitoring/material-monitoring')
},
{
path: 'produce/report/pin_check',
name: `${pre}report-pin_check`,

View File

@@ -0,0 +1,353 @@
<template>
<d2-container>
<template #header>
<div class="search-bar">
<el-form ref="searchForm" :inline="true" :model="search" size="mini">
<el-form-item :label="$t(key('batch_no'))" prop="batch">
<el-input
v-model="search.batch"
:placeholder="$t(key('enter_batch_no'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item :label="$t(key('semi_finished_id'))" prop="item_id">
<el-input
v-model="search.item_id"
:placeholder="$t(key('enter_semi_finished_id'))"
clearable
style="width:200px"
@keyup.enter.native="onSearch"
/>
</el-form-item>
<el-form-item :label="$t(key('workingsubclass'))" prop="workingsubclass">
<el-select
v-model="search.workingsubclass"
:placeholder="$t(key('select_workingsubclass'))"
clearable
filterable
style="width:200px"
@focus="loadBatchOptions"
>
<el-option
v-for="item in processOptions"
:key="item.code || item.name"
:label="item.name || item.code"
:value="item.code || item.name"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t(key('finish_time'))" prop="start_time">
<el-date-picker
v-model="search.start_time"
type="datetimerange"
range-separator="-"
:start-placeholder="$t(key('start_time'))"
:end-placeholder="$t(key('end_time'))"
value-format="yyyy-MM-dd HH:mm:ss"
style="width:330px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" :disabled="loading" @click="onSearch">
{{ $t(key('query')) }}
</el-button>
<el-button icon="el-icon-refresh" :disabled="loading" @click="onReset">
{{ $t(key('reset')) }}
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<page-table
:columns="columns"
:data="tableData"
:loading="loading"
:toolbar-buttons="toolbarButtons"
:row-buttons="[]"
:pagination="pagination"
:table-attrs="{ size: 'mini', rowKey: 'id', highlightCurrentRow: true }"
auto-height
@page-change="onPageChange"
>
<template #col-last_status="{ row }">
<el-tag :type="Number(row.last_status) === 0 ? 'danger' : 'success'" size="mini">
{{ Number(row.last_status) === 0 ? $t(key('finished')) : $t(key('status_unused')) }}
</el-tag>
</template>
<template #empty>
<el-empty :description="$t('暂无数据')" :image-size="80" />
</template>
</page-table>
<el-dialog :title="$t(key('input_semi_product_data'))" :visible.sync="dialogVisible" width="520px">
<el-form ref="form" :model="form" :rules="rules" label-width="130px" size="mini">
<el-form-item :label="$t(key('batch_id'))" prop="batch_id">
<el-select
v-model="form.batch_id"
:placeholder="$t(key('select_batch_id'))"
clearable
filterable
style="width:100%"
@focus="loadBatchOptions"
@change="onBatchChange"
>
<el-option
v-for="item in batchOptions"
:key="item.id"
:label="item.batch"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t(key('workingsubclass'))" prop="process_code">
<el-select
v-model="form.process_code"
:placeholder="$t(key('select_workingsubclass'))"
clearable
filterable
style="width:100%"
:disabled="!form.batch_id"
>
<el-option
v-for="item in selectedBatchProcesses"
:key="item.code || item.name"
:label="item.name || item.code"
:value="item.code || item.name"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t(key('device_code'))" prop="device_code">
<el-select
v-model="form.device_code"
:placeholder="$t(key('select_device_code'))"
clearable
filterable
style="width:100%"
@focus="loadDeviceOptions"
>
<el-option
v-for="item in deviceOptions"
:key="item.code || item.device_code"
:label="item.name || item.device_name || item.code || item.device_code"
:value="item.code || item.device_code"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t(key('output_quantity'))" prop="item_quantity">
<el-input-number v-model="form.item_quantity" :min="1" style="width:100%" />
</el-form-item>
<el-form-item :label="$t(key('output_date'))" prop="start_time">
<el-date-picker
v-model="form.start_time"
type="date"
value-format="yyyy-MM-dd"
style="width:100%"
:placeholder="$t(key('output_date'))"
/>
</el-form-item>
</el-form>
<span slot="footer">
<el-button size="mini" @click="dialogVisible = false">{{ $t(key('cancel')) }}</el-button>
<el-button size="mini" type="primary" :loading="submitting" @click="submitForm">
{{ $t(key('confirm')) }}
</el-button>
</span>
</el-dialog>
</d2-container>
</template>
<script>
import { useTableColumns } from '@/composables/useTableColumns'
import { i18nMixin } from '@/composables/useI18n'
import PageTable from '@/components/page-table'
import { getBatchAll } from '@/api/planning-production/batch-list'
import { getDeviceAll } from '@/api/planning-production/equipment-monitoring'
import { createWipData, getWipDataList } from '@/api/planning-production/material-monitoring'
export default {
name: 'planning-production-material-monitoring',
components: { PageTable },
mixins: [i18nMixin('page.planning_production.production_monitoring.material_monitoring')],
data () {
return {
loading: false,
submitting: false,
dialogVisible: false,
search: {
batch: '',
item_id: '',
workingsubclass: '',
start_time: []
},
tableData: [],
pagination: {
current: 1,
size: 10,
total: 0
},
form: {
batch_id: '',
process_code: '',
device_code: '',
item_quantity: undefined,
start_time: ''
},
batchOptions: [],
deviceOptions: []
}
},
computed: {
columns () {
return useTableColumns([
{ prop: 'batch', label: this.key('batch_no'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'item_id', label: this.key('semi_finished_id'), minWidth: 160, showOverflowTooltip: true },
{ prop: 'item_code', label: this.key('semi_product_code'), minWidth: 170, showOverflowTooltip: true },
{ prop: 'item_quantity', label: this.key('output_quantity'), minWidth: 120 },
{ prop: 'workingsubclass', label: this.key('workingsubclass'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'device_code', label: this.key('production_device'), minWidth: 150, showOverflowTooltip: true },
{ prop: 'last_status', label: this.key('last_status'), width: 120, slot: 'last_status' },
{ prop: 'finish_time', label: this.key('finish_time'), minWidth: 170, showOverflowTooltip: true }
], {
selectionWidth: 0,
indexWidth: 55
})
},
toolbarButtons () {
return [
{
key: 'create',
label: this.key('input_semi_product_data'),
type: 'primary',
icon: 'el-icon-plus',
size: 'mini',
onClick: this.openDialog
}
]
},
rules () {
return {
batch_id: [{ required: true, message: this.$t(this.key('select_batch_id')), trigger: 'change' }],
process_code: [{ required: true, message: this.$t(this.key('select_workingsubclass')), trigger: 'change' }],
device_code: [{ required: true, message: this.$t(this.key('select_device_code')), trigger: 'change' }],
item_quantity: [{ required: true, message: this.$t(this.key('enter_output_quantity')), trigger: 'blur' }]
}
},
selectedBatch () {
return this.batchOptions.find(item => String(item.id) === String(this.form.batch_id)) || {}
},
selectedBatchProcesses () {
return Array.isArray(this.selectedBatch.process) ? this.selectedBatch.process : []
},
processOptions () {
const map = new Map()
this.batchOptions.forEach(batch => {
const processes = Array.isArray(batch.process) ? batch.process : []
processes.forEach(item => {
const value = item.code || item.name
if (value && !map.has(value)) map.set(value, item)
})
})
return Array.from(map.values())
}
},
created () {
this.fetchData()
},
methods: {
responseData (res) {
return res && res.data ? res.data : (res || {})
},
searchParams () {
const params = {
batch: this.search.batch || undefined,
item_id: this.search.item_id || undefined,
workingsubclass: this.search.workingsubclass || undefined
}
if (Array.isArray(this.search.start_time) && this.search.start_time.length === 2) {
params.start_time = this.search.start_time[0]
params.end_time = this.search.start_time[1]
}
return params
},
async fetchData () {
this.loading = true
try {
const res = await getWipDataList({
...this.searchParams(),
page_no: this.pagination.current,
page_size: this.pagination.size
})
const payload = this.responseData(res)
this.tableData = Array.isArray(payload.data) ? payload.data : []
this.pagination.total = Number(payload.count || 0)
} finally {
this.loading = false
}
},
onSearch () {
this.pagination.current = 1
this.fetchData()
},
onReset () {
this.search = {
batch: '',
item_id: '',
workingsubclass: '',
start_time: []
}
this.onSearch()
},
onPageChange (pagination) {
this.pagination = pagination
this.fetchData()
},
openDialog () {
this.dialogVisible = true
this.form = {
batch_id: '',
process_code: '',
device_code: '',
item_quantity: undefined,
start_time: ''
}
this.$nextTick(() => {
if (this.$refs.form) this.$refs.form.clearValidate()
})
this.loadBatchOptions()
this.loadDeviceOptions()
},
async loadBatchOptions () {
if (this.batchOptions.length) return
const res = await getBatchAll({})
const data = Array.isArray(res) ? res : (res && res.data) || []
this.batchOptions = Array.isArray(data) ? data : []
},
async loadDeviceOptions () {
if (this.deviceOptions.length) return
const res = await getDeviceAll({})
const payload = this.responseData(res)
this.deviceOptions = Array.isArray(payload.data) ? payload.data : []
},
onBatchChange () {
this.form.process_code = ''
},
submitForm () {
this.$refs.form.validate(async valid => {
if (!valid) return
this.submitting = true
try {
await createWipData({ ...this.form })
this.$message.success(this.$t(this.key('operation_success')))
this.dialogVisible = false
this.fetchData()
} finally {
this.submitting = false
}
})
}
}
}
</script>