class_name LogPanel extends PanelModule # 日志面板: # - 展示农场事件、系统记录等只读列表。 # - 列表数据只在打开时拉取,不常驻轮询,减少 H5 Web 端网络和节点压力。 # - 行背景/文本使用 UiBuilder 统一创建,保持和其它活动面板同一风格。 var _log_item_bg: Texture2D var _tab_index := 0 var _page_index := 1 var _page_max := 1 var _list: VBoxContainer var _page_label: Label var _panel: Control func setup(options: Dictionary) -> void: super.setup(options) _log_item_bg = options.get("log_item_bg_texture") as Texture2D func open() -> void: _close_modal(false) _tab_index = 0 _page_index = 1 _page_max = 1 _panel = _ui.create_common_panel_shell("LogPanel", Vector2(600.0, 790.0), "panel_icon_log", _texture_provider.atlas_path("common"), "panel_title_log", _texture_provider.atlas_path("common")) _ui.add_content_panel_background(_panel, "InnerPage", Vector2(77.0, 239.0), Vector2(487.0, 520.0)) _ui.create_tab_bar(_panel, "LogTabs", ["玩家日志", "系统日志"], Vector2(77.0, 192.0), _tab_index, Callable(self, "_on_tab_pressed")) var scroll := ScrollContainer.new() scroll.name = "LogScroll" scroll.position = Vector2(80.0, 248.0) scroll.size = Vector2(482.0, 500.0) scroll.clip_contents = true _ui.configure_vertical_scroll(scroll) _panel.add_child(scroll) _list = VBoxContainer.new() _list.name = "LogList" _list.custom_minimum_size = Vector2(482.0, 0.0) _list.add_theme_constant_override("separation", 3) scroll.add_child(_list) _ui.add_texture_button(_panel, "LogPrev", _texture_provider.common("btn_page_prev"), Vector2(241.0, 789.0), Vector2(46.0, 41.0), "", Callable(self, "_on_prev_pressed"), 20) _ui.add_texture_button(_panel, "LogNext", _texture_provider.common("btn_page_next"), Vector2(369.0, 789.0), Vector2(46.0, 41.0), "", Callable(self, "_on_next_pressed"), 20) _page_label = _ui.add_panel_label(_panel, "LogPage", "1/1", Vector2(289.0, 796.0), Vector2(72.0, 28.0), 24, Color.html("#753f24"), 0, Color.TRANSPARENT) _ui.add_texture_button(_panel, "LogClear", _texture_provider.common("c_btn2"), Vector2(463.0, 784.0), Vector2(88.0, 44.0), "清空", Callable(self, "_on_clear_pressed"), 22) _open_modal(_panel) await render() func render() -> void: var list := _list if not _clear_children_if_live(list): return _ui.add_panel_label(list, "Loading", "读取日志中...", Vector2.ZERO, Vector2(462.0, 44.0), 22, Color.html("#753f24"), 0, Color.TRANSPARENT) var token := _token() if token.is_empty(): return var offset := (_page_index - 1) * 10 var result: Dictionary if _tab_index == 0: result = await _activity_api.fetch_user_logs(token, offset, 10) else: result = await _activity_api.fetch_system_logs(token, offset, 10) if not _is_current_node(list, _list): return _clear_children(list) if not result.get("ok", false): _ui.add_panel_label(list, "Error", _response_status_text(result, "日志读取失败"), Vector2.ZERO, Vector2(462.0, 48.0), 22, Color.html("#ce5f5f"), 0, Color.TRANSPARENT) return var data = _response_data(result, {}) var rows: Array = [] if data is Dictionary: rows = data.get("list", []) _page_max = clampi(int(ceil(float(int(data.get("total_count", rows.size()))) / 10.0)), 1, 10) else: _page_max = 1 if _is_live_node(_page_label): _page_label.text = "%s/%s" % [_page_index, _page_max] if rows.is_empty(): _ui.add_panel_label(list, "Empty", "暂无日志", Vector2.ZERO, Vector2(462.0, 48.0), 24, Color.html("#753f24"), 0, Color.TRANSPARENT) return for index in range(rows.size()): if rows[index] is Dictionary: _add_row(list, rows[index], index) func _on_tab_pressed(index: int) -> void: _tab_index = index _page_index = 1 var tab_bar := _panel.get_node_or_null("LogTabs") if _panel != null else null _ui.update_tab_bar(tab_bar as Control, index) await render() func _on_prev_pressed() -> void: if _page_index <= 1: return _page_index -= 1 await render() func _on_next_pressed() -> void: if _page_index >= _page_max: return _page_index += 1 await render() func _on_clear_pressed() -> void: if _is_busy(): return var token := _token() if token.is_empty(): _show_toast("登录信息失效") return _set_busy(true) var result: Dictionary = await _activity_api.clean_user_logs(token) _set_busy(false) if not result.get("ok", false): _show_toast(_response_status_text(result, "清空失败")) return _page_index = 1 await render() func _add_row(parent: Control, row: Dictionary, index: int) -> void: var holder := Control.new() holder.name = "LogRow%s" % index holder.custom_minimum_size = Vector2(462.0, 56.0) holder.size = Vector2(462.0, 56.0) parent.add_child(holder) _ui.add_nine_patch(holder, "Bg", _log_item_bg, Vector2.ZERO, holder.size, Vector4(25.0, 25.0, 1.0, 1.0)) _ui.add_panel_label(holder, "Text", _format_row(row), Vector2(16.0, 3.0), Vector2(430.0, 50.0), 20, Color.html("#753f24"), 0, Color.TRANSPARENT, HORIZONTAL_ALIGNMENT_LEFT) func _format_row(row: Dictionary) -> String: if _tab_index == 1: return "%s %s" % [_format_timestamp(int(row.get("created_at", row.get("time", 0)))), str(row.get("message", row.get("content", "")))] var type_text := _player_log_action(int(row.get("type", -1))) var player := _player_data() var actor := "我" if int(row.get("user_id", row.get("uid", 0))) == int(player.get("user_id", player.get("id", 0))) else str(row.get("be_user_nickname", row.get("name", "玩家"))) var item_id := int(row.get("item_id", 0)) var item_text := "" if item_id > 0: item_text = _game_config.get_item_name(item_id) var num := int(row.get("num", 0)) if num > 0: item_text += "x%s" % num return "%s %s %s %s" % [_format_timestamp(int(row.get("time", row.get("created_at", 0)))), actor, type_text, item_text] func _player_log_action(type: int) -> String: match type: 0: return "购买" 1: return "卖出" 2: return "种植" 3: return "收获" 4: return "偷取了" 5: return "对植物进行了施肥" 6: return "进行了锄地" 7: return "进行了土地升级" 8: return "切换了农场风格" 9: return "喂了狗狗" 10: return "使坏放虫" 11: return "使坏种草" 12: return "对土地浇水" 13: return "对植物除虫" 14: return "对植物除草" 15: return "扩建了1块土地" 16: return "进行了房屋升级" 17: return "被偷了" 18: return "狗狗抓住" 19: return "被狗狗抓住" 20: return "给你放虫" 21: return "给你种草" 22, 24: return "帮你浇水" 23: return "帮你放虫" _: return "操作"