|
|
@@ -1,23 +1,79 @@
|
|
|
package com.fujica.abk.component.nav;
|
|
|
|
|
|
import com.fujica.abk.ResourceTable;
|
|
|
-import ohos.agp.components.Component;
|
|
|
-import ohos.agp.components.ComponentContainer;
|
|
|
-import ohos.agp.components.DirectionalLayout;
|
|
|
-import ohos.agp.components.LayoutScatter;
|
|
|
+import com.fujica.abk.component.LoadingComponent;
|
|
|
+import com.fujica.abk.component.OrderItemComponent;
|
|
|
+import com.fujica.abk.model.response.OrderRes;
|
|
|
+import com.fujica.abk.model.response.Page;
|
|
|
+import com.fujica.abk.utils.*;
|
|
|
+import com.google.gson.Gson;
|
|
|
+import com.google.gson.reflect.TypeToken;
|
|
|
+import ohos.agp.components.*;
|
|
|
+import ohos.agp.components.element.ShapeElement;
|
|
|
+import ohos.agp.utils.Color;
|
|
|
+import com.fujica.abk.utils.CustomListDialog;
|
|
|
import ohos.app.Context;
|
|
|
+import ohos.eventhandler.EventHandler;
|
|
|
+import ohos.eventhandler.EventRunner;
|
|
|
+
|
|
|
+import java.lang.reflect.Type;
|
|
|
+import java.text.DecimalFormat;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Calendar;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
/**
|
|
|
* 缴费记录页面组件
|
|
|
*/
|
|
|
public class OrderComponent extends DirectionalLayout {
|
|
|
+ // 页面组件
|
|
|
private Component rootLayout;
|
|
|
+ private DirectionalLayout btnBizTypeFilter; // 业务类型筛选按钮
|
|
|
+ private Text textBizType; // 业务类型文本
|
|
|
+ private DirectionalLayout btnDateFilter; // 日期筛选按钮
|
|
|
+ private Text textDate; // 日期文本
|
|
|
+ private ScrollView orderListScroll; // 订单列表滚动视图
|
|
|
+ private DirectionalLayout orderListContainer; // 订单列表容器
|
|
|
+
|
|
|
+ // 筛选参数
|
|
|
+ private String bizType = "全部"; // 业务类型:全部、停车、月卡
|
|
|
+ private String paySuccessTime = ""; // 支付成功时间(格式:YYYY-MM)
|
|
|
+
|
|
|
+ // 分页相关
|
|
|
+ private int currentPage = 1; // 当前页码
|
|
|
+ private static final int PAGE_SIZE = 10; // 每页数量
|
|
|
+ private boolean isLoading = false; // 是否正在加载
|
|
|
+ private boolean hasMore = true; // 是否还有更多数据
|
|
|
+ private long lastLoadTime = 0; // 上次加载时间,用于防抖
|
|
|
+ private static final long LOAD_THROTTLE_MS = 500; // 防抖时间间隔(毫秒)
|
|
|
+
|
|
|
+ // 数据
|
|
|
+ private List<OrderRes> orderList = new ArrayList<>(); // 订单列表数据
|
|
|
+ private OrderItemComponent selectedOrderItem = null; // 当前选中的订单列表项
|
|
|
+
|
|
|
+ // 外部依赖
|
|
|
+ private Context context;
|
|
|
+ private LoadingComponent loadingComponent; // Loading组件
|
|
|
+ private DateDialog dateDialog; // 日期选择对话框
|
|
|
+ private OnOrderSelectedListener onOrderSelectedListener; // 订单选中监听器
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单选中监听器
|
|
|
+ */
|
|
|
+ public interface OnOrderSelectedListener {
|
|
|
+ void onOrderSelected(OrderRes order);
|
|
|
+ void onOrderDeselected();
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
* 构造函数
|
|
|
*/
|
|
|
public OrderComponent(Context context) {
|
|
|
super(context);
|
|
|
+ this.context = context;
|
|
|
initComponent(context);
|
|
|
}
|
|
|
|
|
|
@@ -30,10 +86,394 @@ public class OrderComponent extends DirectionalLayout {
|
|
|
setHeight(ComponentContainer.LayoutConfig.MATCH_PARENT);
|
|
|
setOrientation(VERTICAL);
|
|
|
|
|
|
+ // 初始化日期(当前年月)
|
|
|
+ Calendar calendar = Calendar.getInstance();
|
|
|
+ int year = calendar.get(Calendar.YEAR);
|
|
|
+ int month = calendar.get(Calendar.MONTH) + 1;
|
|
|
+
|
|
|
+ year = 2024;
|
|
|
+ month = 8;
|
|
|
+
|
|
|
+ paySuccessTime = String.format("%d-%02d", year, month);
|
|
|
+
|
|
|
// 加载布局文件
|
|
|
rootLayout = LayoutScatter.getInstance(context)
|
|
|
.parse(ResourceTable.Layout_layout_order, null, false);
|
|
|
addComponent(rootLayout);
|
|
|
+
|
|
|
+ // 初始化组件引用
|
|
|
+ initViews();
|
|
|
+
|
|
|
+ // 设置事件监听
|
|
|
+ setupListeners();
|
|
|
+
|
|
|
+ // 加载数据
|
|
|
+ reset();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化视图组件
|
|
|
+ */
|
|
|
+ private void initViews() {
|
|
|
+ btnBizTypeFilter = (DirectionalLayout) rootLayout.findComponentById(ResourceTable.Id_btn_biz_type_filter);
|
|
|
+ if (btnBizTypeFilter != null && btnBizTypeFilter.getChildCount() > 0) {
|
|
|
+ Component firstChild = btnBizTypeFilter.getComponentAt(0);
|
|
|
+ if (firstChild instanceof Text) {
|
|
|
+ textBizType = (Text) firstChild;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ btnDateFilter = (DirectionalLayout) rootLayout.findComponentById(ResourceTable.Id_btn_date_filter);
|
|
|
+ if (btnDateFilter != null && btnDateFilter.getChildCount() > 0) {
|
|
|
+ Component firstChild = btnDateFilter.getComponentAt(0);
|
|
|
+ if (firstChild instanceof Text) {
|
|
|
+ textDate = (Text) firstChild;
|
|
|
+ if (textDate != null) {
|
|
|
+ textDate.setText(paySuccessTime);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ orderListScroll = (ScrollView) rootLayout.findComponentById(ResourceTable.Id_order_list_scroll);
|
|
|
+ orderListContainer = (DirectionalLayout) rootLayout.findComponentById(ResourceTable.Id_order_list_container);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置事件监听
|
|
|
+ */
|
|
|
+ private void setupListeners() {
|
|
|
+ // 设置业务类型筛选点击事件
|
|
|
+ if (btnBizTypeFilter != null) {
|
|
|
+ btnBizTypeFilter.setClickedListener(component -> showBizTypeFilterDialog());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置日期筛选点击事件
|
|
|
+ if (btnDateFilter != null) {
|
|
|
+ btnDateFilter.setClickedListener(component -> showDateFilterDialog());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置滚动监听,实现分页加载
|
|
|
+ if (orderListScroll != null) {
|
|
|
+ orderListScroll.setReboundEffect(false);
|
|
|
+ // 监听滚动到底部
|
|
|
+ orderListScroll.addScrolledListener(new Component.ScrolledListener() {
|
|
|
+ @Override
|
|
|
+ public void onContentScrolled(Component component, int x, int y, int oldX, int oldY) {
|
|
|
+ // 防抖:避免频繁触发
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+ if (currentTime - lastLoadTime < LOAD_THROTTLE_MS) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果正在加载或没有更多数据,直接返回
|
|
|
+ if (isLoading || !hasMore) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算是否滚动到底部
|
|
|
+ int scrollY = orderListScroll.getScrollValue(1);
|
|
|
+ int viewHeight = orderListScroll.getHeight();
|
|
|
+
|
|
|
+ // 获取内容高度
|
|
|
+ int contentHeight = 0;
|
|
|
+ if (orderListContainer != null) {
|
|
|
+ contentHeight = orderListContainer.getHeight();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果容器高度为0,尝试使用EstimatedHeight
|
|
|
+ if (contentHeight == 0) {
|
|
|
+ contentHeight = orderListScroll.getEstimatedHeight();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果内容高度或视图高度为0,无法计算,直接返回
|
|
|
+ if (contentHeight == 0 || viewHeight == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算剩余滚动距离
|
|
|
+ int remainingDistance = contentHeight - scrollY - viewHeight;
|
|
|
+
|
|
|
+ // 计算阈值(50像素),当剩余距离小于等于阈值时触发加载
|
|
|
+ int threshold = 50;
|
|
|
+
|
|
|
+ // 当滚动到底部(剩余距离小于等于阈值)时,触发加载
|
|
|
+ if (remainingDistance <= threshold && remainingDistance >= 0) {
|
|
|
+ lastLoadTime = currentTime;
|
|
|
+ // 加载下一页
|
|
|
+ loadOrderData(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 显示业务类型筛选下拉对话框
|
|
|
+ */
|
|
|
+ private void showBizTypeFilterDialog() {
|
|
|
+ String[] options = {"全部", "停车", "月卡"};
|
|
|
+
|
|
|
+ // 找到当前选中的索引
|
|
|
+ int currentIndex = -1;
|
|
|
+ for (int i = 0; i < options.length; i++) {
|
|
|
+ if (options[i].equals(bizType)) {
|
|
|
+ currentIndex = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ CustomListDialog listDialog = DialogUtil.createListDialog(context, options, (iDialog, index) -> {
|
|
|
+ bizType = options[index];
|
|
|
+ if (textBizType != null) {
|
|
|
+ textBizType.setText(bizType);
|
|
|
+ }
|
|
|
+ // 重新加载数据
|
|
|
+ reset();
|
|
|
+ iDialog.destroy();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 设置当前选中项
|
|
|
+ if (currentIndex >= 0) {
|
|
|
+ listDialog.setSelectedIndex(currentIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ listDialog.show();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 显示日期筛选对话框
|
|
|
+ */
|
|
|
+ private void showDateFilterDialog() {
|
|
|
+ if (dateDialog != null) {
|
|
|
+ dateDialog.destroy();
|
|
|
+ }
|
|
|
+ dateDialog = new DateDialog(context, paySuccessTime, date -> {
|
|
|
+ paySuccessTime = date;
|
|
|
+ if (textDate != null) {
|
|
|
+ textDate.setText(paySuccessTime);
|
|
|
+ }
|
|
|
+ reset();
|
|
|
+ });
|
|
|
+ dateDialog.show();
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ /**
|
|
|
+ * 重置数据并加载
|
|
|
+ */
|
|
|
+ public void reset() {
|
|
|
+ orderList.clear();
|
|
|
+ if (orderListContainer != null) {
|
|
|
+ orderListContainer.removeAllComponents();
|
|
|
+ }
|
|
|
+ // 清除选中状态
|
|
|
+ selectedOrderItem = null;
|
|
|
+ currentPage = 1;
|
|
|
+ hasMore = true;
|
|
|
+ isLoading = false;
|
|
|
+ lastLoadTime = 0;
|
|
|
+ loadOrderData(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 加载订单数据
|
|
|
+ */
|
|
|
+ private void loadOrderData(boolean isRefresh) {
|
|
|
+ if (isLoading) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isRefresh) {
|
|
|
+ currentPage = 1;
|
|
|
+ hasMore = true;
|
|
|
+ lastLoadTime = 0;
|
|
|
+ orderList.clear();
|
|
|
+ if (orderListContainer != null) {
|
|
|
+ orderListContainer.removeAllComponents();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!hasMore) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 锁定当前页码,避免并发问题
|
|
|
+ final int requestPage = currentPage;
|
|
|
+ isLoading = true;
|
|
|
+
|
|
|
+ // 构建请求参数
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("current", requestPage);
|
|
|
+ params.put("size", PAGE_SIZE);
|
|
|
+ params.put("paySuccessTime", paySuccessTime);
|
|
|
+
|
|
|
+ // 业务类型:全部=0, 停车=1, 月卡=2
|
|
|
+ int bizTypeValue = 0;
|
|
|
+ if ("停车".equals(bizType)) {
|
|
|
+ bizTypeValue = 1;
|
|
|
+ } else if ("月卡".equals(bizType)) {
|
|
|
+ bizTypeValue = 2;
|
|
|
+ }
|
|
|
+ if (bizTypeValue != 0) {
|
|
|
+ params.put("bizType", bizTypeValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建请求URL
|
|
|
+ ApiOption option = new ApiOption();
|
|
|
+ option.setUrl("/order/page");
|
|
|
+ option.setMethod(ApiOption.GET);
|
|
|
+ option.setData(params);
|
|
|
+
|
|
|
+
|
|
|
+ // 发送请求
|
|
|
+ CompletableFuture<R<Page<OrderRes>>> future = api.http(context, option, new TypeToken<R<Page<OrderRes>>>(){});
|
|
|
+ if (loadingComponent != null) {
|
|
|
+ loadingComponent.show();
|
|
|
+ }
|
|
|
+ future.thenAccept(res -> {
|
|
|
+ try {
|
|
|
+ if (res == null || !res.isSuccess()) {
|
|
|
+ String errorMsg = res != null ? res.getMsg() : "请求失败";
|
|
|
+ Log.error("加载订单数据失败: " + errorMsg);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析响应数据
|
|
|
+ List<OrderRes> records = res.getData().getRecords();
|
|
|
+ if (records.isEmpty()) {
|
|
|
+ hasMore = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新分页信息
|
|
|
+ boolean hasMoreData = true;
|
|
|
+ int nextPage = requestPage + 1;
|
|
|
+ if (res.getData().getCurrent() != null && res.getData().getPages() != null) {
|
|
|
+ int serverCurrent = res.getData().getCurrent();
|
|
|
+ int totalPages = res.getData().getPages();
|
|
|
+ if (serverCurrent >= totalPages) {
|
|
|
+ hasMoreData = false;
|
|
|
+ } else {
|
|
|
+ nextPage = serverCurrent + 1;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (records.size() < PAGE_SIZE) {
|
|
|
+ hasMoreData = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存最终的分页信息
|
|
|
+ final boolean finalHasMore = hasMoreData;
|
|
|
+ final int finalNextPage = nextPage;
|
|
|
+ final List<OrderRes> finalRecords = records;
|
|
|
+
|
|
|
+ // 只在UI线程更新界面
|
|
|
+ new EventHandler(EventRunner.getMainEventRunner()).postTask(() -> {
|
|
|
+ // 检查currentPage是否已经被其他请求更新
|
|
|
+ if (currentPage > requestPage) {
|
|
|
+ Log.info("忽略过期请求,当前页码: " + currentPage + ", 请求页码: " + requestPage);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到列表
|
|
|
+ orderList.addAll(finalRecords);
|
|
|
+
|
|
|
+ // 批量渲染列表项
|
|
|
+ if (orderListContainer != null && !finalRecords.isEmpty()) {
|
|
|
+ for (OrderRes order : finalRecords) {
|
|
|
+ Component item = createOrderItem(order);
|
|
|
+ if (item != null) {
|
|
|
+ orderListContainer.addComponent(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新分页信息
|
|
|
+ hasMore = finalHasMore;
|
|
|
+ if (finalHasMore) {
|
|
|
+ currentPage = finalNextPage;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.error("解析订单数据失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ } finally {
|
|
|
+ isLoading = false;
|
|
|
+ if (loadingComponent != null) {
|
|
|
+ loadingComponent.hide();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建订单列表项组件
|
|
|
+ */
|
|
|
+ private Component createOrderItem(OrderRes order) {
|
|
|
+ if (orderListContainer == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 使用 OrderItemComponent 组件
|
|
|
+ OrderItemComponent item = new OrderItemComponent(context, order);
|
|
|
+
|
|
|
+ // 设置选中监听器,实现全局选中状态管理
|
|
|
+ item.setOnItemSelectedListener(new OrderItemComponent.OnItemSelectedListener() {
|
|
|
+ @Override
|
|
|
+ public void onItemSelected(OrderItemComponent component, OrderRes orderData) {
|
|
|
+ // 如果之前有选中的项,取消其选中状态
|
|
|
+ if (selectedOrderItem != null && selectedOrderItem != component) {
|
|
|
+ selectedOrderItem.setSelected(false);
|
|
|
+ }
|
|
|
+ // 更新当前选中的项
|
|
|
+ selectedOrderItem = component;
|
|
|
+ // 通知外部监听器
|
|
|
+ if (onOrderSelectedListener != null) {
|
|
|
+ onOrderSelectedListener.onOrderSelected(orderData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onItemDeselected(OrderItemComponent component) {
|
|
|
+ // 如果取消的是当前选中的项,清除选中状态
|
|
|
+ if (selectedOrderItem == component) {
|
|
|
+ selectedOrderItem = null;
|
|
|
+ }
|
|
|
+ // 通知外部监听器
|
|
|
+ if (onOrderSelectedListener != null) {
|
|
|
+ onOrderSelectedListener.onOrderDeselected();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return item;
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.error("创建订单列表项失败: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置Loading组件
|
|
|
+ */
|
|
|
+ public void setLoadingComponent(LoadingComponent loadingComponent) {
|
|
|
+ this.loadingComponent = loadingComponent;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新数据
|
|
|
+ */
|
|
|
+ public void refresh() {
|
|
|
+ reset();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置订单选中监听器
|
|
|
+ */
|
|
|
+ public void setOnOrderSelectedListener(OnOrderSelectedListener listener) {
|
|
|
+ this.onOrderSelectedListener = listener;
|
|
|
+ }
|
|
|
+}
|