From 543655d441f0570aa0fb9ef15200c43167f69893 Mon Sep 17 00:00:00 2001 From: zhoujianjin <204045053@qq.com> Date: Fri, 18 Sep 2026 18:54:54 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=A4=9A=E9=A1=B9=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- a.txt | 1 + bj_power_mes/ent/bomitem.go | 2 +- bj_power_mes/ent/inspectionrecord.go | 2 +- bj_power_mes/ent/migrate/schema.go | 2 + bj_power_mes/ent/mutation.go | 110 +++++- bj_power_mes/ent/runtime.go | 10 +- bj_power_mes/ent/station.go | 24 ++ bj_power_mes/ent/station/station.go | 20 ++ bj_power_mes/ent/station/where.go | 30 ++ bj_power_mes/ent/station_create.go | 50 +++ bj_power_mes/ent/station_update.go | 68 ++++ bj_power_mes/frontend/src/help.js | 49 +-- .../frontend/src/layouts/MainLayout.vue | 3 +- .../frontend/src/pages/AlertCenter.vue | 89 ++++- .../frontend/src/pages/Attachment.vue | 56 +-- bj_power_mes/frontend/src/pages/EventLog.vue | 9 - .../frontend/src/pages/ProductType.vue | 8 +- bj_power_mes/frontend/src/pages/QtyReport.vue | 73 ---- bj_power_mes/frontend/src/pages/Station.vue | 48 ++- bj_power_mes/frontend/src/router/index.js | 3 +- bj_power_mes/internal/db/db.go | 9 + bj_power_mes/internal/handler/perm.go | 3 +- bj_power_mes/internal/handler/processflow.go | 11 + .../internal/handler/production/alert.go | 21 +- .../internal/handler/production/workorder.go | 2 +- .../internal/handler/routes_production.go | 7 +- bj_power_mes/internal/handler/station_ops.go | 30 -- bj_power_mes/internal/logic/alert.go | 48 ++- bj_power_mes/internal/logic/processflow.go | 41 ++- bj_power_mes/internal/logic/product.go | 8 +- bj_power_mes/internal/logic/seed.go | 58 +++- bj_power_mes/internal/logic/station_ops.go | 43 +-- bj_power_mes/schema/station.go | 7 + .../src/components/MaterialSelect.vue | 4 +- bj_power_wms/frontend/src/help.js | 8 +- bj_power_wms/frontend/src/pages/BaseData.vue | 208 +++++++++-- bj_power_wms/frontend/src/pages/EventLog.vue | 9 - bj_power_wms/frontend/src/pages/Inbound.vue | 151 +++++--- bj_power_wms/frontend/src/pages/Inventory.vue | 17 +- bj_power_wms/frontend/src/pages/Outbound.vue | 12 +- bj_power_wms/frontend/src/pages/Stocktake.vue | 3 +- bj_power_wms/frontend/vite.config.js | 7 +- bj_power_wms/internal/handler/jwt.go | 6 +- bj_power_wms/internal/handler/material.go | 8 +- bj_power_wms/internal/handler/producttype.go | 14 +- bj_power_wms/internal/handler/routes.go | 2 +- bj_power_wms/internal/handler/zone.go | 325 ++++++++++++++---- 小周.md | 17 + 48 files changed, 1289 insertions(+), 447 deletions(-) create mode 100644 a.txt delete mode 100644 bj_power_mes/frontend/src/pages/QtyReport.vue create mode 100644 小周.md diff --git a/a.txt b/a.txt new file mode 100644 index 0000000..c2d4602 --- /dev/null +++ b/a.txt @@ -0,0 +1 @@ +hello diff --git a/bj_power_mes/ent/bomitem.go b/bj_power_mes/ent/bomitem.go index ed36b1b..6d86f50 100644 --- a/bj_power_mes/ent/bomitem.go +++ b/bj_power_mes/ent/bomitem.go @@ -20,7 +20,7 @@ type BomItem struct { ID int `json:"id,omitempty"` // 产品编码 ProductCode string `json:"productCode,omitempty"` - // BOM名称:同一型号多份并存(如 厂家A/厂家B/含半成品),工单按名选用 + // 物料清单名称:同一型号多份并存(如 厂家A/厂家B/含半成品),工单按名选用 BomName string `json:"bomName,omitempty"` // 物料编码 MaterialCode string `json:"materialCode,omitempty"` diff --git a/bj_power_mes/ent/inspectionrecord.go b/bj_power_mes/ent/inspectionrecord.go index fc49dd2..7ebdbcc 100644 --- a/bj_power_mes/ent/inspectionrecord.go +++ b/bj_power_mes/ent/inspectionrecord.go @@ -18,7 +18,7 @@ type InspectionRecord struct { config `json:"-"` // ID of the ent. ID int `json:"id,omitempty"` - // 记录类型 CHECKIN/POINT/PROCESS/DONE/ALARM/INCOMING/FINAL + // 记录类型 CHECKIN/POINT/PROCESS/DONE/ALARM/INCOMING/FINAL/QC_PROCESS Category string `json:"category,omitempty"` // 工位号 StationNo string `json:"stationNo,omitempty"` diff --git a/bj_power_mes/ent/migrate/schema.go b/bj_power_mes/ent/migrate/schema.go index 6b9418c..0d042d2 100644 --- a/bj_power_mes/ent/migrate/schema.go +++ b/bj_power_mes/ent/migrate/schema.go @@ -689,6 +689,8 @@ var ( {Name: "flow_id", Type: field.TypeInt, Nullable: true}, {Name: "station_type", Type: field.TypeString, Size: 20, Default: "LINE"}, {Name: "status", Type: field.TypeString, Size: 20, Default: "ENABLED"}, + {Name: "is_builtin", Type: field.TypeBool, Default: false}, + {Name: "has_dock", Type: field.TypeBool, Default: false}, {Name: "created_at", Type: field.TypeTime}, } // StationTable holds the schema information for the "station" table. diff --git a/bj_power_mes/ent/mutation.go b/bj_power_mes/ent/mutation.go index 2f24262..7e16067 100644 --- a/bj_power_mes/ent/mutation.go +++ b/bj_power_mes/ent/mutation.go @@ -17429,6 +17429,8 @@ type StationMutation struct { addflowId *int stationType *string status *string + isBuiltin *bool + hasDock *bool createdAt *time.Time clearedFields map[string]struct{} done bool @@ -17774,6 +17776,78 @@ func (m *StationMutation) ResetStatus() { m.status = nil } +// SetIsBuiltin sets the "isBuiltin" field. +func (m *StationMutation) SetIsBuiltin(b bool) { + m.isBuiltin = &b +} + +// IsBuiltin returns the value of the "isBuiltin" field in the mutation. +func (m *StationMutation) IsBuiltin() (r bool, exists bool) { + v := m.isBuiltin + if v == nil { + return + } + return *v, true +} + +// OldIsBuiltin returns the old "isBuiltin" field's value of the Station entity. +// If the Station object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *StationMutation) OldIsBuiltin(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIsBuiltin is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIsBuiltin requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIsBuiltin: %w", err) + } + return oldValue.IsBuiltin, nil +} + +// ResetIsBuiltin resets all changes to the "isBuiltin" field. +func (m *StationMutation) ResetIsBuiltin() { + m.isBuiltin = nil +} + +// SetHasDock sets the "hasDock" field. +func (m *StationMutation) SetHasDock(b bool) { + m.hasDock = &b +} + +// HasDock returns the value of the "hasDock" field in the mutation. +func (m *StationMutation) HasDock() (r bool, exists bool) { + v := m.hasDock + if v == nil { + return + } + return *v, true +} + +// OldHasDock returns the old "hasDock" field's value of the Station entity. +// If the Station object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *StationMutation) OldHasDock(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldHasDock is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldHasDock requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldHasDock: %w", err) + } + return oldValue.HasDock, nil +} + +// ResetHasDock resets all changes to the "hasDock" field. +func (m *StationMutation) ResetHasDock() { + m.hasDock = nil +} + // SetCreatedAt sets the "createdAt" field. func (m *StationMutation) SetCreatedAt(t time.Time) { m.createdAt = &t @@ -17844,7 +17918,7 @@ func (m *StationMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *StationMutation) Fields() []string { - fields := make([]string, 0, 6) + fields := make([]string, 0, 8) if m.stationNo != nil { fields = append(fields, station.FieldStationNo) } @@ -17860,6 +17934,12 @@ func (m *StationMutation) Fields() []string { if m.status != nil { fields = append(fields, station.FieldStatus) } + if m.isBuiltin != nil { + fields = append(fields, station.FieldIsBuiltin) + } + if m.hasDock != nil { + fields = append(fields, station.FieldHasDock) + } if m.createdAt != nil { fields = append(fields, station.FieldCreatedAt) } @@ -17881,6 +17961,10 @@ func (m *StationMutation) Field(name string) (ent.Value, bool) { return m.StationType() case station.FieldStatus: return m.Status() + case station.FieldIsBuiltin: + return m.IsBuiltin() + case station.FieldHasDock: + return m.HasDock() case station.FieldCreatedAt: return m.CreatedAt() } @@ -17902,6 +17986,10 @@ func (m *StationMutation) OldField(ctx context.Context, name string) (ent.Value, return m.OldStationType(ctx) case station.FieldStatus: return m.OldStatus(ctx) + case station.FieldIsBuiltin: + return m.OldIsBuiltin(ctx) + case station.FieldHasDock: + return m.OldHasDock(ctx) case station.FieldCreatedAt: return m.OldCreatedAt(ctx) } @@ -17948,6 +18036,20 @@ func (m *StationMutation) SetField(name string, value ent.Value) error { } m.SetStatus(v) return nil + case station.FieldIsBuiltin: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIsBuiltin(v) + return nil + case station.FieldHasDock: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetHasDock(v) + return nil case station.FieldCreatedAt: v, ok := value.(time.Time) if !ok { @@ -18055,6 +18157,12 @@ func (m *StationMutation) ResetField(name string) error { case station.FieldStatus: m.ResetStatus() return nil + case station.FieldIsBuiltin: + m.ResetIsBuiltin() + return nil + case station.FieldHasDock: + m.ResetHasDock() + return nil case station.FieldCreatedAt: m.ResetCreatedAt() return nil diff --git a/bj_power_mes/ent/runtime.go b/bj_power_mes/ent/runtime.go index ffaf631..7c02f4e 100644 --- a/bj_power_mes/ent/runtime.go +++ b/bj_power_mes/ent/runtime.go @@ -1077,8 +1077,16 @@ func init() { station.DefaultStatus = stationDescStatus.Default.(string) // station.StatusValidator is a validator for the "status" field. It is called by the builders before save. station.StatusValidator = stationDescStatus.Validators[0].(func(string) error) + // stationDescIsBuiltin is the schema descriptor for isBuiltin field. + stationDescIsBuiltin := stationFields[6].Descriptor() + // station.DefaultIsBuiltin holds the default value on creation for the isBuiltin field. + station.DefaultIsBuiltin = stationDescIsBuiltin.Default.(bool) + // stationDescHasDock is the schema descriptor for hasDock field. + stationDescHasDock := stationFields[7].Descriptor() + // station.DefaultHasDock holds the default value on creation for the hasDock field. + station.DefaultHasDock = stationDescHasDock.Default.(bool) // stationDescCreatedAt is the schema descriptor for createdAt field. - stationDescCreatedAt := stationFields[6].Descriptor() + stationDescCreatedAt := stationFields[8].Descriptor() // station.DefaultCreatedAt holds the default value on creation for the createdAt field. station.DefaultCreatedAt = stationDescCreatedAt.Default.(func() time.Time) // stationDescID is the schema descriptor for id field. diff --git a/bj_power_mes/ent/station.go b/bj_power_mes/ent/station.go index 562fec6..d4b64bb 100644 --- a/bj_power_mes/ent/station.go +++ b/bj_power_mes/ent/station.go @@ -27,6 +27,10 @@ type Station struct { StationType string `json:"stationType,omitempty"` // Status holds the value of the "status" field. Status string `json:"status,omitempty"` + // 内置工位:不可改类型/不可删除 + IsBuiltin bool `json:"isBuiltin,omitempty"` + // 是否有接驳台(实体位置,内置不可改) + HasDock bool `json:"hasDock,omitempty"` // CreatedAt holds the value of the "createdAt" field. CreatedAt time.Time `json:"createdAt,omitempty"` selectValues sql.SelectValues @@ -37,6 +41,8 @@ func (*Station) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { + case station.FieldIsBuiltin, station.FieldHasDock: + values[i] = new(sql.NullBool) case station.FieldID, station.FieldStationNo, station.FieldFlowId: values[i] = new(sql.NullInt64) case station.FieldName, station.FieldStationType, station.FieldStatus: @@ -94,6 +100,18 @@ func (_m *Station) assignValues(columns []string, values []any) error { } else if value.Valid { _m.Status = value.String } + case station.FieldIsBuiltin: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field isBuiltin", values[i]) + } else if value.Valid { + _m.IsBuiltin = value.Bool + } + case station.FieldHasDock: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field hasDock", values[i]) + } else if value.Valid { + _m.HasDock = value.Bool + } case station.FieldCreatedAt: if value, ok := values[i].(*sql.NullTime); !ok { return fmt.Errorf("unexpected type %T for field createdAt", values[i]) @@ -151,6 +169,12 @@ func (_m *Station) String() string { builder.WriteString("status=") builder.WriteString(_m.Status) builder.WriteString(", ") + builder.WriteString("isBuiltin=") + builder.WriteString(fmt.Sprintf("%v", _m.IsBuiltin)) + builder.WriteString(", ") + builder.WriteString("hasDock=") + builder.WriteString(fmt.Sprintf("%v", _m.HasDock)) + builder.WriteString(", ") builder.WriteString("createdAt=") builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) builder.WriteByte(')') diff --git a/bj_power_mes/ent/station/station.go b/bj_power_mes/ent/station/station.go index db51902..0249d10 100644 --- a/bj_power_mes/ent/station/station.go +++ b/bj_power_mes/ent/station/station.go @@ -23,6 +23,10 @@ const ( FieldStationType = "station_type" // FieldStatus holds the string denoting the status field in the database. FieldStatus = "status" + // FieldIsBuiltin holds the string denoting the isbuiltin field in the database. + FieldIsBuiltin = "is_builtin" + // FieldHasDock holds the string denoting the hasdock field in the database. + FieldHasDock = "has_dock" // FieldCreatedAt holds the string denoting the createdat field in the database. FieldCreatedAt = "created_at" // Table holds the table name of the station in the database. @@ -37,6 +41,8 @@ var Columns = []string{ FieldFlowId, FieldStationType, FieldStatus, + FieldIsBuiltin, + FieldHasDock, FieldCreatedAt, } @@ -63,6 +69,10 @@ var ( DefaultStatus string // StatusValidator is a validator for the "status" field. It is called by the builders before save. StatusValidator func(string) error + // DefaultIsBuiltin holds the default value on creation for the "isBuiltin" field. + DefaultIsBuiltin bool + // DefaultHasDock holds the default value on creation for the "hasDock" field. + DefaultHasDock bool // DefaultCreatedAt holds the default value on creation for the "createdAt" field. DefaultCreatedAt func() time.Time // IDValidator is a validator for the "id" field. It is called by the builders before save. @@ -102,6 +112,16 @@ func ByStatus(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldStatus, opts...).ToFunc() } +// ByIsBuiltin orders the results by the isBuiltin field. +func ByIsBuiltin(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIsBuiltin, opts...).ToFunc() +} + +// ByHasDock orders the results by the hasDock field. +func ByHasDock(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldHasDock, opts...).ToFunc() +} + // ByCreatedAt orders the results by the createdAt field. func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() diff --git a/bj_power_mes/ent/station/where.go b/bj_power_mes/ent/station/where.go index 5165e0d..8ab1b9b 100644 --- a/bj_power_mes/ent/station/where.go +++ b/bj_power_mes/ent/station/where.go @@ -79,6 +79,16 @@ func Status(v string) predicate.Station { return predicate.Station(sql.FieldEQ(FieldStatus, v)) } +// IsBuiltin applies equality check predicate on the "isBuiltin" field. It's identical to IsBuiltinEQ. +func IsBuiltin(v bool) predicate.Station { + return predicate.Station(sql.FieldEQ(FieldIsBuiltin, v)) +} + +// HasDock applies equality check predicate on the "hasDock" field. It's identical to HasDockEQ. +func HasDock(v bool) predicate.Station { + return predicate.Station(sql.FieldEQ(FieldHasDock, v)) +} + // CreatedAt applies equality check predicate on the "createdAt" field. It's identical to CreatedAtEQ. func CreatedAt(v time.Time) predicate.Station { return predicate.Station(sql.FieldEQ(FieldCreatedAt, v)) @@ -369,6 +379,26 @@ func StatusContainsFold(v string) predicate.Station { return predicate.Station(sql.FieldContainsFold(FieldStatus, v)) } +// IsBuiltinEQ applies the EQ predicate on the "isBuiltin" field. +func IsBuiltinEQ(v bool) predicate.Station { + return predicate.Station(sql.FieldEQ(FieldIsBuiltin, v)) +} + +// IsBuiltinNEQ applies the NEQ predicate on the "isBuiltin" field. +func IsBuiltinNEQ(v bool) predicate.Station { + return predicate.Station(sql.FieldNEQ(FieldIsBuiltin, v)) +} + +// HasDockEQ applies the EQ predicate on the "hasDock" field. +func HasDockEQ(v bool) predicate.Station { + return predicate.Station(sql.FieldEQ(FieldHasDock, v)) +} + +// HasDockNEQ applies the NEQ predicate on the "hasDock" field. +func HasDockNEQ(v bool) predicate.Station { + return predicate.Station(sql.FieldNEQ(FieldHasDock, v)) +} + // CreatedAtEQ applies the EQ predicate on the "createdAt" field. func CreatedAtEQ(v time.Time) predicate.Station { return predicate.Station(sql.FieldEQ(FieldCreatedAt, v)) diff --git a/bj_power_mes/ent/station_create.go b/bj_power_mes/ent/station_create.go index c86c7e8..e8be0d1 100644 --- a/bj_power_mes/ent/station_create.go +++ b/bj_power_mes/ent/station_create.go @@ -82,6 +82,34 @@ func (_c *StationCreate) SetNillableStatus(v *string) *StationCreate { return _c } +// SetIsBuiltin sets the "isBuiltin" field. +func (_c *StationCreate) SetIsBuiltin(v bool) *StationCreate { + _c.mutation.SetIsBuiltin(v) + return _c +} + +// SetNillableIsBuiltin sets the "isBuiltin" field if the given value is not nil. +func (_c *StationCreate) SetNillableIsBuiltin(v *bool) *StationCreate { + if v != nil { + _c.SetIsBuiltin(*v) + } + return _c +} + +// SetHasDock sets the "hasDock" field. +func (_c *StationCreate) SetHasDock(v bool) *StationCreate { + _c.mutation.SetHasDock(v) + return _c +} + +// SetNillableHasDock sets the "hasDock" field if the given value is not nil. +func (_c *StationCreate) SetNillableHasDock(v *bool) *StationCreate { + if v != nil { + _c.SetHasDock(*v) + } + return _c +} + // SetCreatedAt sets the "createdAt" field. func (_c *StationCreate) SetCreatedAt(v time.Time) *StationCreate { _c.mutation.SetCreatedAt(v) @@ -149,6 +177,14 @@ func (_c *StationCreate) defaults() { v := station.DefaultStatus _c.mutation.SetStatus(v) } + if _, ok := _c.mutation.IsBuiltin(); !ok { + v := station.DefaultIsBuiltin + _c.mutation.SetIsBuiltin(v) + } + if _, ok := _c.mutation.HasDock(); !ok { + v := station.DefaultHasDock + _c.mutation.SetHasDock(v) + } if _, ok := _c.mutation.CreatedAt(); !ok { v := station.DefaultCreatedAt() _c.mutation.SetCreatedAt(v) @@ -184,6 +220,12 @@ func (_c *StationCreate) check() error { return &ValidationError{Name: "status", err: fmt.Errorf(`ent: validator failed for field "Station.status": %w`, err)} } } + if _, ok := _c.mutation.IsBuiltin(); !ok { + return &ValidationError{Name: "isBuiltin", err: errors.New(`ent: missing required field "Station.isBuiltin"`)} + } + if _, ok := _c.mutation.HasDock(); !ok { + return &ValidationError{Name: "hasDock", err: errors.New(`ent: missing required field "Station.hasDock"`)} + } if _, ok := _c.mutation.CreatedAt(); !ok { return &ValidationError{Name: "createdAt", err: errors.New(`ent: missing required field "Station.createdAt"`)} } @@ -244,6 +286,14 @@ func (_c *StationCreate) createSpec() (*Station, *sqlgraph.CreateSpec) { _spec.SetField(station.FieldStatus, field.TypeString, value) _node.Status = value } + if value, ok := _c.mutation.IsBuiltin(); ok { + _spec.SetField(station.FieldIsBuiltin, field.TypeBool, value) + _node.IsBuiltin = value + } + if value, ok := _c.mutation.HasDock(); ok { + _spec.SetField(station.FieldHasDock, field.TypeBool, value) + _node.HasDock = value + } if value, ok := _c.mutation.CreatedAt(); ok { _spec.SetField(station.FieldCreatedAt, field.TypeTime, value) _node.CreatedAt = value diff --git a/bj_power_mes/ent/station_update.go b/bj_power_mes/ent/station_update.go index b8981c2..ece539f 100644 --- a/bj_power_mes/ent/station_update.go +++ b/bj_power_mes/ent/station_update.go @@ -118,6 +118,34 @@ func (_u *StationUpdate) SetNillableStatus(v *string) *StationUpdate { return _u } +// SetIsBuiltin sets the "isBuiltin" field. +func (_u *StationUpdate) SetIsBuiltin(v bool) *StationUpdate { + _u.mutation.SetIsBuiltin(v) + return _u +} + +// SetNillableIsBuiltin sets the "isBuiltin" field if the given value is not nil. +func (_u *StationUpdate) SetNillableIsBuiltin(v *bool) *StationUpdate { + if v != nil { + _u.SetIsBuiltin(*v) + } + return _u +} + +// SetHasDock sets the "hasDock" field. +func (_u *StationUpdate) SetHasDock(v bool) *StationUpdate { + _u.mutation.SetHasDock(v) + return _u +} + +// SetNillableHasDock sets the "hasDock" field if the given value is not nil. +func (_u *StationUpdate) SetNillableHasDock(v *bool) *StationUpdate { + if v != nil { + _u.SetHasDock(*v) + } + return _u +} + // Mutation returns the StationMutation object of the builder. func (_u *StationUpdate) Mutation() *StationMutation { return _u.mutation @@ -212,6 +240,12 @@ func (_u *StationUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Status(); ok { _spec.SetField(station.FieldStatus, field.TypeString, value) } + if value, ok := _u.mutation.IsBuiltin(); ok { + _spec.SetField(station.FieldIsBuiltin, field.TypeBool, value) + } + if value, ok := _u.mutation.HasDock(); ok { + _spec.SetField(station.FieldHasDock, field.TypeBool, value) + } _spec.AddModifiers(_u.modifiers...) if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { @@ -324,6 +358,34 @@ func (_u *StationUpdateOne) SetNillableStatus(v *string) *StationUpdateOne { return _u } +// SetIsBuiltin sets the "isBuiltin" field. +func (_u *StationUpdateOne) SetIsBuiltin(v bool) *StationUpdateOne { + _u.mutation.SetIsBuiltin(v) + return _u +} + +// SetNillableIsBuiltin sets the "isBuiltin" field if the given value is not nil. +func (_u *StationUpdateOne) SetNillableIsBuiltin(v *bool) *StationUpdateOne { + if v != nil { + _u.SetIsBuiltin(*v) + } + return _u +} + +// SetHasDock sets the "hasDock" field. +func (_u *StationUpdateOne) SetHasDock(v bool) *StationUpdateOne { + _u.mutation.SetHasDock(v) + return _u +} + +// SetNillableHasDock sets the "hasDock" field if the given value is not nil. +func (_u *StationUpdateOne) SetNillableHasDock(v *bool) *StationUpdateOne { + if v != nil { + _u.SetHasDock(*v) + } + return _u +} + // Mutation returns the StationMutation object of the builder. func (_u *StationUpdateOne) Mutation() *StationMutation { return _u.mutation @@ -448,6 +510,12 @@ func (_u *StationUpdateOne) sqlSave(ctx context.Context) (_node *Station, err er if value, ok := _u.mutation.Status(); ok { _spec.SetField(station.FieldStatus, field.TypeString, value) } + if value, ok := _u.mutation.IsBuiltin(); ok { + _spec.SetField(station.FieldIsBuiltin, field.TypeBool, value) + } + if value, ok := _u.mutation.HasDock(); ok { + _spec.SetField(station.FieldHasDock, field.TypeBool, value) + } _spec.AddModifiers(_u.modifiers...) _node = &Station{config: _u.config} _spec.Assign = _node.assignValues diff --git a/bj_power_mes/frontend/src/help.js b/bj_power_mes/frontend/src/help.js index 9850fd9..35391bc 100644 --- a/bj_power_mes/frontend/src/help.js +++ b/bj_power_mes/frontend/src/help.js @@ -3,16 +3,16 @@ // 说明:这里的"数据来源"特指该字段的数据由谁产生、从哪个系统/页面来,帮助跑通真实流程。 export const helpProductType = { - title: '产品型号(档案维护)', + title: '物料档案(档案维护)', overview: - '产品型号 = 企业要生产哪些产品的分类档案,是整条产线的源头基础数据。\n\n主数据链条:产品型号 → 工单(选产品型号+物料清单) → 物料清单(给该产品编号配料) → 日排产 → 备料单 → 库房出库 → 产线生产 → 完工成品自动回流 WMS 入库。\n\n关系说明(几对几):\n· 产品型号 ↔ 工单:1 对 N。一个产品型号可以建多个工单(同一型号今天做一批、明天做一批,工单号不同、产品编号相同)。\n· 产品型号 ↔ 物料清单:1 对多(选用制)。同一型号可并存多份物料清单(不同厂家供货/含半成品结构),用「物料清单名称」区分,工单建单时选定一份,备料/装机都按工单锁定的那份算。\n· 产品型号 ↔ 备料单:间接。备料单由「日排产 × 工单锁定的物料清单」算出,与产品型号无直接约束。\n· 强约束:工单/物料清单都挂在产品编号下,产品型号编码变了,下游关联会断,建议建好后不要改。\n· 与 WMS 的关系:两套系统解耦。产品/图号主数据以 WMS 为单一来源,MES 实时拉取(WMS 不可达时允许手填、仅提示不强校验);成品完工后由系统自动生成 SN 回流 WMS 入库,无需人工对账。', + '物料档案 = 本页是 WMS「基础数据(物料主数据)」中"成品"品类在本系统的视图,与 WMS 指向同一份主数据(以 WMS 为准,实时拉取、不双录)。本页承载的是"要生产哪些成品",是整条产线的源头基础数据。\n\n品类来源(与 WMS 一致):原材料=进货来的(外购);半成品、成品=自己生产的;其他=辅料/工装/试验设备等。成品还可以作为零部件,放进另一个成品的物料清单(BOM)里被再次加工成新的成品。\n\n主数据链条:物料档案(成品) → 工单(选成品+物料清单) → 物料清单(给该产品编号配料) → 日排产 → 备料单 → 库房出库 → 产线生产 → 完工成品自动回流 WMS 入库。\n\n关系说明(几对几):\n· 成品 ↔ 工单:1 对 N。一个成品可以建多个工单(同一型号今天做一批、明天做一批,工单号不同、产品编号相同)。\n· 成品 ↔ 物料清单:1 对多(选用制)。同一成品可并存多份物料清单(不同厂家供货/含半成品结构),用「物料清单名称」区分,工单建单时选定一份,备料/装机都按工单锁定的那份算。\n· 成品 ↔ 备料单:间接。备料单由「日排产 × 工单锁定的物料清单」算出,与成品无直接约束。\n· 强约束:工单/物料清单都挂在产品编号下,成品编码变了,下游关联会断,建议建好后不要改。\n· 与 WMS 的关系:两套系统解耦。物料/图号主数据以 WMS 为单一来源,MES 实时拉取(WMS 不可达时降级为本地只读缓存);成品完工后由系统自动生成 SN 回流 WMS 入库,无需人工对账。', fields: [ - { name: '名称', source: '手工填写。用户自定义,无上游来源。', purpose: '产品型号的中文名,用于识别与下拉展示', fill: '如:智能电表、断路器、配电柜' }, + { name: '名称', source: '手工填写。用户自定义,无上游来源。', purpose: '成品的中文名,用于识别与下拉展示', fill: '如:智能电表、断路器、配电柜' }, { name: '编码', source: '系统自动生成 + 可手动修改。新建时会自动带出(格式 PROD-YYYYMMDD-序号),你可直接使用或改为自定义编码;编辑时保留已存值。一个产品编号可对应多个工单(同一型号今天做一批、明天做一批,工单号不同,产品编号相同)。', purpose: '产品唯一编码。它是 MES 内部识别该产品的标识,决定该产品的物料清单内容,也是工单、备料、拧紧、追溯的挂靠键。', - fill: '建议用有意义的编码,如 METER-01。要点:①编码主数据以 WMS(库房) 为单一来源,本系统实时从 WMS 拉取物料/产品档案;WMS 不可达时允许手填、仅提示不强校验;②成品完工后系统自动生成 SN 并回流 WMS 入库,无需人工对账;③如产品编号就是"产品型号",则直接沿用该类型编码即可;④建好后不要随意改,否则下游工单/追溯的关联会断。' + fill: '建议用有意义的编码,如 METER-01。要点:①编码主数据以 WMS(库房) 为单一来源,本系统实时从 WMS 拉取物料/成品档案;WMS 不可达时降级为本地只读缓存;②成品完工后系统自动生成 SN 并回流 WMS 入库,无需人工对账;③如产品编号就是成品编码,则直接沿用该编码即可;④建好后不要随意改,否则下游工单/追溯的关联会断。' }, { name: '类别', source: '手工填写。可选,无上游来源。', purpose: '产品所属大类,便于分类统计', fill: '如:计量表 / 开关柜;可空' }, { name: '启用', source: '开关选择。默认开启。', purpose: '启用后才允许在新建工单/备料的下拉里被选到', fill: '默认开启;暂时停产的可关闭(不删除,保留档案)' }, @@ -23,13 +23,13 @@ export const helpProductType = { export const helpWorkOrder = { title: '工单管理(新建 / 编辑 / 删除工单)', overview: - '工单 = 一个"合同/生产批次"的唯一载体,是生产执行和仓库出库挂靠的核心对象。\n\n全流程:先有产品型号 → 建工单(选产品型号、选定物料清单、定数量、勾选工位组合) → 进"物料清单"给该产品配料 → 到"日排产"排哪天产多少 → 仓库按排产备料出库 → 产线装配/拧紧/报工 → 完工(成品 SN 系统自动生成,并自动回流 WMS 入库为"成品"品类)。\n工单号会贯穿 备料单、拧紧查询、手动报工、工件追溯、操作日志 全链路。\n\n工程编号:一个合同对应一个工程编号,一个工程编号下对应各个产品的产品序号——即「合同号 → 工程编号 → 产品序号」三级归属,用于按合同/工程项目归集与追溯。\n\n查询:支持按 工单号 / 产品编号 / 产品名称(模糊) / 合同号 / 工程编号 / 状态 组合筛选;删除:仅「已创建 / 待排产 / 已发布」且无排产、备料、生产数据的工单可删除(操作列「删除」),生产中或已完成的工单不可删除。', + '工单 = 一个"合同/生产批次"的唯一载体,是生产执行和仓库出库挂靠的核心对象。\n\n全流程:先有产品型号(在「物料档案」里建档) → 建工单(选产品型号、选定物料清单、定数量、勾选工位组合) → 进"物料清单"给该产品配料 → 到"日排产"排哪天产多少 → 仓库按排产备料出库 → 产线装配/拧紧/报工 → 完工(成品 SN 系统自动生成,并自动回流 WMS 入库为"成品"品类)。\n工单号会贯穿 备料单、拧紧查询、手动报工、工件追溯、操作日志 全链路。\n\n工程编号:一个合同对应一个工程编号,一个工程编号下对应各个产品的产品序号——即「合同号 → 工程编号 → 产品序号」三级归属,用于按合同/工程项目归集与追溯。\n\n查询:支持按 工单号 / 产品编号 / 产品名称(模糊) / 合同号 / 工程编号 / 状态 组合筛选;删除:仅「已创建 / 待排产 / 已发布」且无排产、备料、生产数据的工单可删除(操作列「删除」),生产中或已完成的工单不可删除。', sections: [ { title: '工单基本信息', fields: [ { name: '工单号', source: '系统自动生成(WO+时间戳),可手工改。新建时自动带出。', purpose: '唯一标识一个工单,备料/出库/报工/追溯都挂在此号下', fill: '一般不用改;如改动须保证唯一' }, - { name: '产品型号', source: '下拉选择。数据来自"产品型号"页面已启用的记录。', purpose: '说明这批生产的是什么产品,并决定物料清单内容', fill: '先到"产品型号"页面建好记录再选择' }, + { name: '产品型号', source: '下拉选择。数据来自「物料档案」页面已启用的记录。', purpose: '说明这批生产的是什么产品,并决定物料清单内容', fill: '先到「物料档案」页面建好记录再选择' }, { name: '合同号', source: '手工填写。', purpose: '标记本工单所属的合同(一个合同对应一个工程编号)', fill: '如 HT-2026-001(可空)' }, { name: '工程编号', source: '手工填写。', purpose: '标记本工单所属的工程编号(一个工程编号下挂多个产品序号)', fill: '如 GC-2026-001(可空)' }, { name: '产品序号', source: '手工填写。', purpose: '本产品在该工程编号下的序号,用于区分同工程下的不同产品', fill: '如 01、02(可空)' }, @@ -81,10 +81,10 @@ export const helpDailyPlan = { export const helpBom = { title: '产品物料清单', overview: - '物料清单 = 生产 1 台该产品需要哪些物料、各用多少。\n按「产品编号 + 物料清单名称」维护:同一产品编号可并存多份物料清单(选用制)——不同厂家供货、含半成品的结构差异,都用不同的物料清单名称区分(缺省叫「默认」,不用 V1/V2 版本号)。\n工单建单时选定其中一份,之后该工单的备料算料、装机绑定校验都锁定这份物料清单,不同厂家的料单互不串。\n\n与 WMS 的关系:本页(MES)只维护「产品由哪几种料、各用几个」;每种物料叫什么、规格、是电气件(SN)还是结构件(批次)、库存有多少,统一在 WMS(库房)的「物料档案」里维护,两套系统互相解耦、通过图号对齐。\n\n编码对应规则:本页的「图号」必须与 WMS 物料档案里的「图号」完全一致;「管理方式」也要对应(电气件=SN 逐件,结构件=批次)。编码对不上,WMS 将无法为对应工单备料/出库。\n\n保护规则:某份物料清单一旦被工单选用过,就不允许再从中移除物料(防止历史工单追溯断链);新建/改名的物料清单名称在本页「物料清单名称」下拉直接输入即可创建。\n\n数据链路:先建「产品型号」→ 到本页选产品编号 + 命名物料清单,逐条添加物料 → 建工单时选定物料清单 → 仓库才能为对应工单领料。', + '物料清单 = 生产 1 台该产品需要哪些物料、各用多少。\n按「产品编号 + 物料清单名称」维护:同一产品编号可并存多份物料清单(选用制)——不同厂家供货、含半成品的结构差异,都用不同的物料清单名称区分(缺省叫「默认」,不用 V1/V2 版本号)。\n工单建单时选定其中一份,之后该工单的备料算料、装机绑定校验都锁定这份物料清单,不同厂家的料单互不串。\n\n与 WMS 的关系:本页(MES)只维护「产品由哪几种料、各用几个」;每种物料叫什么、规格、是电气件(SN)还是结构件(批次)、库存有多少,统一在 WMS(库房)的「物料档案」里维护,两套系统互相解耦、通过图号对齐。\n\n编码对应规则:本页的「图号」必须与 WMS 物料档案里的「图号」完全一致;「管理方式」也要对应(电气件=SN 逐件,结构件=批次)。编码对不上,WMS 将无法为对应工单备料/出库。\n\n保护规则:某份物料清单一旦被工单选用过,就不允许再从中移除物料(防止历史工单追溯断链);新建/改名的物料清单名称在本页「物料清单名称」下拉直接输入即可创建。\n\n数据链路:先建「物料档案」→ 到本页选产品编号 + 命名物料清单,逐条添加物料 → 建工单时选定物料清单 → 仓库才能为对应工单领料。', fields: [ { name: '物料清单名称', source: '下拉选择/输入(可搜索,输入新名即创建)。缺省「默认」。', purpose: '同一产品编号下多份物料清单的区分名,工单建单时按此选定', fill: '如「默认」「A厂料单」「含半成品」;同一型号下名称唯一' }, - { name: '产品编号', source: '下拉选择。来自「产品型号」里建的编码。', purpose: '要为哪个产品配置物料清单', fill: '在查询区或「添加物料」弹窗里选择产品编号' }, + { name: '产品编号', source: '下拉选择。来自「物料档案」里建的编码。', purpose: '要为哪个产品配置物料清单', fill: '在查询区或「添加物料」弹窗里选择产品编号' }, { name: '图号', source: '手工填写。必须与 WMS(库房) 物料档案的图号一致。', purpose: '该物料在库房的全库唯一编码,两系统靠它关联', fill: '如 螺丝=GJ-LUOSI-M16;图号对不上 WMS 将无法备料' }, { name: '物料名称', source: '手工填写。', purpose: '物料中文名,便于识别', fill: '如 十字螺丝、主控芯片' }, { name: '规格', source: '手工填写。', purpose: '物料规格型号', fill: '如 M16×80 / SMT-48脚' }, @@ -184,11 +184,15 @@ export const helpScan = { export const helpStation = { title: '关联工位(工位总览)', overview: - '工位数以数据库为准(仅可修改名称,不增删工位)。本页是工位总览:查看每个工位当前绑定了哪个工艺流程,可修改工位名称。\n\n绑定关系由本页单独维护:在下拉中选择「启用」的工艺流程即绑定到该工位;一个工位只能有一个启用的工艺流程(先停用旧流程再绑新流程);流程停用后自动解绑。\n\n数据链路:关联工位页(选启用流程绑定) → 手动报工/工位终端按该流程的工序步骤采集 → 报工判定 合格/不合格。', + '工位数以数据库为准。工位分「内置」与「自定义」两类:内置工位由种子/初始化数据产生(数据库 is_builtin=true),对应实体产线位置,需与 PLC 通讯并必须按工位号顺序流转,不允许修改工位类型、不允许删除;自定义工位是在本页点击「新增工位」创建的,可修改类型、可删除。\n\n本页是工位总览:查看每个工位当前绑定了哪个工艺流程,可修改工位名称、删除自定义工位。\n\n绑定关系由本页单独维护:在下拉中选择「启用」的工艺流程即绑定到该工位;一个工位只能有一个启用的工艺流程(先停用旧流程再绑新流程);流程停用后自动解绑。\n\n「是否有接驳台」对应实体位置(1~10 号工位各 2 个接驳台;0/11/12/13 号无),由内置数据给定,页面不可手动更改。\n\n数据链路:关联工位页(选启用流程绑定) → 手动报工/工位终端按该流程的工序步骤采集 → 报工判定 合格/不合格。', fields: [ - { name: '工位号', source: '系统固定(工位数以数据库为准),不可增删。', purpose: '产线物理工位编号', fill: '无需填写' }, + { name: '工位号', source: '内置工位由系统预置;自定义工位由本页「新增工位」填写。', purpose: '产线物理工位编号', fill: '新增时自定义,可与现有工位不连续(如 14、15…)' }, { name: '工位名称', source: '可编辑。默认如 装配工位1。', purpose: '工位的显示名称', fill: '如 一号装配工位' }, + { name: '类型', source: '内置工位由系统给定且不可修改;自定义工位可改。', purpose: '线上位(连 PLC)/线下位(虚拟,不连 PLC)', fill: '内置工位只读;自定义工位可在改名弹窗切换' }, + { name: '来源', source: '系统标识(数据库 is_builtin)。', purpose: '区分内置工位与自定义工位', fill: '只读' }, + { name: '接驳台', source: '内置数据给定(对应实体位置),不可手动更改。', purpose: '该工位位置是否有接驳台', fill: '只读' }, { name: '绑定工艺流程', source: '自动来自本页:为工位选择「启用」的工艺流程即绑定。', purpose: '该工位执行的工艺流程(决定工序步骤/考核标准/PDF)', fill: '到本页为该工位选择启用流程;本页只读展示' }, + { name: '删除', source: '本页操作。', purpose: '删除自定义工位', fill: '仅自定义工位可删除;内置工位不显示删除按钮' }, { name: '虚拟工位 0 / 13', source: '系统预置。工位号 0=上线位、13=下线位。', purpose: '产线进线口建档上线、出料口完工入库,由 PAD 操作,不连 PLC/拧紧枪', fill: '只读;上线位/下线位在工位终端(PAD)用「上线建档」「下线完工」面板操作' } ] } @@ -391,7 +395,7 @@ export const helpProcessCard = { overview: '这个菜单做什么用:按**产品型号**全方位展示该型号的完整制造定义,用于工艺交底、审核与客户查阅;与「工件追溯」页里按单个 SN 打印的流程卡互补——后者记的是“这一件实际怎么做的”,本页讲的是“这个型号应该怎么做”。\n\n内容构成(页面按页签展示,打印时全部输出):①工位流程总览——工位组合、每工位绑定的工艺流程、流程状态、步骤数、检验点数、拧紧步骤数、工艺图纸、该型号的历史作业时间;②工艺流程与步骤——逐工位列出每条步骤的采集方式(手工录入/自动采集/无需采集)、是否需检测确认、是否拧紧采集、考核标准(含上下限);③物料清单——该型号的物料清单明细,按物料清单名称分组,含图号/名称/规格/单位/管理方式/装配工位/单台用量/损耗率/需求总量;④检验卡项——把所有「需要检测确认」的步骤汇总成检验项清单,含判定标准。\n\n工位组合从哪来:优先取该产品编号最近一张工单的「工位组合」;若该型号还没有工单,则按全产线工位顺序(升序)展示,页面顶部会标注来源。\n\n打印/导出:点「打印 / 导出 PDF」,系统会在新窗口打开排版好的 A4 卡片版式,用浏览器打印即可(选择“另存为 PDF”即导出电子档)。', fields: [ - { name: '产品型号', source: '下拉选择,来自「产品型号」页。', purpose: '要查看哪个型号的全景流程', fill: '选定后自动带出产品编号' }, + { name: '产品型号', source: '下拉选择,来自「物料档案」页。', purpose: '要查看哪个型号的全景流程', fill: '选定后自动带出产品编号' }, { name: '产品编号', source: '自动带出(也可手工填写,直接按编号生成)。', purpose: '匹配工单、物料清单的关键字', fill: '一般无需手填' }, { name: '工位组合', source: '只读,自动带出。', purpose: '该型号要走哪些工位、按什么顺序', fill: '-' }, { name: '工位流程总览-工艺图纸', source: '来自「工艺流程」页上传的图纸文件名。', purpose: '该工位是否有可查阅的工艺图', fill: '-' }, @@ -401,23 +405,6 @@ export const helpProcessCard = { } -export const helpQtyReport = { - title: '数量不符上报', - overview: - '这个菜单做什么用:记录「工位收料时实收数量 ≠ 备料单应发数量」的差异,形成闭环——上报→落预警→生产/仓库核实处理。\n\n上报入口有两处:①工位终端「巡检/异常」面板——操作工现场发现数量不符时直接上报;②本页(MES 电脑端)——由生产调度/线长代录或复核。\n\n差异怎么算:系统自动按「实收 − 应发」计算差异(正数=多收、负数=少收);上报后同步落一条预警中心消息(type=qty_diff),未读状态可提醒相关人员。\n\n处理流程:仓库/生产核实后在预警中心标记已读,本页记录保持存档,供后续对账与追溯。', - fields: [ - { name: '工位', source: '工位终端上报自动带入;本页手录时手动选择(工位数以数据库为准)。', purpose: '差异发生在哪个工位', fill: '选工位号' }, - { name: '工单号', source: '工位终端上报自动带入;来自【工单管理】。', purpose: '定位是哪张工单的物料', fill: '搜索/输入工单号' }, - { name: '产品序列号', source: '工位终端上报自动带入(当前作业件)。', purpose: '追溯到具体那一件', fill: '扫描/输入 SN' }, - { name: '图号/名称', source: '工位终端上报自动带入(当前收料物料)。', purpose: '是哪一种物料数量不符', fill: '选择/输入物料' }, - { name: '应发数量', source: '手动/自动带入备料单应发数。', purpose: '作为差异计算基准', fill: '填应发数' }, - { name: '实收数量', source: '手动填写(现场实际清点结果)。', purpose: '作为差异计算基准', fill: '填实收数' }, - { name: '差异', source: '系统自动计算(实收−应发)。', purpose: '一眼看出多收/少收', fill: '只读' }, - { name: '原因', source: '手动填写。', purpose: '说明数量不符的原因(如供应商短装/破损/漏发)', fill: '描述原因' }, - { name: '状态', source: '系统维护(待处理/已处理)。', purpose: '跟踪闭环进度', fill: '只读,筛选用' } - ] -} - export const helpQuality = { title: '质量检验(过程检 / 完工检)', overview: @@ -450,9 +437,11 @@ export const helpWip = { export const helpAlertCenter = { title: '预警中心(系统自动触发)', overview: - '预警中心汇集全系统自动产生的异常/提醒消息,人工无需配置规则——全部由内置触发源自动落库并推送。顶部铃铛角标显示未读数,点击进入本页处理。\n\n八类内置触发源:\n· 扭紧不合格(torque_ng):拧紧扭矩/角度超考核标准时自动告警;\n· 检验不合格(inspection_ng):质量检验判定 NG 时告警;\n· 备料未齐套(material_shortage):工单装机绑定物料未齐套时告警;\n· 库存偏低(inventory_low):可用量低于安全库存时告警;\n· 工单超时(task_overdue):工单超过完成日期未完工时告警;\n· 缓存位满(cache_full):产线缓存/接驳台占满时告警;\n· 数量不符(qty_diff):工位收料实收≠应发时告警(见「数量不符上报」);\n· 应急呼叫(emergency_call):工位终端现场发起的紧急呼叫。\n\n应急呼叫闭环:操作工在工位终端点「应急呼叫」(设备故障/质量异常/缺料/安全等紧急情形)→ 系统即时落一条 type=emergency_call 的未读预警并通过 SSE 推到大屏/看板 → 线长、维修、仓库等相关人员在本页看到未读告警并到场处置 → 处置完点「标记已读」闭环。\n\n处理流程:按状态(全部/未读/已读)筛选,核实后点行末「标记已读」;已读记录留存备查。', + '预警中心汇集全系统自动产生的异常/提醒消息,人工无需配置规则——全部由内置触发源自动落库并推送。顶部铃铛角标显示未读数,点击进入本页处理。\n\n八类内置触发源:\n· 扭紧不合格(torque_ng):拧紧扭矩/角度超考核标准时自动告警;\n· 检验不合格(inspection_ng):质量检验判定 NG 时告警;\n· 备料未齐套(material_shortage):工单装机绑定物料未齐套时告警;\n· 库存偏低(inventory_low):可用量低于安全库存时告警;\n· 工单超时(task_overdue):工单超过完成日期未完工时告警;\n· 缓存位满(cache_full):产线缓存/接驳台占满时告警;\n· 数量不符(qty_diff):工位收料实收≠应发时告警(MES 仅预警,明细由 WMS 处理);\n· 应急呼叫(emergency_call):工位终端现场发起的紧急呼叫。\n\n应急呼叫闭环:操作工在工位终端点「应急呼叫」(设备故障/质量异常/缺料/安全等紧急情形)→ 系统即时落一条 type=emergency_call 的未读预警并通过 SSE 推到大屏/看板 → 线长、维修、仓库等相关人员在本页看到未读告警并到场处置 → 处置完点「标记已读」闭环。\n\n处理流程:按 状态(全部/未读/已读)、预警类型、开始时间(默认当天)、结束时间(默认不限)筛选并分页浏览,核实后点行末「标记已读」;已读记录留存备查。', fields: [ - { name: '状态', source: '系统维护(未读/已读)。', purpose: '区分待处理与已处理预警', fill: '顶部单选筛选:全部/未读/已读' }, + { name: '状态', source: '系统维护(未读/已读)。', purpose: '区分待处理与已处理预警', fill: '筛选项单选:全部/未读/已读' }, + { name: '预警类型', source: '内置触发源自动写入。', purpose: '只关注某一类异常', fill: '下拉选择八类之一,留空=全部' }, + { name: '开始时间 / 结束时间', source: '系统记录触发时刻。', purpose: '按时间范围缩小排查范围', fill: '开始时间默认当天;结束时间留空=不限' }, { name: '类型', source: '内置触发源自动写入。', purpose: '区分八类预警(含应急呼叫)', fill: '只读' }, { name: '标题 / 内容', source: '触发时系统自动生成。', purpose: '说明预警的具体对象与原因', fill: '只读' }, { name: '关联ID', source: '系统写入(工单号/SN/工位等业务键)。', purpose: '定位预警来源的具体业务对象', fill: '只读' }, diff --git a/bj_power_mes/frontend/src/layouts/MainLayout.vue b/bj_power_mes/frontend/src/layouts/MainLayout.vue index b6da178..a11ec6d 100644 --- a/bj_power_mes/frontend/src/layouts/MainLayout.vue +++ b/bj_power_mes/frontend/src/layouts/MainLayout.vue @@ -151,8 +151,7 @@ const menuGroups = [ title: '基础配置', icon: Box, children: [ { code: 'produce.processflow', name: '工艺流程', path: '/process-flow', icon: Operation }, { code: 'produce.station', name: '关联工位', path: '/station', icon: Link }, - { code: 'produce.product', name: '产品型号', path: '/product-type', icon: Box }, - { code: 'produce.qtyreport', name: '数量不符上报', path: '/qty-report', icon: Warning } + { code: 'produce.product', name: '物料档案', path: '/product-type', icon: Box } ] }, { diff --git a/bj_power_mes/frontend/src/pages/AlertCenter.vue b/bj_power_mes/frontend/src/pages/AlertCenter.vue index d38a1f2..2369f4b 100644 --- a/bj_power_mes/frontend/src/pages/AlertCenter.vue +++ b/bj_power_mes/frontend/src/pages/AlertCenter.vue @@ -1,18 +1,37 @@ @@ -39,8 +63,28 @@ import PageHelp from '../components/PageHelp.vue' import { helpAlertCenter } from '../help' const alerts = ref([]) +const total = ref(0) +const page = ref(1) +const pageSize = ref(20) +const loading = ref(false) const unread = ref(0) const statusFilter = ref('all') +const typeFilter = ref('') +const startDate = ref(todayStr()) // 开始时间默认当天 +const endDate = ref('') // 结束时间默认空 + +function todayStr() { + const d = new Date() + const p = (n) => String(n).padStart(2, '0') + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}` +} +function fmtTime(v) { + if (!v) return '-' + const d = new Date(v) + if (Number.isNaN(d.getTime())) return String(v) + const p = (n) => String(n).padStart(2, '0') + return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}` +} // 内置触发源类型(仅用于展示文案,不在前端配置) const types = [ @@ -59,10 +103,37 @@ function typeLabel(v) { } async function loadAlerts() { - const status = statusFilter.value === 'all' ? '' : statusFilter.value - alerts.value = (await request.get('/alerts', { params: { status } })) || [] + loading.value = true + try { + const params = { + status: statusFilter.value === 'all' ? '' : statusFilter.value, + type: typeFilter.value, + from: startDate.value, + to: endDate.value, + page: page.value, + pageSize: pageSize.value + } + const data = await request.get('/alerts', { params }) + alerts.value = data?.list || [] + total.value = data?.total || 0 + } finally { + loading.value = false + } + // 未读数不带时间/类型筛选,始终反映全局未读,与右上角铃铛一致 unread.value = (await request.get('/alerts', { params: { status: 'UNREAD' } }))?.length || 0 } +function search() { + page.value = 1 + loadAlerts() +} +function reset() { + statusFilter.value = 'all' + typeFilter.value = '' + startDate.value = todayStr() + endDate.value = '' + page.value = 1 + loadAlerts() +} async function markRead(row) { await request.post('/alert/read', { id: row.id }) loadAlerts() diff --git a/bj_power_mes/frontend/src/pages/Attachment.vue b/bj_power_mes/frontend/src/pages/Attachment.vue index 94e0006..2054d46 100644 --- a/bj_power_mes/frontend/src/pages/Attachment.vue +++ b/bj_power_mes/frontend/src/pages/Attachment.vue @@ -1,26 +1,31 @@ diff --git a/bj_power_mes/frontend/src/pages/EventLog.vue b/bj_power_mes/frontend/src/pages/EventLog.vue index f27d3b2..d9c0cac 100644 --- a/bj_power_mes/frontend/src/pages/EventLog.vue +++ b/bj_power_mes/frontend/src/pages/EventLog.vue @@ -30,7 +30,6 @@ @@ -161,11 +160,3 @@ function onReset() { onMounted(load) - - diff --git a/bj_power_mes/frontend/src/pages/ProductType.vue b/bj_power_mes/frontend/src/pages/ProductType.vue index 59a3cce..2e50868 100644 --- a/bj_power_mes/frontend/src/pages/ProductType.vue +++ b/bj_power_mes/frontend/src/pages/ProductType.vue @@ -16,7 +16,7 @@ 查询 - 新增产品型号 + 新增物料档案 导出 @@ -47,7 +47,7 @@ :total="total" v-model:current-page="page" v-model:page-size="pageSize" :page-sizes="[20, 50, 100]" @current-change="load" @size-change="search" /> - + @@ -75,7 +75,7 @@ import PageHelp from '../components/PageHelp.vue' import { helpProductType } from '../help' import { fmtTime } from '../utils/format' -// 数据源说明:产品型号档案已并入 WMS 物料主数据(item_type=3,品类=成品), +// 数据源说明:物料档案已并入 WMS 物料主数据(item_type=3,品类=成品), // 本页经 MES 后端实时代理 WMS;WMS 不可达时降级为本地只读缓存(wmsOnline=false)。 const list = ref([]) const total = ref(0) @@ -148,7 +148,7 @@ async function exportXlsx() { const blob = await request.get('/product-types/export', { params, responseType: 'blob' }) const a = document.createElement('a') a.href = URL.createObjectURL(blob) - a.download = '产品型号.xlsx' + a.download = '物料档案.xlsx' a.click() URL.revokeObjectURL(a.href) } diff --git a/bj_power_mes/frontend/src/pages/QtyReport.vue b/bj_power_mes/frontend/src/pages/QtyReport.vue deleted file mode 100644 index 06750a0..0000000 --- a/bj_power_mes/frontend/src/pages/QtyReport.vue +++ /dev/null @@ -1,73 +0,0 @@ - - - diff --git a/bj_power_mes/frontend/src/pages/Station.vue b/bj_power_mes/frontend/src/pages/Station.vue index 5ef66dd..b3f669b 100644 --- a/bj_power_mes/frontend/src/pages/Station.vue +++ b/bj_power_mes/frontend/src/pages/Station.vue @@ -1,7 +1,7 @@ + + + + + + - + @@ -45,10 +58,13 @@ - + 线上位(连 PLC) 线下位(虚拟,不连 PLC) +
+ 内置工位需与 PLC 通讯并按顺序流转,工位类型不可修改。 +