钢铁生产管理系统这个方向,很多做Web开发的同行可能觉得离自己挺远,但真把需求捋完你会发现,它本质上就是一套带行业属性的制造执行系统。我从去年开始用Vue、Node.js和Element UI把这套系统完整落地了一次,覆盖生产计划、工单跟踪、质量检验、设备管理和报表统计这些核心模块。整个过程里踩了不少坑,也总结出一些可以直接复用的套路。这篇文章我会把整个系统的设计和实现过程从头拆开讲,包括技术选型背后的思考、数据库怎么建模、前端页面怎么做、后端接口怎么组织,以及开发调试时常见的坑,希望能帮到正在做毕业设计、准备入行工业软件方向,或者需要在公司独立扛一个小型管理系统的同学。
1. 项目背景与系统定位分析
1.1 钢铁生产系统到底要解决什么问题
钢铁企业的生产环境和普通互联网产品差别很大,它是一条连续不断的流程线,从炼铁开始,经过炼钢、连铸、轧钢,最后到精整包装,中间任何一个环节出问题都会波及后面所有工序。我调研了几个现场之后发现,很多分厂当时的状态是:计划靠Excel排,产量靠人工统计,质量数据散落在不同的纸质记录本里,设备出问题靠电话层层上报,报表月底突击加班汇总。这种模式下,信息传递的滞后非常明显,领导问今天的产量,可能要等到第二天甚至第三天才能给出准确数字。
这套系统要解决的核心问题,其实就是三件事:生产过程看得见、质量信息追得回、报表统计算得快。生产计划下达到各个班组,工单推送到岗位,每个炉次、每个工序的关键节点都要能查到实时的完成情况;质量检验的取样记录、化验成分、判定结论集中管理,真出现问题可以按炉次号一路追溯到上游原料和工艺参数;产量报表、质量报表、设备报表自动从业务数据里汇总,不用再靠人工手动算。
既然定位是内部使用的制造管理系统,它的核心需求是功能完整、数据准确、操作简单,而不是并发量多高、界面多花哨。实际使用人数大概在几十到一百人,单日操作频率也不算极端,这就为技术选型定了一个很重要的基调。
1.2 技术选型:为什么是Vue加Node.js加Element UI
这套系统选型的时候,我认真对比过几个方向:Spring Boot加Vue、Python Django加Vue、还有最后定的Node.js加Vue。先说结论,如果是团队里Java背景的人多、系统后续要接入大量硬件设备和高并发场景,Spring Boot确实是更稳妥的选择。但我这个项目的情况是团队规模小、开发周期紧、前后端最好同一套语言维护,Node.js的高效异步I/O能力和JavaScript全栈特性就非常合适了。
Vue作为前端框架,最大的优势是渐进式——你不需要一下子把全家桶都加上,可以按需引入路由、状态管理这些模块。它的模板语法和响应式机制对做管理后台非常顺手,而且中文社区活跃,遇到问题基本能搜到现成的解决方案。Element UI则把后台管理系统里最常用的表格、表单、弹窗、日期选择、分页这些组件都做得很成熟,直接拿来用,能省掉大量重复的样式和交互开发时间。
这套组合踩过的坑也值得提前说。Element UI目前对Vue 2的支持最稳定,如果项目初始化的时候直接选了Vue 3,那配套的组件库就要换成Element Plus,很多API和插槽写法对不上,迁移的工作量不小。我建议做类似项目时直接用Vue 2加Element UI这套经典组合,等到熟练之后再考虑上Vue 3。
2. 业务模型梳理与数据库设计
2.1 钢铁生产的核心业务流程拆解
不懂业务直接写代码是工业软件项目里最容易翻车的地方。我在设计数据库之前,先跟着生产管理人员捋了一遍完整的工艺路线:高炉炼铁出铁水,铁水经过预处理脱硫,进转炉吹炼,出钢后到LF精炼炉或者RH真空炉调整成分和温度,然后上连铸机浇铸成方坯或板坯,铸坯再送轧钢产线轧制成材,最后精整、打包、入库发货。每一道工序都有关键参数要记录,比如转炉的吹炼时间、精炼的温度和成分调整记录、连铸的拉速和液位。
围绕这条工艺主线,系统功能模块我拆成了六个部分:生产计划管理、工单管理、生产实绩跟踪、质量检验管理、设备管理、报表统计中心。生产计划负责把月度订单分解成周计划和日班次计划,工单模块把计划转成每个炉次可执行的任务,生产实绩模块各工序回报开完工和产量,质量模块记录取样送检和判定结果,设备模块管台账和点检维修记录,报表中心自动汇总各类统计指标。
这里有一个钢铁行业特有的概念需要特别理解清楚:炉次。一炉钢就是一个生产批次,有唯一的炉次号,从转炉开始一路跟着走完所有工序。很多字段在设计时都应该考虑以炉次为维度来关联,比如工单表里的furnace_no,质检表里的furnace_no,实绩回报表里的furnace_no。这个概念对应到代码层面,就是一张张三表之间的外键关联,但它背后代表的是现场追溯的实际业务逻辑,设计数据库的时候一定要有这个意识。
2.2 数据库表结构与关键字段设计
数据库选了MySQL,存储引擎InnoDB,字符集utf8mb4。核心原因很简单:MySQL部署运维成本低,InnoDB支持事务,钢铁生产系统的工单状态流转、实绩回报这些关键操作必须保证数据一致性。字符集用utf8mb4是因为它完整支持中文和特殊符号,避免后续出现乱码和索引问题。
整个系统前后一共设计了二十多张表,最重要的几张我单独说一下。
用户权限体系:三张表,sys_user、sys_role、sys_user_role,再加一张菜单权限表sys_permission。用户和角色是多对多关系,角色和菜单权限也是多对多。现场的情况通常是厂长有全部报表权限,车间主任能管理计划和工单,操作工只能录入实绩和查看自己班组的任务,质检员只开放质检模块,所以这套RBAC权限模型是必须的,不能图省事只做单层账号。
生产计划表plan_production的关键字段:计划编号、计划类型(月度/周/日)、产品种类(板材/棒材/线材)、钢种牌号、计划数量、计划开始和结束时间、下发状态、负责车间。这里的钢种牌号是一个持续更新的字典表,新钢种开发出来就会增加一条,所以单独建了steel_grade_dict表。
工单表work_order是业务核心,我给出它的核心结构:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | bigint | 主键 |
| order_no | varchar(32) | 工单号,规则W+日期+序号 |
| plan_id | bigint | 关联生产计划 |
| furnace_no | varchar(32) | 炉次号 |
| steel_grade | varchar(32) | 钢种 |
| spec | varchar(64) | 规格 |
| process_flow | varchar(128) | 工序路线 |
| workshop | varchar(32) | 车间 |
| team | varchar(32) | 班组 |
| plan_qty | decimal(10,2) | 计划数量 |
| actual_qty | decimal(10,2) | 完成数量 |
| status | tinyint | 0待生产 1生产中 2完成 3暂停 4异常 |
| start_time | datetime | 开始时间 |
| end_time | datetime | 完成时间 |
| create_by | varchar(32) | 创建人 |
| create_time | datetime | 创建时间 |
工单状态这里我用的是状态字段加状态流转接口的方式,比如POST /api/work-orders/{id}/start表示开工,POST /api/work-orders/{id}/complete表示完工。每一个流转接口里都会做状态校验,防止从待生产直接跳到已完成这种非法操作。
生产实绩表prod_actual记录每个工序实际发生的产量和消耗,字段包括关联工单ID、工序名称、完成量、合格量、废品量、班次、操作人、回报时间。这张表是后面所有产量报表和合格率统计的数据源,必须保证每次回报都有完整的工单ID和工序信息。
质量检验表quality_inspection的核心字段包括检验单号、工单ID、炉次号、取样时间、检验项目(C、Si、Mn、P、S等化学成分)、实测值、标准值、判定结果、检验员。质量判定结果一般有合格、让步接收、降级处理、判废四种,这个枚举值在前后端都要做统一字典。
这张工单表看起来简单,但其实我在字段命名上特意做了几个约定:所有时间字段统一叫xxx_time,所有创建信息统一叫create_by和create_time,所有金额和数量字段用decimal不用float。这些约定在当时看来是顺手为之,到后面写统计SQL和对接报表的时候,真的能省下很多来回确认字段的时间。
3. 前端工程化与核心页面实现
3.1 Vue项目搭建与环境准备
前端项目我是用Vue CLI初始化的,命令很简单:
npm install -g @vue/cli vue create steel-front版本这里特别说一下,我用的Node.js 16 LTS版本,配合Vue CLI 4.x和Vue 2.6,这套组合非常稳定。如果你刚安装了最新的Node.js 20或22版本,再来跑老项目的依赖,经常会遇到node-sass编译失败这种问题,最好先确认版本兼容性。做生产系统我建议求稳不求新,能跑得动、跑得稳比什么都强。
项目创建完以后,核心依赖安装命令:
npm install element-ui@2.15.x npm install axios npm install vue-router@3.x npm install vuex@3.x npm install sass sass-loader -D npm install echartsnpm源如果觉得下载慢,可以临时切换成国内镜像,命令是npm config set registry https://registry.npmmirror.com。这里有个小经验:项目里最好是统一用.npmrc文件来配置源,这样团队其他人拉代码后安装依赖时用的就是同一个配置,不会出现有人装不上依赖的问题。
然后按模块组织前端目录:
src/ ├── api/ 接口请求定义 ├── assets/ 静态资源 ├── components/ 公共组件 ├── layout/ 整体布局框架 ├── router/ 路由配置 ├── store/ Vuex模块 ├── views/ 页面组件 │ ├── plan/ 生产计划 │ ├── work-order/ 工单管理 │ ├── quality/ 质量检验 │ ├── equipment/ 设备管理 │ ├── report/ 报表中心 │ └── system/ 系统管理 ├── utils/ 工具函数 └── App.vue3.2 Axios封装、路由守卫与状态管理
Axios封装这一步千万别省。生产系统里的接口几十个,如果每个页面都直接调axios,到后期改请求头、加统一错误处理、处理token过期,工作量会非常痛苦。我的做法是统一封装一个service实例,拦截器里做三件事:请求前自动带上token,响应后统一解包数据,遇到HTTP 401自动跳登录页。
import axios from 'axios' import { Message } from 'element-ui' import router from '@/router' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 15000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { Message.error(res.message || '请求异常') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { localStorage.removeItem('token') router.push('/login') } else { Message.error(error.message || '网络异常,请稍后重试') } return Promise.reject(error) } ) export default service路由守卫的作用是在前端拦截未登录用户,所有页面在跳转前先检查本地是否有token,没有token就跳登录页。同时根据用户角色过滤菜单权限,这一步和后端接口的JWT鉴权是前后双重保险,前端是用户体验层面的控制,后端才是真正的安全边界。
Vuex我按业务模块拆分了store,user模块存用户信息和权限点,plan模块存当前计划筛选条件,order模块存工单列表状态。钢铁生产系统的特点是很多页面之间存在联动,比如在计划页面把计划状态改成已下发,工单页面就要能立刻看到对应新生成的工单,这种跨页面的状态同步用Vuex处理比用事件总线干净得多。
3.3 核心业务页面拆解与实现
生产计划看板是系统里最直观的页面。它的主区域是一张大的计划列表,通过日期范围、车间、计划类型、状态筛选,能查到每个计划的下发情况和执行进度。这里我用Element UI的el-table展示数据,计划数量列和完成数量列用进度条组件el-progress做了可视化,一眼就能看出来哪些计划严重滞后,哪些已经超额完成。表头固定、操作列固定,横向滚动的时候依然能看清关键字段。这个页面还放了一个统计卡片行,顶部显示本月总计划量、本月完成量、完成率、在制计划数四个核心指标,领导进系统第一眼看到的就是这个,反馈很好。
工单管理页面是操作最频繁的模块。列表支持按工序、班组、状态多条件筛选,关键字模糊搜索工单号和炉次号。新增工单的表单里,钢种和规格用el-select从字典表加载,工艺路线用级联选择器,从预设的工序组合里选,这样能减少手输错误。工单详情我用el-drawer从右侧滑出,里面放炉次的基本信息、当前工序状态、每个工序的回填实绩,以及最近一次质检结果的摘要。工单的一些关键操作,比如暂停、异常上报、恢复生产,都用了带二次确认的按钮,防止误操作。
质量检验页面我做了两段式布局,上半部分是用检记录列表,包括取样时间和取样工序;下半部分是选中检验单的详细化验结果,用el-tabs切换化学成分、力学性能、金相检验这几个标签页。每个化验项目都有实测值、标准上下限和单项判定,最后汇总出整个检验单的综合判定结论。检验报告支持弹窗预览PDF,这里就是用el-dialog里嵌iframe,地址指向后端生成的PDF报告接口。
设备管理页面除了设备台账和维护记录,我还加了一个设备状态监控卡片视图,每台设备用不同颜色标识运行中、待机、维修中、停机四种状态。某个车间装了摄像头之后,我把实时监控画面直接嵌到设备详情页里,用video标签配合hls.js播放摄像头流的m3u8地址,现场人员不用专门打开监控软件就能在系统里看到产线实时情况,这个小功能当时被评为最实用功能之一。
报表中心页面是给管理层看的。产量日报用ECharts柱状图展示各班组当天的计划量和实际完成量对比,质量趋势用折线图展示近三十天的合格率变化,设备利用率用饼图展示。所有图表都有时间范围筛选器,支持按车间、产线下钻。导出Excel的功能我放在后端实现,前端点导出按钮直接下载后端生成的文件,不占用浏览器内存,大数据量场景也不会卡页面。
3.4 Element UI的定制与细节处理
Element UI默认主题是亮蓝色,钢铁企业用这个颜色不算难看,但总觉得差点工业感。我通过定制SCSS变量把主题色改成了偏沉稳的深蓝灰色,菜单左侧栏做了深色背景处理,整体观感比默认主题好很多。具体做法是在项目里建一个element-variables.scss文件,覆盖$primary-color这类变量,然后重新编译组件样式。
按需引入这块我建议做一下,虽然全量引入Element UI代码也不复杂,但打包体积能从接近1MB降到400KB左右,对页面首次加载速度的提升还是很明显的。配合babel-plugin-component插件,在babel.config.js里配置好就可以按需加载了。
实际开发中Element UI有一个高频需求:表格列文字超出后隐藏,鼠标悬浮显示完整内容。钢铁系统里钢种规格、工艺路线、化学成分这些字段经常很长,直接展示会把表格撑得很难看。我在项目里封装了一个公共的ellipsis-tooltip组件,内部用el-tooltip包裹一个超出隐藏的span,计算列宽,内容溢出时自动显示tooltip,没溢出就不弹,这样所有表格的列配置都可以复用这个组件。
4. 后端服务与接口设计实现
4.1 Node.js服务端框架搭建与项目结构
后端我用Express框架,Node.js的Web框架里它的生态最成熟,中间件丰富,资料最多,遇到问题基本都能快速找到解决方案。Koa我也试过,语法更现代,但Express的route处理方式更直观,团队其他人接手也更容易上手,所以最后还是定了Express。
后端目录结构是按分层思想组织的:
server/ ├── app.js 应用入口 ├── config/ │ ├── index.js 配置项 │ └── db.js 数据库连接池 ├── routes/ 路由定义 │ ├── auth.js │ ├── plan.js │ ├── workOrder.js │ ├── quality.js │ ├── equipment.js │ └── report.js ├── controllers/ 控制层 ├── services/ 业务逻辑层 ├── models/ 数据访问层 ├── middlewares/ 中间件 │ ├── auth.js JWT鉴权 │ ├── errorHandler.js 错误处理 │ └── logger.js 访问日志 └── utils/配置文件里我习惯把所有环境相关的参数都集中起来,比如端口号、数据库连接信息、JWT密钥、token过期时间。每个环境的配置用NODE_ENV区分,开发环境连本地数据库,生产环境连服务器数据库,部署的时候只需要设置环境变量,不用改代码。
数据库连接用的是mysql2库,重点是启用连接池,避免每次请求都重新建立数据库连接,在高频次访问下性能差别很明显。
const mysql = require('mysql2/promise') const pool = mysql.createPool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, waitForConnections: true, connectionLimit: 10, queueLimit: 0, charset: 'utf8mb4' }) module.exports = pool4.2 接口设计与统一响应规范
后端接口我全部采用RESTful风格设计,统一以/api/v1开头,按资源划分路由。例如工单模块的接口是这样:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/v1/work-orders | 工单分页查询 |
| GET | /api/v1/work-orders/:id | 工单详情 |
| POST | /api/v1/work-orders | 新建工单 |
| PUT | /api/v1/work-orders/:id | 更新工单 |
| POST | /api/v1/work-orders/:id/start | 工单开工 |
| POST | /api/v1/work-orders/:id/complete | 工单完工 |
| POST | /api/v1/work-orders/:id/hold | 工单暂停 |
| GET | /api/v1/work-orders/export | 工单导出Excel |
统一响应格式是一个很关键的约定:
{ "code": 200, "message": "success", "data": {} }所有接口都遵循这个格式,前端Axios拦截器解包data字段,后端通过一个wrapResponse工具函数包装返回值,错误时code用非200值并带上具体错误信息。这个约定一旦建立起来,前后端联调效率会高很多,接口文档都不用写得特别细,一看格式就知道怎么解析。
4.3 JWT身份认证与权限控制
登录认证用的是JWT方案。用户输入用户名密码,后端校验通过后用jsonwebtoken签发token,token里携带用户ID和角色信息,设置过期时间一般为8到12小时,对应一个工作班次。密码存储必须做加密处理,我用的是bcryptjs,不能存明文密码,这是个基本底线。
const jwt = require('jsonwebtoken') const bcrypt = require('bcryptjs') async function login(username, password) { const user = await userModel.findByUsername(username) if (!user) throw new Error('用户不存在') const valid = await bcrypt.compare(password, user.password) if (!valid) throw new Error('密码错误') const token = jwt.sign( { userId: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '12h' } ) return { token, user: { id: user.id, name: user.name, role: user.role } } }权限控制中间件做了两层。第一层是token有效性校验,每个需要登录的接口都得过这个中间件,解析失败直接返回401。第二层是角色权限过滤,通过一个简单但够用的方式:初始化的时候把各角色的权限列表加载到内存里,中间件里判断当前用户角色是否包含请求所需权限码,不包含返回403。需要新增接口时在路由定义上加一个权限码即可。
4.4 关键业务接口的实现逻辑
工单查询接口是整个系统里最复杂的一个,因为它涉及多条件组合筛选、联表统计、排序和分页。看一个核心SQL就能理解设计思路:
SELECT wo.id, wo.order_no, wo.furnace_no, wo.steel_grade, wo.spec, wo.plan_qty, wo.status, wo.workshop, wo.team, COALESCE(SUM(pa.actual_qty), 0) AS actual_qty, COALESCE(SUM(pa.qualified_qty), 0) AS qualified_qty FROM work_order wo LEFT JOIN prod_actual pa ON wo.id = pa.order_id WHERE (wo.order_no LIKE ? OR wo.furnace_no LIKE ?) AND (wo.status = ? OR ? = -1) AND (wo.workshop = ? OR ? = '') GROUP BY wo.id ORDER BY wo.create_time DESC LIMIT ? OFFSET ?为什么用LEFT JOIN而不是INNER JOIN?因为工单还没报实绩的时候,实绩表里没有对应的记录,用INNER JOIN会把未开工的工单查丢,LEFT JOIN加上COALESCE函数把NULL转成0,才能保证列表里的每个工单都有结果行。这类细节在实际开发里非常容易踩坑,统计类接口只要join方向写错,数据就会少一截,而且很难排查。
新建工单的接口逻辑稍微复杂一点。前端传过来的数据先做字段格式校验和业务校验,比如钢种和规格必须在字典表里存在、计划数量大于零、同一计划下不允许重复创建同钢种同规格工单。校验通过后开启数据库事务,在work_order表插入主记录,同时按工艺路线拆分成多条工单工序记录,这步操作必须在一个事务里完成,任何一步失败都要整体回滚,不能出现工单建出来了工序记录却是空的这种脏数据。
报表导出接口用node-xlsx库生成Excel文件,后端把查询结果组织成行和列,直接在响应流里返回文件流,前端用a标签触发下载。这个方案对几十万行以内的数据量完全没有问题,如果以后报表数据量突破百万行,再考虑用异步任务生成文件存到服务器,前端轮询下载状态。
5. 开发调试中的常见问题与排查实录
5.1 环境问题:npm.ps1无法加载与Node.js安装报错
这个坑几乎每个用Windows开发的同事都遇到过。项目环境配置好后,执行npm install直接报错:
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本原因是PowerShell默认的执行策略是Restricted,不允许运行本地脚本文件。解决办法是在当前用户范围内开放RemoteSigned权限:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser这条命令执行完再重开一个PowerShell窗口,npm就能正常用了。如果是公司电脑管理员权限受限,也可以改用cmd运行npm,cmd没有这个执行策略限制。
还有一个常见问题是Node.js安装时报错2203,这个问题多数时候是安装程序没有足够的权限写入系统目录。处理办法是用管理员身份运行安装包,实在不行就把安装日志删干净,重启安装程序手动选择安装到非系统盘的目录,比如D:\nodejs。装完以后把node的全局目录配置好,npm config set prefix和npm config set cache指向自定义目录,避免后续权限问题。
5.2 Element UI表格固定列变透明怎么修复
有一次测试反馈,工单列表把操作列固定到右侧之后,横向滚动时整个操作列变得半透明,文字和按钮都能透到下一层,看上去非常怪。排查了很久,最终发现是固定列用的是position: sticky定位,而它的父级容器在某个页面加了transform样式,导致sticky定位失效,浏览器的渲染层级就乱了。
解决办法是把父容器上的transform样式去掉,如果有动画需求,改用opacity或者其他不影响定位属性的方案。Element UI的固定列组件本身没什么问题,问题基本都出在外部CSS干扰上。如果确实需要在父容器上保留transform,可以考虑升级到支持will-change的浏览器版本,或者在组件内部手动调z-index,但这些都是workaround,最干净的做法还是消除冲突的CSS。
5.3 文字超出隐藏和悬浮显示完整信息
生产系统里钢种名称、成分描述、工艺备注这些字段特别长,直接展示会破坏表格布局。我封装了公共的悬浮提示组件,核心思路是:单元格内容用一段带CSS类名的span包裹,设置overflow: hidden、text-overflow: ellipsis、white-space: nowrap三个属性,然后外层包一个el-tooltip。
有个细节要注意,el-tooltip的disabled属性应该根据文本是否真正溢出动态绑定,否则没溢出的单元格悬浮时也会弹提示框,体验很怪。判断是否溢出的方法是通过scrollWidth和clientWidth比较,这个逻辑放在组件mount和窗口大小变化时执行即可。
5.4 Element UI弹窗加载PDF预览
质量检验报告需要支持在线预览,我的实现是点击按钮打开el-dialog,里面放一个iframe,src指向后端生成的PDF文件地址。el-dialog打开时再动态设置iframe的src,不要提前加载,避免每次打开都重新请求一遍。如果PDF文件较大,可以加一个loading遮罩,用iframe的onload事件判断加载完成。
这里有一个需要注意的地方,iframe嵌入PDF本质上依赖浏览器内置的PDF插件,如果用户用的是一个没有PDF插件的极简浏览器,预览区域会空白。有条件的话可以用pdf.js库做更可控的渲染方案,但对于内部系统,iframe方案基本够用,省时省力。
5.5 Vue打包后布局异常问题
开发环境一切正常,npm run build打包部署到服务器后,发现登录页还能打开,但登录进去以后侧边栏错位、图片全部加载不出来。排查后定位到两个问题:第一个是vue-router用了history模式,服务器没有配置try_files规则,刷新非根路径时返回404,需要在Nginx里面加一段配置:
location / { try_files $uri $uri/ /index.html; }第二个是静态资源路径问题,打包后的文件如果部署在子目录下,默认的publicPath是/,所有资源都会从根路径加载。解决方法是vue.config.js里把publicPath设为相对路径./,这样资源路径会根据当前页面动态解析,兼顾不同部署位置。
5.6 设备监控实时视频的接入经验
设备监控页面接入摄像头实时画面时,我一开始直接在video标签里放摄像头流的m3u8地址,结果发现浏览器原生不支持HLS格式,根本播不出来。后来装了hls.js库,在video标签的loadedmetadata事件里,把m3u8流通过hls.js绑定到video上,才正常播放。这里有一个小经验:如果摄像头流是HTTP的,页面访问是HTTPS的话会有混合内容拦截,需要把页面协议和流协议保持统一,或者给摄像头流单独配置HTTPS代理。
钢铁厂现场的网络环境一般比较复杂,摄像头流可能跨网段,需要注意服务器是否能访问到摄像头所在网段,否则在办公室看到的永远是一块黑屏。测试环境我直接在设备详情页里加了一个跳转按钮,打开摄像头厂商的Web管理页,后来觉得体验不好,才改成iframe或者video内嵌的展示方案。
我自己做完这套系统最大的体会是,工业管理系统的技术栈其实是最不卡人的部分,真正花时间的是把业务流程搞清楚、把数据模型设计对。Vue加Node.js加Element UI这套组合,可以用很低的成本把一套可用的制造执行系统搭出来,后续如果要扩展模块、增加并发能力,也有清晰的演进路径。如果你正在做类似的项目,先把生产计划、工单、质量检验这三个核心模块跑通,系统基本就能投入使用了,再逐步加设备管理、报表分析这些外围模块。最后再提醒一句,Node.js版本尽量选LTS,Element UI搭配Vue 2用,版本匹配这种事看着不起眼,但真出问题的时候会浪费你整整一个下午。