refactor: 重构产线工位与备料流程,移除冗余字段
1. 移除工单、工件、日排产中的冗余工位组合/接驳台字段 2. 新增工位接驳台编码字段与唯一约束,关联WMS接驳台主数据 3. 重构日排产为工位产量分配模式,替代原接驳台列表 4. 新增备料单工位级字段与分批出库支持 5. 移除定时同步可生产数量,改为WMS实时拉取接口 6. 过滤操作日志,排除登录/退出相关日志 7. 调整仪表盘工序展示逻辑为今日派工路线 8. 新增接驳台维护菜单与基础数据
This commit is contained in:
@@ -213,6 +213,20 @@ export const helpZone = {
|
||||
]
|
||||
}
|
||||
|
||||
export const helpDock = {
|
||||
title: '接驳台维护',
|
||||
overview:
|
||||
'维护全厂接驳台(AGV 取送货的物理停靠位)主数据。接驳台是独立主数据,不依附工位:除了产线各工位旁,库房收货口、线边缓存区等其他位置也有接驳台位。\n\n三种类型:\n· 产线:位于产线工位旁,必须绑定一个工位号,且与工位一一对应(一个工位只能有一个接驳台、一个接驳台只能给一个工位);\n· 库房:库房收货/发货口(如 DOCK21),不绑定工位;\n· 其他:缓存区、暂存位等,不绑定工位。\n\n接驳台编码是产线与 AGV 任务的引用锚点,创建后不可修改;MES「关联工位」页绑定接驳台时从本页数据实时拉取。有托盘占用的接驳台不允许删除,需先处理托盘。',
|
||||
fields: [
|
||||
{ name: '接驳台编码', source: '手工填写,创建后不可改。', purpose: '接驳台唯一编号,产线/AGV 任务据此定位', fill: '如 DOCK21、DOCK22;建议 DOCK+两位数字' },
|
||||
{ name: '接驳台名称', source: '手工填写。', purpose: '接驳台中文名,便于识别', fill: '如 库房收货接驳台、三号缓存接驳台' },
|
||||
{ name: '类型', source: '选择:产线 / 库房 / 其他。', purpose: '区分接驳台所处位置与用途', fill: '产线接驳台需填工位号;库房/其他不填' },
|
||||
{ name: '绑定工位号', source: '仅产线类型填写。', purpose: '该接驳台服务哪个工位,与工位 1:1', fill: '如 3 表示 3 号工位;该工位已绑定其他接驳台时会提示' },
|
||||
{ name: '托盘状态', source: '系统自动维护(PLC 托盘传感器 / AGV 到位卸货)。', purpose: '看该接驳台当前是否被占用', fill: '不可手改;有托盘的接驳台不允许删除' },
|
||||
{ name: '状态', source: '选择:启用 / 停用。', purpose: '停用的接驳台不再参与配送与下拉选择', fill: '默认启用' }
|
||||
]
|
||||
}
|
||||
|
||||
export const helpMaterial = {
|
||||
title: '物料档案',
|
||||
overview:
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
HomeFilled, Download, Upload, Search, CircleCheck, List,
|
||||
Box, Tickets, Monitor, EditPen, Fold, Expand, Location, Goods, Key, Operation, Van, Clock, RefreshLeft
|
||||
Box, Tickets, Monitor, EditPen, Fold, Expand, Location, Goods, Key, Operation, Van, Clock, RefreshLeft, Aim
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getUser, getToken, logout, setSession } from '../utils/auth'
|
||||
import request from '../utils/request'
|
||||
@@ -93,7 +93,8 @@ const menuGroups = [
|
||||
{
|
||||
title: '基础资料', icon: Goods, children: [
|
||||
{ path: '/material', title: '物料档案', icon: Goods, code: 'material:view' },
|
||||
{ path: '/zone', title: '区域维护', icon: Location, code: 'zone:view' }
|
||||
{ path: '/zone', title: '区域维护', icon: Location, code: 'zone:view' },
|
||||
{ path: '/dock', title: '接驳台维护', icon: Aim, code: 'dock:manage' }
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<div class="dock-page">
|
||||
<el-card shadow="never">
|
||||
<el-alert type="info" :closable="false" show-icon style="margin-bottom:12px"
|
||||
title="接驳台是独立主数据:除产线工位旁,库房、缓存区等其他位置也有接驳台位。类型分 产线 / 库房 / 其他;只有产线接驳台绑定工位号,且与工位一一对应(一个工位只能有一个接驳台)。库房/其他接驳台不绑定工位。" />
|
||||
|
||||
<el-form inline :model="filter">
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="filter.dockType" clearable style="width:130px" placeholder="全部">
|
||||
<el-option label="产线" value="line" />
|
||||
<el-option label="库房" value="store" />
|
||||
<el-option label="其他" value="other" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input v-model="filter.keyword" clearable style="width:200px" placeholder="接驳台编码/名称" @keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-bottom:12px">
|
||||
<el-button v-if="can('dock:add')" type="primary" @click="openAdd">新增接驳台</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="list" border size="small" v-loading="loading">
|
||||
<el-table-column prop="dockCode" label="接驳台编码" width="140" />
|
||||
<el-table-column prop="name" label="接驳台名称" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="类型" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="tagType(row.dockType)">{{ typeLabel(row.dockType) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="绑定工位" width="110" align="center">
|
||||
<template #default="{ row }">{{ row.stationNo > 0 ? '工位' + row.stationNo : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="托盘状态" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="row.hasPallet ? 'warning' : 'info'">{{ row.hasPallet ? '有托盘' : '空闲' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="palletRef" label="托盘关联" min-width="160" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.palletRef || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" :type="row.status === 'ENABLED' ? 'success' : 'info'">
|
||||
{{ row.status === 'ENABLED' ? '启用' : '停用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" width="170">
|
||||
<template #default="{ row }">{{ fmtTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="can('dock:edit')" size="small" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button v-if="can('dock:delete')" size="small" type="danger" @click="remove(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<el-empty description="暂无接驳台数据" />
|
||||
</template>
|
||||
</el-table>
|
||||
|
||||
<el-pagination v-model:current-page="page.current" v-model:page-size="page.size" :page-sizes="[20, 50, 100]"
|
||||
:total="total" background layout="total, sizes, prev, pager, next, jumper" style="margin-top:12px"
|
||||
@size-change="search" @current-change="load" />
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="dialog.show" :title="dialog.isAdd ? '新增接驳台' : '编辑接驳台'" width="480px">
|
||||
<el-form :model="form" label-width="110px">
|
||||
<el-form-item label="接驳台编码" required>
|
||||
<el-input v-model="form.dockCode" :disabled="!dialog.isAdd" placeholder="如 DOCK22" />
|
||||
<div v-if="!dialog.isAdd" style="color:#909399;font-size:12px;margin-top:4px">编码是产线/AGV 任务的引用锚点,创建后不可修改。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="接驳台名称" required>
|
||||
<el-input v-model="form.name" placeholder="如 三号缓存接驳台" />
|
||||
</el-form-item>
|
||||
<el-form-item label="类型" required>
|
||||
<el-radio-group v-model="form.dockType">
|
||||
<el-radio label="line">产线</el-radio>
|
||||
<el-radio label="store">库房</el-radio>
|
||||
<el-radio label="other">其他</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.dockType === 'line'" label="绑定工位号" required>
|
||||
<el-input-number v-model="form.stationNo" :min="1" :max="999" style="width:100%" />
|
||||
<div style="color:#909399;font-size:12px;margin-top:4px">产线接驳台与工位一一对应;该工位已绑定其他接驳台时会提示。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="ENABLED">启用</el-radio>
|
||||
<el-radio label="DISABLED">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialog.show = false">取消</el-button>
|
||||
<el-button type="primary" :loading="saving" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<PageHelp :data="helpDock" page="Dock" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({ name: 'Dock' })
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { fmtTime } from '../utils/format'
|
||||
import { can } from '../utils/perm'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
import { helpDock } from '../help'
|
||||
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const page = reactive({ current: 1, size: 20 })
|
||||
const filter = reactive({ dockType: '', keyword: '' })
|
||||
|
||||
const dialog = reactive({ show: false, isAdd: true })
|
||||
const form = reactive({ id: 0, dockCode: '', name: '', dockType: 'line', stationNo: 1, status: 'ENABLED' })
|
||||
|
||||
const TYPE_LABEL = { line: '产线', store: '库房', other: '其他' }
|
||||
function typeLabel(t) { return TYPE_LABEL[t] || '其他' }
|
||||
function tagType(t) { return t === 'line' ? 'success' : (t === 'store' ? 'warning' : 'info') }
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = { page: page.current, pageSize: page.size }
|
||||
if (filter.dockType) params.dockType = filter.dockType
|
||||
if (filter.keyword) params.keyword = filter.keyword
|
||||
const data = await request.get('/docks', { params })
|
||||
list.value = data?.list || []
|
||||
total.value = data?.total || 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function search() {
|
||||
page.current = 1
|
||||
load()
|
||||
}
|
||||
function reset() {
|
||||
filter.dockType = ''
|
||||
filter.keyword = ''
|
||||
search()
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
dialog.isAdd = true
|
||||
Object.assign(form, { id: 0, dockCode: '', name: '', dockType: 'line', stationNo: 1, status: 'ENABLED' })
|
||||
dialog.show = true
|
||||
}
|
||||
function openEdit(row) {
|
||||
dialog.isAdd = false
|
||||
Object.assign(form, {
|
||||
id: row.id, dockCode: row.dockCode, name: row.name,
|
||||
dockType: row.dockType || 'line', stationNo: row.stationNo > 0 ? row.stationNo : 1, status: row.status || 'ENABLED'
|
||||
})
|
||||
dialog.show = true
|
||||
}
|
||||
|
||||
async function save() {
|
||||
if (!form.dockCode.trim()) return ElMessage.warning('请填写接驳台编码')
|
||||
if (!form.name.trim()) return ElMessage.warning('请填写接驳台名称')
|
||||
saving.value = true
|
||||
try {
|
||||
const body = {
|
||||
dockCode: form.dockCode.trim(), name: form.name.trim(),
|
||||
dockType: form.dockType,
|
||||
stationNo: form.dockType === 'line' ? form.stationNo : 0,
|
||||
status: form.status
|
||||
}
|
||||
if (dialog.isAdd) {
|
||||
await request.post('/docks', body)
|
||||
ElMessage.success('接驳台已新增')
|
||||
} else {
|
||||
await request.post('/docks/update', { ...body, id: form.id })
|
||||
ElMessage.success('接驳台已保存')
|
||||
}
|
||||
dialog.show = false
|
||||
load()
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认删除接驳台「${row.dockCode} ${row.name}」?`, '删除接驳台', { type: 'warning' })
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await request.post('/docks/delete', { id: row.id })
|
||||
ElMessage.success('接驳台已删除')
|
||||
load()
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -61,8 +61,8 @@ import PageHelp from '../components/PageHelp.vue'
|
||||
import { helpEventLog } from '../help'
|
||||
|
||||
// 事件类型按"族前缀"归类(写点处 eventType 形如 user.create / inbound.create …)
|
||||
// 登录/退出(auth.*)不属于操作日志,后端已排除、本页也不提供该筛选项
|
||||
const EVENT_FAMILIES = [
|
||||
{ label: '登录/退出', value: 'auth.' },
|
||||
{ label: '账号管理', value: 'user.' },
|
||||
{ label: '角色管理', value: 'role.' },
|
||||
{ label: '权限初始化', value: 'rbac.' },
|
||||
@@ -76,12 +76,12 @@ const EVENT_FAMILIES = [
|
||||
]
|
||||
const FAMILY_LABEL = Object.fromEntries(EVENT_FAMILIES.map((f) => [f.value, f.label]))
|
||||
const FAMILY_TAG = {
|
||||
'auth.': 'danger', 'user.': 'primary', 'role.': 'warning', 'rbac.': 'danger',
|
||||
'user.': 'primary', 'role.': 'warning', 'rbac.': 'danger',
|
||||
'material.': 'success', 'zone.': 'success', 'inbound.': 'success',
|
||||
'outbound.': 'warning', 'inspection.': 'info', 'stocktake.': 'info', 'semi.': 'info'
|
||||
}
|
||||
const ENTITY_LABEL = {
|
||||
auth: '登录', user: '账号', role: '角色', rbac: '权限', material: '物料', zone: '区域',
|
||||
user: '账号', role: '角色', rbac: '权限', material: '物料', zone: '区域',
|
||||
inbound: '入库', outbound: '出库', inspection: '检验', stocktake: '盘点', semi: '半成品'
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ const routes = [
|
||||
{ path: 'agv', name: 'Agv', component: () => import('../pages/Agv.vue'), meta: { title: 'AGV配送', keepAlive: true, componentName: 'Agv' } },
|
||||
{ path: 'zone', name: 'Zone', component: () => import('../pages/BaseData.vue'), props: { mode: 'zone' }, meta: { title: '区域维护', keepAlive: true, componentName: 'BaseData' } },
|
||||
{ path: 'material', name: 'Material', component: () => import('../pages/BaseData.vue'), props: { mode: 'material' }, meta: { title: '物料档案', keepAlive: true, componentName: 'BaseData' } },
|
||||
{ path: 'dock', name: 'Dock', component: () => import('../pages/Dock.vue'), meta: { title: '接驳台维护', keepAlive: true, componentName: 'Dock' } },
|
||||
{ path: 'change-password', name: 'ChangePassword', component: () => import('../pages/ChangePassword.vue'), meta: { title: '修改密码' } },
|
||||
{ path: 'users', name: 'UserManage', component: () => import('../pages/UserManage.vue'), meta: { title: '账号管理', keepAlive: true, componentName: 'UserManage' } },
|
||||
{ path: 'roles', name: 'RoleManage', component: () => import('../pages/RoleManage.vue'), meta: { title: '角色管理', keepAlive: true, componentName: 'RoleManage' } },
|
||||
|
||||
Reference in New Issue
Block a user