初始化2
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="工单号">
|
||||
<el-select v-model="orderNo" filterable allow-create default-first-option style="width:220px" @change="load">
|
||||
<el-option v-for="w in workOrders" :key="w.workOrderNo" :label="w.workOrderNo" :value="w.workOrderNo" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<el-button @click="openAdd">添加物料</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="120" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="120" />
|
||||
<el-table-column prop="spec" label="规格" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
<el-table-column prop="manageMode" label="管理方式" width="100">
|
||||
<template #default="{row}">{{ row.manageMode === '1' ? '精密件(SN)' : '结构件(批次)' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="unitQty" label="单台用量" width="90" />
|
||||
<el-table-column prop="lossRate" label="损耗率%" width="90" />
|
||||
<el-table-column prop="requiredQty" label="需求量" width="90" />
|
||||
<el-table-column label="操作" width="90">
|
||||
<template #default="{row}">
|
||||
<el-button size="small" type="danger" @click="remove(row)">移除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="add.show" title="添加BOM物料" width="520px">
|
||||
<el-form :model="add" label-width="90px">
|
||||
<el-form-item label="物料编码"><el-input v-model="add.materialCode" /></el-form-item>
|
||||
<el-form-item label="物料名称"><el-input v-model="add.materialName" /></el-form-item>
|
||||
<el-form-item label="规格"><el-input v-model="add.spec" /></el-form-item>
|
||||
<el-form-item label="单位"><el-input v-model="add.unit" /></el-form-item>
|
||||
<el-form-item label="管理方式">
|
||||
<el-radio-group v-model="add.manageMode">
|
||||
<el-radio label="1">精密件(SN)</el-radio>
|
||||
<el-radio label="2">结构件(批次)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="单台用量"><el-input-number v-model="add.unitQty" :min="0" /></el-form-item>
|
||||
<el-form-item label="损耗率%"><el-input-number v-model="add.lossRate" :min="0" /></el-form-item>
|
||||
<el-form-item label="需求量"><el-input-number v-model="add.requiredQty" :min="0" /></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="add.show=false">取消</el-button>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const orderNo = ref('')
|
||||
const workOrders = ref([])
|
||||
const list = ref([])
|
||||
const add = reactive({ show: false, materialCode: '', materialName: '', spec: '', unit: '', manageMode: '2', unitQty: 1, lossRate: 0, requiredQty: 1 })
|
||||
|
||||
async function load() {
|
||||
if (!orderNo.value) return ElMessage.warning('请选择/输入工单号')
|
||||
list.value = (await request.get('/bom', { params: { workOrderNo: orderNo.value } })) || []
|
||||
}
|
||||
function openAdd() {
|
||||
Object.assign(add, { show: true, materialCode: '', materialName: '', spec: '', unit: '', manageMode: '2', unitQty: 1, lossRate: 0, requiredQty: 1 })
|
||||
}
|
||||
async function save() {
|
||||
await request.put('/bom', { workOrderNo: orderNo.value, items: [add] })
|
||||
ElMessage.success('保存成功')
|
||||
add.show = false
|
||||
load()
|
||||
}
|
||||
async function remove(row) {
|
||||
await ElMessageBox.confirm('确认移除该物料?', '提示', { type: 'warning' })
|
||||
const items = list.value
|
||||
.filter((x) => x.id !== row.id)
|
||||
.map(({ materialCode, materialName, spec, unit, manageMode, unitQty, lossRate, requiredQty }) => ({ materialCode, materialName, spec, unit, manageMode, unitQty, lossRate, requiredQty }))
|
||||
await request.put('/bom', { workOrderNo: orderNo.value, items })
|
||||
await load()
|
||||
}
|
||||
onMounted(async () => {
|
||||
workOrders.value = (await request.get('/work-orders')) || []
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:180px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="操作人"><el-input v-model="query.operator" clearable style="width:120px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="类型"><el-input v-model="query.eventType" clearable style="width:180px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="load">查询</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="eventType" label="事件类型" min-width="150" />
|
||||
<el-table-column prop="workOrderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="description" label="描述" min-width="160" />
|
||||
<el-table-column prop="entityType" label="实体" width="130" />
|
||||
<el-table-column prop="entityId" label="实体ID" width="120" />
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
<el-table-column prop="createdAt" label="时间" min-width="170" />
|
||||
</el-table>
|
||||
<el-pagination v-model:current-page="page" :page-size="pageSize" :total="total"
|
||||
layout="total, prev, pager, next" background style="margin-top:10px" @current-change="load" />
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import request from '../utils/request'
|
||||
|
||||
const query = reactive({ orderNo: '', operator: '', eventType: '' })
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
|
||||
async function load() {
|
||||
const data = await request.get('/event-logs', { params: { ...query, page: page.value, pageSize: pageSize.value } })
|
||||
list.value = data?.list || []
|
||||
total.value = data?.total || 0
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="login-wrap">
|
||||
<el-card class="login-card">
|
||||
<h2 class="title">MES 产线控制</h2>
|
||||
<el-form @submit.prevent="onLogin">
|
||||
<el-form-item>
|
||||
<el-input v-model="username" placeholder="用户名" size="large">
|
||||
<template #prefix>👤</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="password" type="password" placeholder="密码" size="large" show-password @keyup.enter="onLogin">
|
||||
<template #prefix>🔒</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-button type="primary" size="large" style="width:100%" :loading="loading" @click="onLogin">登 录</el-button>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
import { setSession } from '../utils/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const username = ref('admin')
|
||||
const password = ref('')
|
||||
const loading = ref(false)
|
||||
|
||||
async function onLogin() {
|
||||
if (!username.value || !password.value) return ElMessage.warning('请输入账号和密码')
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await request.post('/login', { username: username.value, password: password.value })
|
||||
setSession(data.accessToken, { username: username.value })
|
||||
const u = await request.get('/userinfo')
|
||||
localStorage.setItem('user', JSON.stringify(u))
|
||||
router.replace(route.query.redirect || '/work-order')
|
||||
} catch (e) {
|
||||
// 错误已在拦截器提示
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-wrap { height: 100vh; display: flex; align-items: center; justify-content: center;
|
||||
background: linear-gradient(135deg, #1f3b73, #2d5aa0); }
|
||||
.login-card { width: 360px; padding: 10px 20px; }
|
||||
.title { text-align: center; margin: 6px 0 24px; color: #333; }
|
||||
</style>
|
||||
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker v-model="query.planDate" type="date" value-format="YYYY-MM-DD" @change="load" />
|
||||
</el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:160px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item label="状态"><el-input v-model="query.status" style="width:120px" @keyup.enter="load" /></el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<el-button type="success" @click="generate">按日排产生成备料</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="requestNo" label="备料单号" min-width="180" />
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="planDate" label="排产日期" width="110" />
|
||||
<el-table-column prop="materialCode" label="物料编码" min-width="110" />
|
||||
<el-table-column prop="materialName" label="物料名称" min-width="110" />
|
||||
<el-table-column prop="unit" label="单位" width="70" />
|
||||
<el-table-column prop="manageMode" label="管理方式" width="110">
|
||||
<template #default="{row}">{{ row.manageMode === '1' ? 'SN' : '批次' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reqQty" label="需求数量" width="90" />
|
||||
<el-table-column prop="status" label="状态" width="90" />
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const query = reactive({ planDate: '', orderNo: '', status: '' })
|
||||
const list = ref([])
|
||||
|
||||
async function load() {
|
||||
list.value = (await request.get('/material-requests', { params: query })) || []
|
||||
}
|
||||
async function generate() {
|
||||
if (!query.planDate) return ElMessage.warning('请先选择排产日期')
|
||||
const data = await request.post('/material-requests/generate', { planDate: query.planDate })
|
||||
ElMessage.success(`已生成 ${data?.count || 0} 条备料`)
|
||||
load()
|
||||
}
|
||||
onMounted(() => {
|
||||
query.planDate = new Date().toISOString().slice(0, 10)
|
||||
load()
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="工单号"><el-input v-model="send.orderNo" style="width:170px" /></el-form-item>
|
||||
<el-form-item label="工件SN"><el-input v-model="send.sn" style="width:170px" /></el-form-item>
|
||||
<el-form-item label="工位"><el-input-number v-model="send.stationNo" :min="1" :max="12" /></el-form-item>
|
||||
<el-form-item label="工序组合"><el-input v-model="send.processCombination" placeholder="如 135" style="width:110px" /></el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="doSend">下发工序码</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">下发日志</el-divider>
|
||||
<el-form inline>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:160px" /></el-form-item>
|
||||
<el-form-item label="状态"><el-input v-model="query.status" style="width:120px" /></el-form-item>
|
||||
<el-form-item><el-button @click="loadLogs">刷新</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="logs" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="sn" label="SN" min-width="130" />
|
||||
<el-table-column prop="stationNo" label="工位" width="70" />
|
||||
<el-table-column prop="processCombination" label="工序组合" width="100" />
|
||||
<el-table-column prop="processCodes" label="工序码" width="120" />
|
||||
<el-table-column prop="status" label="状态" width="90" />
|
||||
<el-table-column prop="ack" label="完成" width="70">
|
||||
<template #default="{row}">{{ row.ack ? '是' : '否' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="sendTime" label="下发时间" width="170" />
|
||||
<el-table-column prop="doneTime" label="完成时间" width="170" />
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const send = reactive({ orderNo: '', sn: '', stationNo: 1, processCombination: '' })
|
||||
const query = reactive({ orderNo: '', status: '' })
|
||||
const logs = ref([])
|
||||
|
||||
async function doSend() {
|
||||
if (!send.orderNo || !send.processCombination) return ElMessage.warning('请填写工单号和工序组合')
|
||||
try {
|
||||
await request.post('/plc/send-process', { ...send })
|
||||
ElMessage.success('工序码下发成功(未收到完成信号不会下发下一条)')
|
||||
loadLogs()
|
||||
} catch (e) { /* 拦截器已提示,如"上一条未完成" */ }
|
||||
}
|
||||
async function loadLogs() {
|
||||
logs.value = (await request.get('/plc/send-logs', { params: query })) || []
|
||||
}
|
||||
onMounted(loadLogs)
|
||||
</script>
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item><el-button type="primary" @click="openAdd">新增产品类型</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="name" label="名称" min-width="120" />
|
||||
<el-table-column prop="code" label="编码" min-width="120" />
|
||||
<el-table-column prop="category" label="类别" width="110" />
|
||||
<el-table-column prop="isActive" label="启用" width="80">
|
||||
<template #default="{row}"><el-tag :type="row.isActive?'success':'info'">{{ row.isActive?'是':'否' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="160" />
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="{row}">
|
||||
<el-button size="small" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="del(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog v-model="dialog.show" :title="dialog.id?'编辑产品类型':'新增产品类型'" width="460px">
|
||||
<el-form :model="dialog" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="dialog.name" /></el-form-item>
|
||||
<el-form-item label="编码"><el-input v-model="dialog.code" /></el-form-item>
|
||||
<el-form-item label="类别"><el-input v-model="dialog.category" /></el-form-item>
|
||||
<el-form-item label="启用"><el-switch v-model="dialog.isActive" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="dialog.remark" type="textarea" /></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialog.show=false">取消</el-button>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const list = ref([])
|
||||
const dialog = reactive({ show: false, id: 0, name: '', code: '', category: '', remark: '', isActive: true })
|
||||
|
||||
async function load() { list.value = (await request.get('/product-types')) || [] }
|
||||
function openAdd() { Object.assign(dialog, { show: true, id: 0, name: '', code: '', category: '', remark: '', isActive: true }) }
|
||||
function openEdit(row) { Object.assign(dialog, { show: true, ...row }) }
|
||||
async function save() {
|
||||
if (dialog.id) await request.put('/product-types', { ...dialog })
|
||||
else await request.post('/product-types', { ...dialog })
|
||||
ElMessage.success('保存成功')
|
||||
dialog.show = false
|
||||
load()
|
||||
}
|
||||
async function del(row) {
|
||||
await ElMessageBox.confirm('确认删除?', '提示', { type: 'warning' })
|
||||
await request.delete(`/product-types/${row.id}`)
|
||||
load()
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-tabs v-model="tab">
|
||||
<!-- 用户 -->
|
||||
<el-tab-pane label="用户" name="users">
|
||||
<div style="margin-bottom:10px">
|
||||
<el-button type="primary" @click="openUser()">新增用户</el-button>
|
||||
</div>
|
||||
<el-table :data="users" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="username" label="用户名" min-width="120" />
|
||||
<el-table-column prop="name" label="姓名" min-width="110" />
|
||||
<el-table-column prop="roleId" label="角色ID" width="80" />
|
||||
<el-table-column prop="status" label="状态" width="90" />
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="{row}">
|
||||
<el-button size="small" @click="openUser(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="delUser(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 角色 -->
|
||||
<el-tab-pane label="角色" name="roles">
|
||||
<div style="margin-bottom:10px">
|
||||
<el-button type="primary" @click="openRole()">新增角色</el-button>
|
||||
</div>
|
||||
<el-table :data="roles" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="name" label="名称" min-width="120" />
|
||||
<el-table-column prop="code" label="编码" min-width="130" />
|
||||
<el-table-column prop="remark" label="备注" min-width="150" />
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="{row}">
|
||||
<el-button size="small" @click="openRole(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="delRole(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 权限 -->
|
||||
<el-tab-pane label="权限菜单" name="perms">
|
||||
<div style="margin-bottom:10px">
|
||||
<el-button type="primary" @click="openPerm()">新增权限</el-button>
|
||||
</div>
|
||||
<el-table :data="perms" border size="small">
|
||||
<el-table-column prop="id" label="ID" width="60" />
|
||||
<el-table-column prop="code" label="编码" min-width="150" />
|
||||
<el-table-column prop="name" label="名称" min-width="120" />
|
||||
<el-table-column prop="type" label="类型" width="80" />
|
||||
<el-table-column prop="path" label="路径" min-width="120" />
|
||||
<el-table-column prop="sort" label="排序" width="70" />
|
||||
<el-table-column label="操作" width="140">
|
||||
<template #default="{row}">
|
||||
<el-button size="small" @click="openPerm(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="delPerm(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 用户编辑 -->
|
||||
<el-dialog v-model="u.show" :title="u.id?'编辑用户':'新增用户'" width="420px">
|
||||
<el-form :model="u" label-width="80px">
|
||||
<el-form-item label="用户名"><el-input v-model="u.username" /></el-form-item>
|
||||
<el-form-item label="密码"><el-input v-model="u.password" type="password" show-password /></el-form-item>
|
||||
<el-form-item label="姓名"><el-input v-model="u.name" /></el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="u.roleId" style="width:100%">
|
||||
<el-option v-for="r in roles" :key="r.id" :label="r.name" :value="r.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="u.status" style="width:100%">
|
||||
<el-option label="启用" value="ENABLED" /><el-option label="禁用" value="DISABLED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer><el-button @click="u.show=false">取消</el-button><el-button type="primary" @click="saveUser">保存</el-button></template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 角色编辑 -->
|
||||
<el-dialog v-model="r.show" :title="r.id?'编辑角色':'新增角色'" width="560px">
|
||||
<el-form :model="r" label-width="80px">
|
||||
<el-form-item label="名称"><el-input v-model="r.name" /></el-form-item>
|
||||
<el-form-item label="编码"><el-input v-model="r.code" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="r.remark" /></el-form-item>
|
||||
<el-form-item label="权限">
|
||||
<el-select v-model="r.permissionCodes" multiple filterable style="width:100%">
|
||||
<el-option v-for="p in perms" :key="p.code" :label="p.name" :value="p.code" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer><el-button @click="r.show=false">取消</el-button><el-button type="primary" @click="saveRole">保存</el-button></template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 权限编辑 -->
|
||||
<el-dialog v-model="p.show" :title="p.id?'编辑权限':'新增权限'" width="460px">
|
||||
<el-form :model="p" label-width="80px">
|
||||
<el-form-item label="编码"><el-input v-model="p.code" /></el-form-item>
|
||||
<el-form-item label="名称"><el-input v-model="p.name" /></el-form-item>
|
||||
<el-form-item label="类型"><el-input v-model="p.type" /></el-form-item>
|
||||
<el-form-item label="路径"><el-input v-model="p.path" /></el-form-item>
|
||||
<el-form-item label="排序"><el-input-number v-model="p.sort" /></el-form-item>
|
||||
<el-form-item label="备注"><el-input v-model="p.remark" /></el-form-item>
|
||||
</el-form>
|
||||
<template #footer><el-button @click="p.show=false">取消</el-button><el-button type="primary" @click="savePerm">保存</el-button></template>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const tab = ref('users')
|
||||
const users = ref([]), roles = ref([]), perms = ref([])
|
||||
const u = reactive({ show: false, id: 0, username: '', password: '', name: '', roleId: 0, status: 'ENABLED' })
|
||||
const r = reactive({ show: false, id: 0, name: '', code: '', remark: '', permissionCodes: [] })
|
||||
const p = reactive({ show: false, id: 0, code: '', name: '', type: 'MENU', path: '', sort: 0, remark: '' })
|
||||
|
||||
async function loadAll() {
|
||||
users.value = (await request.get('/users')) || []
|
||||
roles.value = (await request.get('/roles')) || []
|
||||
perms.value = (await request.get('/permissions')) || []
|
||||
}
|
||||
function openUser(row) { Object.assign(u, row ? { ...row, show: true, password: '' } : { show: true, id: 0, username: '', password: '', name: '', roleId: roles.value[0]?.id || 0, status: 'ENABLED' }) }
|
||||
async function saveUser() {
|
||||
if (u.id) await request.put('/users', { ...u })
|
||||
else await request.post('/users', { ...u })
|
||||
ElMessage.success('保存成功'); u.show = false; loadAll()
|
||||
}
|
||||
async function delUser(row) {
|
||||
await ElMessageBox.confirm('确认删除该用户?', '提示', { type: 'warning' })
|
||||
await request.delete(`/users/${row.id}`); loadAll()
|
||||
}
|
||||
function openRole(row) { Object.assign(r, row ? { ...row, show: true } : { show: true, id: 0, name: '', code: '', remark: '', permissionCodes: [] }) }
|
||||
async function saveRole() {
|
||||
if (r.id) await request.put('/roles', { ...r })
|
||||
else await request.post('/roles', { ...r })
|
||||
ElMessage.success('保存成功'); r.show = false; loadAll()
|
||||
}
|
||||
async function delRole(row) {
|
||||
await ElMessageBox.confirm('确认删除该角色?', '提示', { type: 'warning' })
|
||||
await request.delete(`/roles/${row.id}`); loadAll()
|
||||
}
|
||||
function openPerm(row) { Object.assign(p, row ? { ...row, show: true } : { show: true, id: 0, code: '', name: '', type: 'MENU', path: '', sort: 0, remark: '' }) }
|
||||
async function savePerm() {
|
||||
if (p.id) await request.put('/permissions', { ...p })
|
||||
else await request.post('/permissions', { ...p })
|
||||
ElMessage.success('保存成功'); p.show = false; loadAll()
|
||||
}
|
||||
async function delPerm(row) {
|
||||
await ElMessageBox.confirm('确认删除该权限?', '提示', { type: 'warning' })
|
||||
await request.delete(`/permissions/${row.id}`); loadAll()
|
||||
}
|
||||
onMounted(loadAll)
|
||||
</script>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="SN"><el-input v-model="scan.sn" style="width:170px" /></el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="scan.orderNo" style="width:170px" /></el-form-item>
|
||||
<el-form-item label="工位"><el-input v-model="scan.station" style="width:90px" /></el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="scan.type" style="width:130px">
|
||||
<el-option label="进线ONLINE" value="ONLINE" />
|
||||
<el-option label="工序PROCESS" value="PROCESS" />
|
||||
<el-option label="完工DONE" value="DONE" />
|
||||
<el-option label="暂存TEMP_STORE" value="TEMP_STORE" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="工序号"><el-input-number v-model="scan.processCode" :min="0" /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="report">扫码报工</el-button></el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-divider content-position="left">报工记录</el-divider>
|
||||
<el-form inline>
|
||||
<el-form-item label="SN"><el-input v-model="query.sn" clearable style="width:160px" /></el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="query.orderNo" clearable style="width:160px" /></el-form-item>
|
||||
<el-form-item><el-button @click="load">刷新</el-button></el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="sn" label="SN" min-width="130" />
|
||||
<el-table-column prop="orderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="station" label="工位" width="80" />
|
||||
<el-table-column prop="type" label="类型" width="110" />
|
||||
<el-table-column prop="processCode" label="工序号" width="80" />
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
<el-table-column prop="time" label="时间" min-width="170" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const scan = reactive({ sn: '', orderNo: '', station: '', type: 'PROCESS', processCode: 0 })
|
||||
const query = reactive({ sn: '', orderNo: '' })
|
||||
const list = ref([])
|
||||
|
||||
async function report() {
|
||||
if (!scan.sn) return ElMessage.warning('请输入SN')
|
||||
await request.post('/scan/report', scan)
|
||||
ElMessage.success('报工成功')
|
||||
Object.assign(scan, { sn: '', station: '', processCode: 0 })
|
||||
load()
|
||||
}
|
||||
async function load() {
|
||||
list.value = (await request.get('/scan/records', { params: query })) || []
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="SN"><el-input v-model="query.sn" clearable style="width:170px" /></el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="query.workOrderNo" clearable style="width:170px" /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="load">查询</el-button></el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 录入拧紧 -->
|
||||
<el-collapse style="margin-bottom:12px">
|
||||
<el-collapse-item title="扫码录入拧紧数据" name="1">
|
||||
<el-form inline>
|
||||
<el-form-item label="SN"><el-input v-model="torque.sn" style="width:150px" /></el-form-item>
|
||||
<el-form-item label="工单号"><el-input v-model="torque.workOrderNo" style="width:150px" /></el-form-item>
|
||||
<el-form-item label="工位"><el-input v-model="torque.stationNo" style="width:80px" /></el-form-item>
|
||||
<el-form-item label="扭矩"><el-input-number v-model="torque.strain" :min="0" /></el-form-item>
|
||||
<el-form-item label="角度"><el-input-number v-model="torque.angle" :min="0" /></el-form-item>
|
||||
<el-form-item label="操作人"><el-input v-model="torque.operator" style="width:100px" /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="report">上报</el-button></el-form-item>
|
||||
</el-form>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
|
||||
<el-table :data="list" border size="small">
|
||||
<el-table-column prop="sn" label="SN" min-width="130" />
|
||||
<el-table-column prop="workOrderNo" label="工单号" min-width="130" />
|
||||
<el-table-column prop="stationNo" label="工位" width="70" />
|
||||
<el-table-column prop="torque" label="扭矩(N·m)" width="100" />
|
||||
<el-table-column prop="angle" label="角度(°)" width="90" />
|
||||
<el-table-column prop="result" label="结果" width="80">
|
||||
<template #default="{row}"><el-tag :type="row.result==='OK'?'success':'danger'">{{ row.result }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
<el-table-column prop="time" label="时间" min-width="170" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const query = reactive({ sn: '', workOrderNo: '' })
|
||||
const torque = reactive({ sn: '', workOrderNo: '', stationNo: '', strain: 20, angle: 90, operator: '' })
|
||||
const list = ref([])
|
||||
|
||||
async function load() {
|
||||
list.value = (await request.get('/torque/records', { params: query })) || []
|
||||
}
|
||||
async function report() {
|
||||
if (!torque.sn) return ElMessage.warning('请输入SN')
|
||||
await request.post('/torque/report', torque)
|
||||
ElMessage.success('拧紧数据已上报')
|
||||
Object.assign(torque, { sn: '', workOrderNo: '', strain: 20, angle: 90 })
|
||||
load()
|
||||
}
|
||||
onMounted(load)
|
||||
</script>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="工件SN">
|
||||
<el-input v-model="sn" style="width:220px" placeholder="输入SN查询追溯" @keyup.enter="search" />
|
||||
</el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="search">追溯查询</el-button></el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-alert v-if="error" :title="error" type="error" show-icon closable style="margin-bottom:10px" />
|
||||
|
||||
<template v-if="data">
|
||||
<el-descriptions :column="4" border style="margin-bottom:12px">
|
||||
<el-descriptions-item label="SN">{{ data.workpiece?.sn }}</el-descriptions-item>
|
||||
<el-descriptions-item label="工单号">{{ data.workpiece?.orderNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{ data.workpiece?.status }}</el-descriptions-item>
|
||||
<el-descriptions-item label="当前工序">{{ data.workpiece?.currentProcess }}</el-descriptions-item>
|
||||
<el-descriptions-item label="进线时间">{{ fmt(data.workpiece?.onlineAt) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="完工时间">{{ fmt(data.workpiece?.doneAt) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-tabs>
|
||||
<el-tab-pane label="工序时间线">
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="p in (data.processTimeline||[])" :key="p.id"
|
||||
:timestamp="p.operator ? (p.operator+' · '+fmt(p.endedAt)) : fmt(p.endedAt)"
|
||||
:type="p.result==='NG'?'danger':'success'">
|
||||
{{ p.processName }}(工序 {{ p.processCode }},工位 {{ p.stationNo }})— {{ p.result }}
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="步骤考核数据">
|
||||
<el-table :data="data.stepData||[]" border size="small">
|
||||
<el-table-column prop="stepName" label="步骤" min-width="110" />
|
||||
<el-table-column prop="criterionName" label="考核项" min-width="110" />
|
||||
<el-table-column prop="value" label="数值" width="100" />
|
||||
<el-table-column prop="isOK" label="合格" width="80">
|
||||
<template #default="{row}"><el-tag :type="row.isOK?'success':'danger'">{{ row.isOK?'OK':'NG' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="拧紧数据">
|
||||
<el-table :data="data.torqueRecords||[]" border size="small">
|
||||
<el-table-column prop="stationNo" label="工位" width="80" />
|
||||
<el-table-column prop="torque" label="扭矩" width="100" />
|
||||
<el-table-column prop="angle" label="角度" width="90" />
|
||||
<el-table-column prop="result" label="结果" width="80" />
|
||||
<el-table-column prop="operator" label="操作人" width="90" />
|
||||
<el-table-column prop="time" label="时间" min-width="160" />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="成品关联">
|
||||
<p>批次号:{{ (data.associationTrace?.batchItems||[]).join(', ') || '无' }}</p>
|
||||
<p>精密件SN:{{ (data.associationTrace?.serialItems||[]).join(', ') || '无' }}</p>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import request from '../utils/request'
|
||||
|
||||
const sn = ref('')
|
||||
const data = ref(null)
|
||||
const error = ref('')
|
||||
|
||||
async function search() {
|
||||
if (!sn.value) return
|
||||
error.value = ''
|
||||
data.value = null
|
||||
try {
|
||||
data.value = await request.get('/trace', { params: { sn: sn.value } })
|
||||
} catch (e) {
|
||||
error.value = e.message || '查询失败'
|
||||
}
|
||||
}
|
||||
function fmt(v) { return v ? String(v).replace('T', ' ').slice(0, 19) : '-' }
|
||||
</script>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-form inline>
|
||||
<el-form-item label="工单号">
|
||||
<el-input v-model="query.orderNo" placeholder="模糊查询" clearable style="width:180px" @keyup.enter="load" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="query.status" clearable placeholder="全部" style="width:150px">
|
||||
<el-option v-for="s in statusOptions" :key="s.v" :label="s.l" :value="s.v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="load">查询</el-button>
|
||||
<el-button @click="openCreate">新建工单</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table :data="list" border stripe size="small">
|
||||
<el-table-column prop="id" label="ID" width="55" />
|
||||
<el-table-column prop="workOrderNo" label="工单号" min-width="140" />
|
||||
<el-table-column prop="productCode" label="产品编码" min-width="120" />
|
||||
<el-table-column prop="productName" label="产品名称" min-width="120" />
|
||||
<el-table-column prop="quantity" label="数量" width="70" />
|
||||
<el-table-column prop="finishedNum" label="已完工" width="70" />
|
||||
<el-table-column prop="processSeq" label="工序组合" width="120" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">{{ statusText(row.status) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="170" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="openDaily(row)">日排产</el-button>
|
||||
<el-button size="small" type="warning" @click="openEdit(row)">编辑</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 新建/编辑工单 -->
|
||||
<el-dialog v-model="dialog.show" :title="dialog.id ? '编辑工单' : '新建工单'" width="520px">
|
||||
<el-form :model="dialog" label-width="90px">
|
||||
<el-form-item label="工单号"><el-input v-model="dialog.workOrderNo" /></el-form-item>
|
||||
<el-form-item label="产品类型">
|
||||
<el-select v-model="dialog.productTypeId" placeholder="选择产品类型" style="width:100%">
|
||||
<el-option v-for="p in productTypes" :key="p.id" :label="p.name + (p.code? ' / '+p.code:'')" :value="p.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品编码"><el-input v-model="dialog.productCode" /></el-form-item>
|
||||
<el-form-item label="产品名称"><el-input v-model="dialog.productName" /></el-form-item>
|
||||
<el-form-item label="数量"><el-input-number v-model="dialog.quantity" :min="1" /></el-form-item>
|
||||
<el-form-item label="工序组合"><el-input v-model="dialog.processSeq" placeholder="如 135" /></el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="dialog.status" style="width:100%">
|
||||
<el-option v-for="s in statusOptions" :key="s.v" :label="s.l" :value="s.v" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialog.show=false">取消</el-button>
|
||||
<el-button type="primary" @click="save">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 日排产 -->
|
||||
<el-dialog v-model="plan.show" title="日排产" width="720px">
|
||||
<el-form inline style="margin-bottom:10px">
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker v-model="plan.date" type="date" value-format="YYYY-MM-DD" />
|
||||
</el-form-item>
|
||||
<el-form-item label="计划数量">
|
||||
<el-input-number v-model="plan.qty" :min="1" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="savePlan">保存排产</el-button>
|
||||
<el-button @click="loadPlans">刷新</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="planList" border size="small">
|
||||
<el-table-column prop="planDate" label="日期" width="120" />
|
||||
<el-table-column prop="planQty" label="计划数量" width="100" />
|
||||
<el-table-column prop="completedQty" label="已完成" />
|
||||
<el-table-column prop="status" label="状态" />
|
||||
<el-table-column prop="operator" label="操作人" />
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from '../utils/request'
|
||||
|
||||
const statusOptions = [
|
||||
{ v: 'CREATED', l: '已创建' }, { v: 'RELEASED', l: '已发布' },
|
||||
{ v: 'IN_PROGRESS', l: '生产中' }, { v: 'DONE', l: '已完成' }
|
||||
]
|
||||
const statusText = (s) => (statusOptions.find((x) => x.v === s) || {}).l || s
|
||||
|
||||
const query = reactive({ orderNo: '', status: '' })
|
||||
const list = ref([])
|
||||
const productTypes = ref([])
|
||||
|
||||
const dialog = reactive({ show: false, id: 0, workOrderNo: '', productTypeId: 0, productCode: '', productName: '', quantity: 1, processSeq: '', status: 'CREATED' })
|
||||
const plan = reactive({ show: false, orderNo: '', date: '', qty: 1 })
|
||||
const planList = ref([])
|
||||
|
||||
async function load() {
|
||||
const data = await request.get('/work-orders', { params: query })
|
||||
list.value = data || []
|
||||
}
|
||||
async function loadProducts() {
|
||||
productTypes.value = (await request.get('/product-types')) || []
|
||||
}
|
||||
function openCreate() {
|
||||
Object.assign(dialog, { show: true, id: 0, workOrderNo: `WO${Date.now()}`, productTypeId: 0, productCode: '', productName: '', quantity: 1, processSeq: '123456789101112', status: 'CREATED' })
|
||||
}
|
||||
function openEdit(row) {
|
||||
Object.assign(dialog, { show: true, id: row.id, workOrderNo: row.workOrderNo, productTypeId: row.productTypeId, productCode: row.productCode, productName: row.productName, quantity: row.quantity, processSeq: row.processSeq, status: row.status })
|
||||
}
|
||||
async function save() {
|
||||
const body = { id: dialog.id, workOrderNo: dialog.workOrderNo, productTypeId: dialog.productTypeId, productCode: dialog.productCode, productName: dialog.productName, quantity: dialog.quantity, processSeq: dialog.processSeq, status: dialog.status }
|
||||
if (dialog.id) await request.put('/work-orders', body)
|
||||
else await request.post('/work-orders', body)
|
||||
ElMessage.success('保存成功')
|
||||
dialog.show = false
|
||||
load()
|
||||
}
|
||||
function openDaily(row) {
|
||||
plan.orderNo = row.workOrderNo
|
||||
plan.date = new Date().toISOString().slice(0, 10)
|
||||
plan.qty = row.quantity
|
||||
plan.show = true
|
||||
loadPlans()
|
||||
}
|
||||
async function loadPlans() {
|
||||
planList.value = (await request.get('/daily-plans', { params: { orderNo: plan.orderNo } })) || []
|
||||
}
|
||||
async function savePlan() {
|
||||
await request.post('/daily-plans', { orderNo: plan.orderNo, planDate: plan.date, planQty: plan.qty })
|
||||
ElMessage.success('排产保存成功')
|
||||
loadPlans()
|
||||
}
|
||||
|
||||
onMounted(() => { load(); loadProducts() })
|
||||
</script>
|
||||
Reference in New Issue
Block a user