diff --git a/sf_warehouse/models/model.py b/sf_warehouse/models/model.py index 33373600..8b9a86e4 100644 --- a/sf_warehouse/models/model.py +++ b/sf_warehouse/models/model.py @@ -459,7 +459,41 @@ class ShelfLocation(models.Model): product_sn_ids = fields.One2many('sf.shelf.location.lot', 'shelf_location_id', string='产品批次号') # 产品数量 product_num = fields.Integer('总数量', compute='_compute_number', store=True) - + tool_rfid = fields.Char('Rfid', compute='_compute_tool', store=True) + tool_name_id = fields.Many2one('sf.functional.cutting.tool.entity', string='功能刀具名称', compute='_compute_tool', store=True) + display_rfid = fields.Char('RFID', compute='_compute_display_rfid', store=True) + @api.depends('product_sn_id') + def _compute_display_rfid(self): + """计算显示 RFID""" + for record in self: + try: + record.display_rfid = record.product_sn_id.rfid if record.product_sn_id else '' + except Exception as e: + record.display_rfid = '' + _logger.error(f"计算 display_rfid 时出错: {e}") + + @api.depends('product_id') + def _compute_tool(self): + """计算工具 RFID""" + for record in self: + try: + if record.product_id: + if record.product_id.categ_id.name == '功能刀具': + # 搜索关联的功能刀具实体 + tool_id = self.env['sf.functional.cutting.tool.entity'].search( + [('barcode_id', '=', record.product_sn_id.id)], limit=1 + ) + if tool_id: + record.tool_rfid = tool_id.rfid + record.tool_name_id = tool_id.id + continue + # 默认值 + record.tool_rfid = '' + record.tool_name_id = False + except Exception as e: + record.tool_rfid = '' + record.tool_name_id = False + _logger.error(f"计算 tool_rfid 时出错: {e}") @api.depends('product_num') def _compute_product_num(self): for record in self: @@ -563,6 +597,7 @@ class ShelfLocation(models.Model): else: _layer_capacity = _layer_capacity _layer = _layer+1 + _layer_capacity = f"{_layer_capacity:02d}" record.kanban_show_layer_info=f"{_layer}-{_layer_capacity}" record.kanban_show_center_control_code=f"{_cc_code}" diff --git a/sf_warehouse/static/src/css/kanban_location_custom.scss b/sf_warehouse/static/src/css/kanban_location_custom.scss index 3bb002e5..67ff9654 100644 --- a/sf_warehouse/static/src/css/kanban_location_custom.scss +++ b/sf_warehouse/static/src/css/kanban_location_custom.scss @@ -1,128 +1,198 @@ -// 定义一个 mixin 来处理重复的样式 -@mixin kanban-common-styles($record-count-each-row, - $record-gap: 16px, - $color-guide-width: 70px) { +// 定义看板公共样式的Mixin +@mixin kanban-common-styles($record-count-each-row, $record-gap: 16px) { $record-gap-total-width: $record-gap * ($record-count-each-row - 1); - + display: flex !important; flex-wrap: wrap !important; overflow-x: hidden !important; overflow-y: auto !important; - padding: 0px !important; + padding: 0 !important; gap: $record-gap !important; width: 100% !important; height: 100% !important; - - // 设置卡片样式 + + // === 卡片基础样式(完全保留)=== .o_kanban_record { - flex: 0 0 calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; - height: calc((100% - #{$record-gap * 6}) / 6) !important; // 平均分配高度 - margin: 0 !important; - padding: 0px !important; - background-color: white !important; - border: 1px solid #dee2e6 !important; - border-radius: 4px !important; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; - min-width: calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; - max-width: calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; - + flex: 0 0 calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; + height: calc((100% - #{$record-gap * 6}) / 6) !important; + margin: 0 !important; + padding: 0 !important; + background-color: white !important; + border: 1px solid #dee2e6 !important; + border-radius: 4px !important; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important; + min-width: calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; + max-width: calc((100% - #{$record-gap-total-width}) / #{$record-count-each-row}) !important; + position: relative; + transition: all 0.25s ease !important; + overflow: visible !important; // 允许悬停条溢出卡片边界 + + // === 状态标签(保留原设计)=== + .status-label { + position: absolute; + top: 8px; + right: 8px; + padding: 3px 8px; + background: rgba(255, 255, 255, 0.9); + border: 1px solid #e0e0e0; + border-radius: 3px; + font-size: 11px; + color: #424242; + z-index: 2; + } + + // === 优化:悬停信息条(核心改动)=== + .status-hover-bar { + position: absolute; + bottom: calc(100% + 8px); // 默认显示在卡片上方 + left: 0; + z-index: 1000; + min-width: max-content; // 宽度自适应内容 + max-width: 300px; // 防止过宽 + padding: 10px 12px; + background: rgba(255, 255, 255, 0.95); + border: 1px solid #e0e0e0; + border-radius: 4px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + font-size: 12px; + color: #424242; + white-space: nowrap; // 强制单行显示 + opacity: 0; + pointer-events: none; // 避免阻挡卡片交互 + transition: opacity 0.2s ease, transform 0.2s ease; + transform: translateY(10px); + + // 三角形指示器 + &::after { + content: ''; + position: absolute; + top: 100%; + left: 15px; + border: 6px solid transparent; + border-top-color: rgba(0, 0, 0, 0.85); + } + + div { + margin-bottom: 4px; + line-height: 1.4; + } + } + + // === 悬停触发逻辑 === + &:hover { + transform: translateY(-4px) !important; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important; + z-index: 10; + + .status-hover-bar { + background: rgba(50, 50, 50, 0.9); + color: #fff !important; + font-size: 12px; + opacity: 0.9; + transform: translateY(0); + pointer-events: auto; // 悬停时允许交互 + } + } + + // === 边界保护(智能定位)=== + // 左侧卡片:左对齐 + &:nth-child(#{$record-count-each-row}n+1) .status-hover-bar { + left: 0; + right: auto; + &::after { left: 15px; } + } + + // 右侧卡片:右对齐 + &:nth-child(#{$record-count-each-row}n) .status-hover-bar { + left: auto; + right: 0; + &::after { + left: auto; + right: 15px; + } + } + &:nth-child(#{$record-count-each-row}n + #{$record-count-each-row - 1}) .status-hover-bar { + left: auto; + right: 0; + &::after { + left: auto; + right: 15px; + } + } + // 顶部卡片:悬停条显示在下方 + &:nth-child(-n+#{$record-count-each-row}) .status-hover-bar { + bottom: auto; + top: calc(100% + 8px); + &::after { + top: auto; + bottom: 100%; + border-top-color: transparent; + border-bottom-color: rgba(255, 255, 255, 0.95); + } + } + + // === 禁用状态样式(保留原效果)=== + &.kanban_color_3 { + opacity: 0.6; &:hover { - transform: translateY(-1px) !important; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) !important; - } - - .o_kanban_record_bottom { - margin: 0; - } - - .oe_kanban_card.kanban_color_3, - .oe_kanban_card.kanban_color_1, - .oe_kanban_card.kanban_color_2 { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - - .sf_kanban_custom_location_info_style { - display: flex !important; - justify-content: center !important; - align-items: center !important; - width: 100%; - font-size: 14px; - color: #000000; - } - - .sf_kanban_no { - display: flex !important; - justify-content: center !important; - align-items: center !important; - font-size: 18px; - color: #000000; - } + opacity: 0.85; + .status-hover-bar { + background:rgba(0, 0, 0, 0.85); + color: white !important; + border: 1px solid rgba(255, 255, 255, 0.15) !important; + } } + } } -} - -// 使用 mixin 为不同的列数生成样式 -.o_kanban_view { - .sf_kanban_location_style { - // 设置卡片样式 - .o_kanban_record { - - &:hover { - transform: translateY(-1px) !important; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) !important; - } - - .o_kanban_record_bottom { - margin: 0; - } - - .oe_kanban_card.kanban_color_3, - .oe_kanban_card.kanban_color_1, - .oe_kanban_card.kanban_color_2 { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - - .sf_kanban_custom_location_info_style { - display: flex !important; - justify-content: center !important; - align-items: center !important; - width: 100%; - font-size: 14px; - color: #000000; - } - - .sf_kanban_no { - display: flex !important; - justify-content: center !important; - align-items: center !important; - font-size: 18px; - color: #000000; - } - } + } + + // === 看板视图样式(完全保留)=== + .o_kanban_view { + // 卡片内部结构(不修改) + .o_kanban_record { + .o_kanban_record_bottom { + margin: 0; + } + .oe_kanban_card.kanban_color_3, + .oe_kanban_card.kanban_color_1, + .oe_kanban_card.kanban_color_2 { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + .sf_kanban_custom_location_info_style { + display: flex !important; + justify-content: center !important; + align-items: center !important; + width: 100%; + font-size: 15px; + color: #000000; + padding:0px; } + + .sf_kanban_no { + display: flex !important; + justify-content: center !important; + align-items: center !important; + font-size: 18px; + color: #000000; + } + } } - + + // 不同列数的看板样式 .sf_kanban_location_style12 { - @include kanban-common-styles(12); + @include kanban-common-styles(12); } - .sf_kanban_location_style19 { - @include kanban-common-styles(19); + @include kanban-common-styles(19); } - .sf_kanban_location_style4 { - @include kanban-common-styles(4); + @include kanban-common-styles(4); } - .sf_kanban_location_style3 { - @include kanban-common-styles(3); + @include kanban-common-styles(3); } -} \ No newline at end of file + } \ No newline at end of file diff --git a/sf_warehouse/views/shelf_location.xml b/sf_warehouse/views/shelf_location.xml index c39f61c6..8b7bf9b6 100644 --- a/sf_warehouse/views/shelf_location.xml +++ b/sf_warehouse/views/shelf_location.xml @@ -193,53 +193,78 @@ - - shelf.location.kanban - sf.shelf.location - - - - - -
+ + shelf.location.kanban + sf.shelf.location + + + + + +
+ + +
+ + +
- -
- + +
+
+
- - - -
-
- -
-
-
- - | - -
-
- - -
- -
-
- -
-
+
+
+ +
- - -
- - + + +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
产品:
+
+ +
标签ID:
+
+ + +
功能刀具名称:
+
+
状态:
+
+
+
+ + + + + shelf.location.search