feat: 完成产线MES系统全流程功能迭代与优化
本提交完成了多个核心模块的功能完善与业务对齐: 1. 工位终端:固定工位配置、移除自选工位逻辑、适配配置化工位号 2. MES核心:重构工位组合逻辑、新增工单/流程状态管理、补全报工/追溯逻辑 3. WMS客户端:新增修改密码功能、优化帮助弹窗逻辑 4. 文档与权限:补充完整操作手册、统一菜单名称与权限描述 5. 修复多业务校验:排产数量校验、工单状态校验、工位绑定规则
This commit is contained in:
@@ -24,9 +24,10 @@ Internal:
|
||||
Sim:
|
||||
Enable: true
|
||||
|
||||
# 工位配置:Code 留空则前端可自选工位;ScrewCount 为本工位需拧紧的螺丝颗数
|
||||
# 工位配置:StationNo 固定本终端工位号(1~12,仅允许运维改配置后重启生效,工人无法在前端修改);
|
||||
# ScrewCount 为本工位需拧紧的螺丝颗数
|
||||
Station:
|
||||
Code: ""
|
||||
StationNo: 1
|
||||
ScrewCount: 10
|
||||
|
||||
# 工艺文件 PDF 本地预缓存目录(留空关闭缓存)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>北京动力 · 工位终端</title>
|
||||
<title>工位终端</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -27,16 +27,7 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<span class="lab">工位</span>
|
||||
<el-select
|
||||
v-if="!fixedStation"
|
||||
v-model="stationNo"
|
||||
size="large"
|
||||
class="station-el"
|
||||
placeholder="请选择工位"
|
||||
>
|
||||
<el-option v-for="n in 12" :key="n" :label="'工位 ' + n" :value="n" />
|
||||
</el-select>
|
||||
<span v-else class="station-fixed">工位 {{ fixedStation }}</span>
|
||||
<span class="station-fixed">{{ fixedStation ? '工位 ' + fixedStation : '工位加载中…' }}</span>
|
||||
</div>
|
||||
|
||||
<el-button class="btn-login" type="primary" size="large" :loading="loading" @click="submit">
|
||||
@@ -55,22 +46,20 @@ import { ElMessage } from 'element-plus'
|
||||
import { getConfig, login } from '../api'
|
||||
import { setAuth } from '../utils/request'
|
||||
|
||||
const STATION_KEY = 'ws_station_no'
|
||||
const router = useRouter()
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
const stationNo = ref(null)
|
||||
const fixedStation = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
// 工位由服务端配置文件固定(1~12),一个工位一个终端,终端不允许修改工位
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const cfg = await getConfig()
|
||||
if (cfg?.stationNo > 0) fixedStation.value = cfg.stationNo
|
||||
} catch {
|
||||
// 网络异常时忽略,工位仍可自选
|
||||
// 网络异常时保留 0,登录会提示配置未加载
|
||||
}
|
||||
stationNo.value = fixedStation.value || Number(localStorage.getItem(STATION_KEY)) || null
|
||||
})
|
||||
|
||||
async function submit() {
|
||||
@@ -78,9 +67,8 @@ async function submit() {
|
||||
ElMessage.warning('请输入账号和密码')
|
||||
return
|
||||
}
|
||||
const sn = fixedStation.value || stationNo.value
|
||||
if (!sn) {
|
||||
ElMessage.warning('请选择工位')
|
||||
if (!fixedStation.value) {
|
||||
ElMessage.warning('工位配置未加载,请检查服务端配置文件后重启')
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
@@ -88,10 +76,9 @@ async function submit() {
|
||||
const data = await login({
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
stationNo: sn
|
||||
stationNo: fixedStation.value
|
||||
})
|
||||
setAuth(data.accessToken, data.user)
|
||||
localStorage.setItem(STATION_KEY, String(sn))
|
||||
ElMessage.success('登录成功')
|
||||
router.push('/main')
|
||||
} catch (e) {
|
||||
|
||||
@@ -4,15 +4,10 @@
|
||||
<header class="top-bar">
|
||||
<div class="brand">工位终端</div>
|
||||
|
||||
<div v-if="!fixedStation" class="dock-select">
|
||||
<!-- 工位固定:由服务端配置文件加载(1~12),终端不允许修改 -->
|
||||
<div class="dock-select">
|
||||
<span class="dock-lab">工位</span>
|
||||
<el-select v-model="stationNo" size="large" class="dock-el">
|
||||
<el-option v-for="n in 12" :key="n" :label="'工位 ' + n" :value="n" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div v-else class="dock-select">
|
||||
<span class="dock-lab">工位</span>
|
||||
<span class="dock-fixed">{{ fixedStation }}</span>
|
||||
<span class="dock-fixed">{{ stationNo ? '工位 ' + stationNo : '…' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="spacer"></div>
|
||||
@@ -169,9 +164,15 @@
|
||||
<!-- 操作说明帮助 -->
|
||||
<PageHelp :data="helpWorkstation" />
|
||||
|
||||
<!-- 我的工作量弹窗(块6) -->
|
||||
<el-dialog v-model="workload.show" title="我的工作量" width="720px" top="8vh">
|
||||
<!-- 我的工作量弹窗(块6):当前登录人所有工位的工作,默认当前工位,可按工位/时间筛选 -->
|
||||
<el-dialog v-model="workload.show" title="我的工作量" width="760px" top="8vh">
|
||||
<el-form inline>
|
||||
<el-form-item label="工位">
|
||||
<el-select v-model="workload.stationNo" size="large" style="width:160px">
|
||||
<el-option label="全部工位" :value="0" />
|
||||
<el-option v-for="n in 12" :key="n" :label="'工位 ' + n" :value="n" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期范围">
|
||||
<el-date-picker
|
||||
v-model="workload.range"
|
||||
@@ -192,6 +193,7 @@
|
||||
<el-col :span="6"><el-card shadow="never" class="wl-stat"><div class="wl-label">总合格率</div><div class="wl-value">{{ workload.summary.rate }}</div></el-card></el-col>
|
||||
</el-row>
|
||||
<el-table :data="workload.rows" border size="small" max-height="360">
|
||||
<el-table-column prop="stationNo" label="工位" width="90" align="center" />
|
||||
<el-table-column prop="date" label="日期" width="110" />
|
||||
<el-table-column prop="processCode" label="工序" width="80" align="center" />
|
||||
<el-table-column prop="processName" label="工序名" min-width="130" show-overflow-tooltip />
|
||||
@@ -209,7 +211,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import PageHelp from '../components/PageHelp.vue'
|
||||
@@ -227,30 +229,23 @@ import {
|
||||
} from '../api'
|
||||
import { clearAuth, getUser } from '../utils/request'
|
||||
|
||||
const STATION_KEY = 'ws_station_no'
|
||||
const router = useRouter()
|
||||
const user = getUser()
|
||||
|
||||
// ---------- 工位配置(固定工位号锁定,否则 1~12 自选,存 localStorage) ----------
|
||||
const fixedStation = ref(0)
|
||||
const stationNo = ref(Number(localStorage.getItem(STATION_KEY)) || 1)
|
||||
// ---------- 工位配置(固定工位号:由服务端 yaml 配置在启动时加载,终端不允许修改) ----------
|
||||
const stationNo = ref(0)
|
||||
const screwCount = ref(0)
|
||||
|
||||
async function loadConfig() {
|
||||
try {
|
||||
const cfg = await getConfig()
|
||||
if (cfg?.stationNo > 0) fixedStation.value = cfg.stationNo
|
||||
if (cfg?.stationNo > 0) stationNo.value = cfg.stationNo
|
||||
if (cfg?.screwCount > 0) screwCount.value = cfg.screwCount
|
||||
} catch {
|
||||
/* 配置加载失败静默 */
|
||||
}
|
||||
}
|
||||
|
||||
watch(stationNo, (v) => {
|
||||
localStorage.setItem(STATION_KEY, String(v))
|
||||
refreshAll()
|
||||
})
|
||||
|
||||
// ---------- 当前任务(MES) ----------
|
||||
const task = ref(null)
|
||||
const flowName = computed(() => task.value?.flow?.name || '-')
|
||||
@@ -534,15 +529,17 @@ async function manualFlush() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 我的工作量(块6) ----------
|
||||
// ---------- 我的工作量(块6):当前登录人所有工位的工作,可筛工位(默认当前工位)/时间 ----------
|
||||
const workload = reactive({
|
||||
show: false,
|
||||
stationNo: 0, // 0=全部工位;默认打开时设为当前工位
|
||||
range: null,
|
||||
rows: [],
|
||||
summary: { doneCount: 0, okCount: 0, ngCount: 0, rate: '-' }
|
||||
})
|
||||
|
||||
function openWorkload() {
|
||||
if (!workload.stationNo) workload.stationNo = stationNo.value // 默认当前工位
|
||||
workload.show = true
|
||||
loadWorkload()
|
||||
}
|
||||
@@ -551,6 +548,7 @@ async function loadWorkload() {
|
||||
const [from, to] = workload.range || []
|
||||
const params = {
|
||||
operator: user.name || user.username || '',
|
||||
stationNo: workload.stationNo || 0, // 0=全部工位
|
||||
from: from || '',
|
||||
to: to || ''
|
||||
}
|
||||
@@ -591,7 +589,6 @@ function logout() {
|
||||
|
||||
onMounted(async () => {
|
||||
await loadConfig()
|
||||
if (fixedStation.value) stationNo.value = fixedStation.value
|
||||
refreshAll()
|
||||
focusScan()
|
||||
window.addEventListener('focus', focusScan)
|
||||
|
||||
@@ -31,10 +31,11 @@ type SqliteConfig struct {
|
||||
Path string `json:",default=workstation.db"`
|
||||
}
|
||||
|
||||
// StationConfig 工位配置:可从配置文件指定固定工位号(1~12),留空则由终端自选
|
||||
// StationConfig 工位配置:启动时由 yaml 加载到内存,固定本终端工位号(1~12)。
|
||||
// 一个工位一个终端,终端不允许修改工位;变更工位只能改配置并重启服务。
|
||||
type StationConfig struct {
|
||||
Code string `json:",optional"` // 固定工位号(如 "3");留空则由终端自选 1~12
|
||||
ScrewCount int `json:",default=10"` // 本工位需拧紧的螺丝颗数(进度 9/10 用)
|
||||
StationNo int `json:",default=1"` // 固定工位号(1~12),启动时校验范围
|
||||
ScrewCount int `json:",default=10"` // 本工位需拧紧的螺丝颗数(进度 9/10 用)
|
||||
}
|
||||
|
||||
// PdfCacheConfig 工艺文件 PDF 本地预缓存目录
|
||||
|
||||
@@ -5,10 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bj_power_workstation/internal/auth"
|
||||
"bj_power_workstation/internal/svc"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func healthHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
@@ -60,11 +57,11 @@ func loginHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
|
||||
// configHandler GET /api/config 返回工位配置:
|
||||
// 固定工位号 stationNo(Station.Code 为空则前端可自选 1~12)、本工位需拧紧的螺丝颗数。
|
||||
// 固定工位号 stationNo(1~12,由 yaml 配置在启动时加载,终端不可修改)、本工位需拧紧的螺丝颗数。
|
||||
func configHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ok(w, map[string]any{
|
||||
"stationNo": atoi(ctx.Config.Station.Code, 0),
|
||||
"stationNo": ctx.Config.Station.StationNo,
|
||||
"screwCount": ctx.Config.Station.ScrewCount,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,17 +34,21 @@ func taskCurrentHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// workloadHandler 代理 MES GET /api/internal/station/workload?operator=&from=&to=(块6)。
|
||||
// 工位终端「我的工作量」仅查询当前登录人数据;MES 响应体原样透传。
|
||||
// workloadHandler 代理 MES GET /api/internal/station/workload?operator=&stationNo=&from=&to=(块6)。
|
||||
// 工位终端「我的工作量」查询当前登录人全部工位的工作,stationNo 为空=全部工位;MES 响应体原样透传。
|
||||
func workloadHandler(ctx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
operator := strings.TrimSpace(r.URL.Query().Get("operator"))
|
||||
stationNo := strings.TrimSpace(r.URL.Query().Get("stationNo"))
|
||||
from := strings.TrimSpace(r.URL.Query().Get("from"))
|
||||
to := strings.TrimSpace(r.URL.Query().Get("to"))
|
||||
params := url.Values{}
|
||||
if operator != "" {
|
||||
params.Set("operator", operator)
|
||||
}
|
||||
if stationNo != "" {
|
||||
params.Set("stationNo", stationNo)
|
||||
}
|
||||
if from != "" {
|
||||
params.Set("from", from)
|
||||
}
|
||||
|
||||
@@ -28,11 +28,9 @@ type MockSource struct {
|
||||
dockCode string // 模拟枪挂载的工位
|
||||
}
|
||||
|
||||
// NewMockSource dockCode 为空时回落到 DOCK01。
|
||||
func NewMockSource(db *store.Store, mc *mes.Client, dockCode string) *MockSource {
|
||||
if dockCode == "" {
|
||||
dockCode = "DOCK01"
|
||||
}
|
||||
// NewMockSource 模拟枪挂载在固定工位(stationNo 1~12),DOCK 编码为 DOCK01~DOCK12。
|
||||
func NewMockSource(db *store.Store, mc *mes.Client, stationNo int) *MockSource {
|
||||
dockCode := fmt.Sprintf("DOCK%02d", stationNo)
|
||||
return &MockSource{db: db, mes: mc, dockCode: dockCode}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,13 @@ func main() {
|
||||
var c config.Config
|
||||
conf.MustLoad(*configFile, &c)
|
||||
|
||||
// 工位号固定(1~12):一个工位一个终端,终端不允许修改工位。
|
||||
// 启动时加载到内存并强校验,变更工位只能改配置文件后重启服务。
|
||||
if c.Station.StationNo < 1 || c.Station.StationNo > 12 {
|
||||
panic(fmt.Sprintf("Station.StationNo 必须在 1~12 之间,当前配置为 %d(工位由配置文件固定,请修改 yaml 后重启)", c.Station.StationNo))
|
||||
}
|
||||
logx.Infof("固定工位号: %d, 本工位螺丝颗数: %d", c.Station.StationNo, c.Station.ScrewCount)
|
||||
|
||||
staticFS, err := fs.Sub(webFS, "web/static")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("加载内嵌前端资源失败: %v", err))
|
||||
@@ -59,7 +66,7 @@ func main() {
|
||||
|
||||
// 内置模拟拧紧枪数据源(Sim.Enable=true 时启动;正式丹尼科尔协议接入后由 NetSource 替代)
|
||||
if c.Sim.Enable {
|
||||
go tightening.NewMockSource(st, mc, c.Station.Code).Start(rootCtx)
|
||||
go tightening.NewMockSource(st, mc, c.Station.StationNo).Start(rootCtx)
|
||||
}
|
||||
|
||||
// 工艺文件 PDF 本地预缓存
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>北京动力 · 工位终端</title>
|
||||
<title>工位终端</title>
|
||||
<script type="module" crossorigin src="/assets/index-CpYENLUl.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dz4RGjfJ.css">
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user