Code Fragment-异常情况,返回长度为0的容器好过返回null

本文讨论了在编程中遇到异常情况时,选择返回长度为0的容器而非null的优点,包括代码简洁性、避免空指针异常、减少错误场景复现等问题,并提供了相应的代码示例。

异常情况,返回长度为0的容器好过返回null。

  • 返回长度为0的容器,后续代码使用容器前,无需判断是否为空。
    • 代码更优美
  • 避免很多平时不出现,但是可能会出现的NullPointException.
    • 不用去时刻记得检查容器是否为空。
    • 避免了很多ForceClose。一些错误的场景很难复现。
异常情况,直接返回null

  • public static List<String> getItems1() {
    
    	if (new Random().nextInt(10) > 5) {// 异常情况,返回null
    		return null;
    	}
    	List<String> list = new ArrayList<String>();
    	for (int i = 0; i < 5; i++) {
    		list.add("item1 + " + i);
    	}
    	return list;
    }
  • List<String> list = getItems1();
    if (list != null) {// 讨厌的判断,如果后续有用到,需要这种判断。
    	for (String s : list) {
    		System.out.println(s);
    	}
    }

修改后

  • public static List<String> getItems2() {
    
    	if (new Random().nextInt(10) > 5) {// 异常情况,返回长度为0的容器
    		return Collections.emptyList();
    	}
    	List<String> list = new ArrayList<String>();
    	for (int i = 0; i < 5; i++) {
    		list.add("item2 - " + i);
    	}
    	return list;
    }
  • for (String s : getItems2()) {// 更通用,无需特别使用一些判断
    	System.out.println(s);
    }




#include "Shader.h" // 从文件读取着色器代码 std::string Shader::readShaderFile(const char* path) { std::ifstream file; std::stringstream code; try { file.open(path); if (!file.is_open()) { std::cerr << "ERROR::SHADER::FILE_NOT_FOUND: " << path << std::endl; return ""; } code << file.rdbuf(); file.close(); } catch (std::exception& e) { std::cerr << "ERROR::SHADER::FILE_READ_ERROR: " << e.what() << std::endl; return ""; } return code.str(); } // 核心编译逻辑 void Shader::createShaderProgram(const char* vertexCode, const char* fragmentCode) { unsigned int vertex, fragment; vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vertexCode, nullptr); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fragmentCode, nullptr); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); id = glCreateProgram(); glAttachShader(id, vertex); glAttachShader(id, fragment); glLinkProgram(id); checkCompileErrors(id, "PROGRAM"); glDeleteShader(vertex); glDeleteShader(fragment); } // 构造函数1:使用字符串(原始方式,完全兼容旧代码) Shader::Shader(const char* vertexSource, const char* fragmentSource) : id(0) { createShaderProgram(vertexSource, fragmentSource); } // 构造函数2:从文件加载(新增功能) Shader::Shader(const char* vertexPath, const char* fragmentPath, bool fromFile) : id(0) { if (!fromFile) { // 如果不是文件,当作字符串处理(兜底) createShaderProgram(vertexPath, fragmentPath); return; } std::string vertexCode = readShaderFile(vertexPath); std::string fragmentCode = readShaderFile(fragmentPath); if (vertexCode.empty() || fragmentCode.empty()) { std::cerr << "ERROR::SHADER::FAILED_TO_LOAD_SHADERS_FROM_FILE" << std::endl; id = 0; return; } createShaderProgram(vertexCode.c_str(), fragmentCode.c_str()); } void Shader::use() { glUseProgram(id); } void Shader::checkCompileErrors(GLuint shader, std::string type) { GLint success; GLchar infoLog[1024]; if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << std::endl; } } } 详细解释每一行代码,每一行都要详细解释
最新发布
10-23
package com.example.kucun2.ui.jinhuo; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.ProgressDialog; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.SearchView; import android.widget.Spinner; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.example.kucun2.R; import com.example.kucun2.entity.*; import com.example.kucun2.entity.data.Data; import com.example.kucun2.entity.data.ReflectionJsonUtils; import com.example.kucun2.function.Adapter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; public class AddInventoryFragment extends Fragment { // UI组件声明 private Spinner spinnerDingdan, spinnerChanpin, spinnerZujian, spinnerBancai; private EditText etShuliang; private RadioGroup rgType; private Button btnNewDingdan, btnAddChanpin, btnAddZujian; private ProgressDialog progressDialog; // 添加进度条 // 当前选中项 private Dingdan selectedDingdan; private Chanpin selectedChanpin; private Zujian selectedZujian; private Bancai selectedBancai; private View rootView; // 日期格式化工具 private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); private static final String TAG = "AddInventoryFragment"; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_add_inventory, container, false); initViews(rootView); setupEventListeners(); // 初始化下拉框数据(使用已加载的数据) setupSpinnersWithExistingData(); return rootView; } /** 初始化UI组件 */ private void initViews(View view) { spinnerDingdan = view.findViewById(R.id.spinner_dingdan); spinnerChanpin = view.findViewById(R.id.spinner_chanpin); spinnerZujian = view.findViewById(R.id.spinner_zujian); spinnerBancai = view.findViewById(R.id.spinner_bancai); etShuliang = view.findViewById(R.id.et_shuliang); rgType = view.findViewById(R.id.rg_type); btnNewDingdan = view.findViewById(R.id.btn_new_dingdan); btnAddChanpin = view.findViewById(R.id.btn_add_chanpin); btnAddZujian = view.findViewById(R.id.btn_add_zujian); } /** * 使用已加载的数据初始化下拉框 */ private void setupSpinnersWithExistingData() { // 1. 设置订单下拉框 setupDingdanSpinner(); // 2. 如果存在选中的订单,设置产品下拉框 if (selectedDingdan != null) { setupChanpinSpinner(selectedDingdan); } // 3. 如果存在选中的产品,设置组件下拉框 if (selectedChanpin != null) { setupZujianSpinner(selectedChanpin); } // 4. 如果存在选中的产品和组件,设置板材下拉框 if (selectedChanpin != null && selectedZujian != null) { setupBancaiSpinner(selectedChanpin, selectedZujian); } } // 修改 setupDingdanSpinner 方法,使用现有数据 private void setupDingdanSpinner() { // 过滤掉预置对象(ID=-1) List<Dingdan> validDingdans = Data.dingdans.stream() .filter(d -> d.getId() != null) .collect(Collectors.toList()); Adapter.setupDingdanSpinner(spinnerDingdan, validDingdans, getContext()); // 设置默认选中项(如果有) if (!validDingdans.isEmpty()) { spinnerDingdan.setSelection(0); selectedDingdan = validDingdans.get(0); } } /** 设置事件监听器 */ private void setupEventListeners() { // 新建订单按钮 btnNewDingdan.setOnClickListener(v -> showNewDingdanDialog()); // 添加产品按钮 btnAddChanpin.setOnClickListener(v -> showAddChanpinDialog()); // 添加组件按钮 btnAddZujian.setOnClickListener(v -> { if (selectedChanpin == null) { Toast.makeText(getContext(), "请先选择产品", Toast.LENGTH_SHORT).show(); return; } showCreateZujianDialog(); }); // 提交按钮 rootView.findViewById(R.id.btn_submit).setOnClickListener(v -> addInventoryRecord()); // 订单下拉框监听 spinnerDingdan.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedDingdan = (Dingdan) parent.getItemAtPosition(position); setupChanpinSpinner(selectedDingdan); } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置产品下拉框 */ private void setupChanpinSpinner(Dingdan dingdan) { List<Chanpin> chanpins = new ArrayList<>(); if (dingdan != null && dingdan.getDingdanChanpin() != null) { for (Dingdan_Chanpin dc : dingdan.getDingdanChanpin()) { if (dc != null && dc.getChanpin() != null) { chanpins.add(dc.getChanpin()); } } } Adapter.setupChanpinSpinner(spinnerChanpin, chanpins, getContext()); spinnerChanpin.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedChanpin = (Chanpin) parent.getItemAtPosition(position); if (selectedChanpin != null) { setupZujianSpinner(selectedChanpin); } } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置组件下拉框 */ private void setupZujianSpinner(Chanpin chanpin) { List<Zujian> zujians = new ArrayList<>(); for (Chanpin_Zujian cz : chanpin.getChanpinZujian()) { zujians.add(cz.getZujian()); } Adapter.setupZujianSpinner(spinnerZujian, zujians, getContext()); spinnerZujian.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedZujian = (Zujian) parent.getItemAtPosition(position); setupBancaiSpinner(selectedChanpin, selectedZujian); } @Override public void onNothingSelected(AdapterView<?> parent) {} }); } /** 设置板材下拉框 */ private void setupBancaiSpinner(Chanpin chanpin, Zujian zujian) { List<Bancai> bancais = new ArrayList<>(); for (Chanpin_Zujian cz : chanpin.getChanpinZujian()) { if (cz.getZujian().getId().equals(zujian.getId())) { bancais.add(cz.getBancai()); } } Adapter.setupBancaiSpinners(spinnerBancai, bancais, getContext()); spinnerBancai.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedBancai = (Bancai) parent.getItemAtPosition(position); Log.d(TAG, "Selected bancai: " + (selectedBancai != null ? selectedBancai.TableText() : "null")); } @Override public void onNothingSelected(AdapterView<?> parent) { selectedBancai = null; } }); // 设置默认选中项 if (!bancais.isEmpty()) { spinnerBancai.setSelection(0); selectedBancai = bancais.get(0); // 确保默认选中第一项 } } /** 添加库存记录 */ private void addInventoryRecord() { // 验证必填项 if (selectedDingdan == null || selectedChanpin == null || selectedZujian == null || selectedBancai == null) { Log.d("addInventoryRecord","selectedDingdan:"+selectedDingdan); Log.d("addInventoryRecord","selectedChanpin:"+selectedChanpin); Log.d("addInventoryRecord","selectedZujian:"+selectedZujian); Log.d("addInventoryRecord","selectedBancai:"+selectedBancai); Toast.makeText(getContext(), "请选择订单、产品、组件和板材", Toast.LENGTH_SHORT).show(); return; } String shuliangStr = etShuliang.getText().toString().trim(); if (shuliangStr.isEmpty()) { Toast.makeText(getContext(), "请输入数量", Toast.LENGTH_SHORT).show(); return; } try { int shuliang = Integer.parseInt(shuliangStr); boolean isJinhuo = rgType.getCheckedRadioButtonId() == R.id.rb_jinhuo; User currentUser = new User(1, "当前用户", "user", "password", 1); createRecord(selectedDingdan, selectedChanpin, selectedZujian, selectedBancai, shuliang, isJinhuo, currentUser); Toast.makeText(getContext(), "记录添加成功", Toast.LENGTH_SHORT).show(); etShuliang.setText(""); } catch (NumberFormatException e) { Toast.makeText(getContext(), "请输入有效的数量", Toast.LENGTH_SHORT).show(); } } /** 创建库存记录 */ private void createRecord(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, int shuliang, boolean isJinhuo, User user) { if (isJinhuo) { Jinhuo jinhuo = new Jinhuo(); jinhuo.setId(Data.jinhuos.size() + 1); jinhuo.setDingdan(dingdan); jinhuo.setChanpin(chanpin); jinhuo.setZujian(zujian); jinhuo.setBancai(bancai); jinhuo.setShuliang(shuliang); jinhuo.setDate(new Date()); jinhuo.setUser(user); Data.jinhuos.add(jinhuo); } else { shuliang = -shuliang; } updateKucun(bancai, shuliang); } /** 更新库存 */ private void updateKucun(Bancai bancai, int changeAmount) { for (Kucun k : Data.kucuns) { if (k.getBancai().getId().equals(bancai.getId())) { k.setShuliang(k.getShuliang() + changeAmount); return; } } // 创建新库存记录 Kucun newKucun = new Kucun(); newKucun.setId(Data.kucuns.size() + 1); newKucun.setBancai(bancai); newKucun.setShuliang(Math.max(changeAmount, 0)); Data.kucuns.add(newKucun); } /** 显示添加产品对话框 */ private void showAddChanpinDialog() { if (selectedDingdan == null) { Toast.makeText(getContext(), "请先选择订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("添加产品到订单"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_chanpin, null); Spinner spinnerChanpinSelection = dialogView.findViewById(R.id.spinner_chanpin_selection); // 设置产品下拉框 if (Data.chanpins.isEmpty()) { Toast.makeText(getContext(), "没有可用的产品,请先创建产品", Toast.LENGTH_SHORT).show(); return; } Adapter.setupChanpinSpinner(spinnerChanpinSelection, Data.chanpins, getContext()); builder.setView(dialogView); builder.setPositiveButton("添加", (dialog, which) -> { Chanpin selected = (Chanpin) spinnerChanpinSelection.getSelectedItem(); if (selected != null) addChanpinToDingdan(selected); }); builder.setNegativeButton("取消", null); builder.show(); } /** 添加产品到订单 */ private void addChanpinToDingdan(Chanpin chanpin) { // 检查是否已添加 for (Dingdan_Chanpin dc : selectedDingdan.getDingdanChanpin()) { if (dc.getChanpin().getId().equals(chanpin.getId())) { Toast.makeText(getContext(), "该产品已添加到订单", Toast.LENGTH_SHORT).show(); return; } } // 创建关联对象 Dingdan_Chanpin dc = new Dingdan_Chanpin(); dc.setChanpin(chanpin); dc.setDingdan(selectedDingdan); selectedDingdan.getDingdanChanpin().add(dc); // 刷新UI setupChanpinSpinner(selectedDingdan); spinnerChanpin.setSelection(selectedDingdan.getDingdanChanpin().size() - 1); Toast.makeText(getContext(), "产品添加成功", Toast.LENGTH_SHORT).show(); } /** 显示新建订单对话框 */ private void showNewDingdanDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("新建订单"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_dingdan, null); // 初始化视图 EditText etOrderNumber = dialogView.findViewById(R.id.et_order_number); EditText etOrderDate = dialogView.findViewById(R.id.et_order_date); EditText etdeliveryDate = dialogView.findViewById(R.id.et_delivery_date); Button btnPickOrderDate = dialogView.findViewById(R.id.btn_pick_order_date); Button btnPickdeliveryDate = dialogView.findViewById(R.id.btn_pick_delivery_date); // 设置默认日期 etOrderDate.setText(dateFormat.format(new Date())); etdeliveryDate.setText(dateFormat.format(new Date())); // 日期选择器 btnPickOrderDate.setOnClickListener(v -> showDatePicker(etOrderDate)); btnPickdeliveryDate.setOnClickListener(v -> showDatePicker(etdeliveryDate)); builder.setView(dialogView); builder.setPositiveButton("创建", (dialog, which) -> { String orderNumber = etOrderNumber.getText().toString().trim(); if (orderNumber.isEmpty()) { Toast.makeText(getContext(), "请输入订单号", Toast.LENGTH_SHORT).show(); return; } // 检查订单号是否重复 for (Dingdan d : Data.dingdans) { if (d.getNumber().equals(orderNumber)) { Toast.makeText(getContext(), "订单号已存在", Toast.LENGTH_SHORT).show(); return; } } try { Date orderDate = dateFormat.parse(etOrderDate.getText().toString()); Date deliveryDate = dateFormat.parse(etdeliveryDate.getText().toString()); createAndSaveDingdan(orderNumber, orderDate, deliveryDate); } catch (ParseException e) { Toast.makeText(getContext(), "日期格式错误", Toast.LENGTH_SHORT).show(); } }); builder.setNegativeButton("取消", null); builder.show(); } /** 创建并保存订单 */ private void createAndSaveDingdan(String orderNumber, Date orderDate, Date deliveryDate) { Dingdan newDingdan = new Dingdan(); newDingdan.setId(Data.dingdans.size() + 1); newDingdan.setNumber(orderNumber); newDingdan.setXiadan(orderDate); newDingdan.setJiaohuo(deliveryDate); newDingdan.setDingdanChanpin(new ArrayList<>()); Data.dingdans.add(newDingdan); // 刷新UI setupDingdanSpinner(); spinnerDingdan.setSelection(Data.dingdans.size() - 1); Toast.makeText(getContext(), "新建订单成功", Toast.LENGTH_SHORT).show(); } /** 显示日期选择器 */ private void showDatePicker(EditText targetField) { Calendar cal = Calendar.getInstance(); new DatePickerDialog(requireContext(), (view, year, month, dayOfMonth) -> { Calendar selected = Calendar.getInstance(); selected.set(year, month, dayOfMonth); targetField.setText(dateFormat.format(selected.getTime())); }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH) ).show(); } /** 显示创建组件对话框 */ private void showCreateZujianDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle("添加新组件"); View dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_create_zujian_bancai, null); // 初始化视图 Spinner etZujianName = dialogView.findViewById(R.id.et_zujian_name); Spinner spinnerbancai = dialogView.findViewById(R.id.spinner_bancai); SearchView searchBancai = dialogView.findViewById(R.id.search_bancai); EditText numberOne_howmany=dialogView.findViewById(R.id.number_one_howmany); // 设置适配器 Adapter.setupZujianSpinner(etZujianName, Data.zujians, getContext()); Adapter.setupBancaiSpinners(spinnerbancai, Data.bancais, getContext()); // 设置搜索功能 searchBancai.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { List<Bancai> filtered = new ArrayList<>(); for (Bancai b : Data.bancais) { if (b.TableText().toLowerCase().contains(newText.toLowerCase())) { filtered.add(b); } } Adapter.setupBancaiSpinners(spinnerbancai, filtered, getContext()); return true; } }); builder.setView(dialogView); builder.setPositiveButton("创建", (dialog, which) -> { Zujian zujian = (Zujian) etZujianName.getSelectedItem(); Bancai bancai = (Bancai) spinnerbancai.getSelectedItem(); String One_howmany= numberOne_howmany.getText().toString(); if (zujian == null || bancai == null) { Toast.makeText(getContext(), "请选择组件和板材", Toast.LENGTH_SHORT).show(); return; } if (One_howmany==null){ Toast.makeText(getContext(), "请选择板材生产多少组件", Toast.LENGTH_SHORT).show(); return; } // 检查是否已存在相同的组合 boolean isDuplicate = false; for (Chanpin_Zujian existing : selectedChanpin.getChanpinZujian()) { if (existing.getZujian().equals(zujian) && existing.getBancai().equals(bancai)) { isDuplicate = true; break; } } if (isDuplicate) { Toast.makeText(getContext(), "该产品和组件组合已存在,不能重复添加", Toast.LENGTH_SHORT).show(); return; } // 创建关联对象 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setZujian(zujian); cz.setBancai(bancai); cz.setChanpin(selectedChanpin); cz.setOne_howmany(Double.valueOf(One_howmany));//double类型 selectedChanpin.getChanpinZujian().add(cz); Dingdan_chanpin_zujian dz=new Dingdan_chanpin_zujian(); dz.setBancai(selectedBancai); dz.setZujian(cz); Data.Dingdan_chanpin_zujians.add(dz); // 刷新UI setupZujianSpinner(selectedChanpin); spinnerZujian.setSelection(selectedChanpin.getChanpinZujian().size() - 1); Toast.makeText(getContext(), "组件添加成功", Toast.LENGTH_SHORT).show(); }); builder.setNegativeButton("取消", null); builder.show(); } }package com.example.kucun2.ui.dingdan; import android.app.AlertDialog; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.PopupMenu; import android.widget.SearchView; import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import com.example.kucun2.MainActivity; import com.example.kucun2.R; import com.example.kucun2.View.HorizontalScrollTextView; import com.example.kucun2.entity.Bancai; import com.example.kucun2.entity.Chanpin; import com.example.kucun2.entity.Chanpin_Zujian; import com.example.kucun2.entity.Dingdan; import com.example.kucun2.entity.Dingdan_chanpin_zujian; import com.example.kucun2.entity.Dingdan_Chanpin; import com.example.kucun2.entity.Zujian; import com.example.kucun2.entity.data.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 订单展示Fragment - 用于展示订单、产品、组件和板材的四级关系数据 * * 主要功能: * 1. 从全局数据源加载订单数据 * 2. 建立订单->产品->组件->板材的四级关系映射 * 3. 提供表格展示数据 * 4. 支持按列排序 * 5. 支持多列搜索过滤 * 6. 提供行操作(查看详情、编辑、删除) */ public class OrderDisplayFragment extends Fragment { //=== 视图组件 ===// private TableLayout table; // 表格布局,用于显示数据行 private HorizontalScrollView horizontalScrollView; // 水平滚动容器,容纳表格 private SearchView searchView; // 搜索框,用于数据过滤 private Spinner columnSelector; // 列选择器,指定搜索列 private View rootView; // Fragment的根视图 //=== 数据管理 ===// private List<Object[]> allTableRowsData = new ArrayList<>(); // 所有数据行(未过滤) private List<Object[]> filteredTableRowsData = new ArrayList<>(); // 过滤后的数据行 private boolean isDataLoaded = false; // 数据加载状态标志 //=== 排序状态 ===// private int currentSortColumn = -1; // 当前排序列索引(-1表示未排序) private boolean sortAscending = true; // 排序方向(true为升序) //=== Fragment生命周期方法 ===// /** * 创建Fragment视图 * * @param inflater 布局填充器 * @param container 父容器 * @param savedInstanceState 保存的状态 * @return 创建的视图 */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 1. 填充布局 rootView = inflater.inflate(R.layout.fragment_order_display, container, false); // 2. 初始化视图组件 initViews(); // 3. 初始化列选择器 initColumnSelector(); // 4. 设置搜索功能 setupSearchFunctionality(); // 5. 数据加载逻辑 if (Data.dingdans.isEmpty()) { // 显示加载指示器 showLoadingIndicator(); // 设置数据加载监听器 setupDataLoadListener(); } else { // 填充表格数据 isDataLoaded = true; } return rootView; } /** * 销毁视图时清理资源 */ @Override public void onDestroyView() { super.onDestroyView(); // 1. 清除视图引用,防止内存泄漏 horizontalScrollView = null; table = null; rootView = null; } //=== 初始化方法 ===// /** * 初始化视图组件 */ private void initViews() { // 1. 查找视图组件 table = rootView.findViewById(R.id.orderTable); horizontalScrollView = rootView.findViewById(R.id.horizontalScrollContainer); searchView = rootView.findViewById(R.id.search_view); columnSelector = rootView.findViewById(R.id.column_selector); // 2. 添加表格表头 addTableHeader(table); } /** * 初始化列选择器(用于指定搜索列) */ private void initColumnSelector() { // 1. 创建适配器(使用资源数组) ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( requireContext(), R.array.table_headers, // 表头字符串数组资源 android.R.layout.simple_spinner_item ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // 2. 设置适配器和默认选择 columnSelector.setAdapter(adapter); columnSelector.setSelection(0); // 默认选择第一项(全部列) // 3. 设置选择监听器 columnSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { // 当列选择变化时应用搜索过滤 applySearchFilter(); } @Override public void onNothingSelected(AdapterView<?> parent) { // 无操作 } }); } /** * 设置搜索功能 */ private void setupSearchFunctionality() { // 设置搜索查询监听器 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { // 提交查询时应用过滤 applySearchFilter(); return true; } @Override public boolean onQueryTextChange(String newText) { // 文本变化时实时应用过滤 applySearchFilter(); return true; } }); } //=== 数据加载与处理 ===// /** * 填充表格数据 - 核心数据重组逻辑 * 建立订单->产品->组件->板材的四级关系映射 */ private void fillTableData() { // 1. 创建映射关系提高查询效率 Map<Integer, List<Dingdan_Chanpin>> orderProductMap = new HashMap<>(); Map<Integer, List<Chanpin_Zujian>> productComponentMap = new HashMap<>(); Map<Integer, List<Dingdan_chanpin_zujian>> componentMaterialMap = new HashMap<>(); // 2. 构建映射关系 // 2.1 订单-产品映射 for (Dingdan_Chanpin op : Data.dingdan_chanpins) { if (op != null && op.getDingdan() != null) { orderProductMap.computeIfAbsent( op.getDingdan().getId(), k -> new ArrayList<>() ).add(op); } } // 2.2 产品-组件映射 for (Chanpin_Zujian cz : Data.chanpin_zujians) { if (cz.getChanpin() != null) { productComponentMap.computeIfAbsent( cz.getChanpin().getId(), k -> new ArrayList<>() ).add(cz); } } // 2.3 组件-板材映射 for (Dingdan_chanpin_zujian dm : Data.Dingdan_chanpin_zujians) { if (dm.getZujian() != null) { componentMaterialMap.computeIfAbsent( dm.getZujian().getId(), k -> new ArrayList<>() ).add(dm); } } // 3. 重组数据(四级关系遍历) for (Dingdan order : Data.dingdans) { List<Dingdan_Chanpin> products = orderProductMap.get(order.getId()); if (products != null) { for (Dingdan_Chanpin op : products) { Chanpin product = op.getChanpin(); if (product == null) continue; List<Chanpin_Zujian> components = productComponentMap.get(product.getId()); if (components == null) continue; for (Chanpin_Zujian cz : components) { Zujian component = cz.getZujian(); if (component == null) continue; List<Dingdan_chanpin_zujian> materials = componentMaterialMap.get(component.getId()); if (materials == null) continue; for (Dingdan_chanpin_zujian dm : materials) { // 创建行数据并添加到数据集 Object[] rowData = createRowData(order, product, op, cz, dm); allTableRowsData.add(rowData); filteredTableRowsData.add(rowData); } } } } } // 4. 初始排序(不指定列,保持原始顺序) sortTableData(-1, true); } /** * 创建表格行数据对象 * * @param order 订单对象 * @param product 产品对象 * @param dingdan_chanpin 订单-产品关联对象 * @param component 产品-组件关联对象 * @param material 组件-板材关联对象 * @return 行数据数组 */ private Object[] createRowData(Dingdan order, Chanpin product, Dingdan_Chanpin dingdan_chanpin, Chanpin_Zujian component, Dingdan_chanpin_zujian material) { Bancai board = material.getBancai(); return new Object[]{ order.getNumber(), // 订单号 product.getBianhao(), // 产品编号 dingdan_chanpin.getShuliang(),// 产品数量 component.getZujian().getName(), // 组件名 board != null ? board.TableText() : "N/A", // 板材信息 Math.round(component.getOne_howmany()), // 每组件所需板材数 material.getShuliang(), // 板材订购数量 "操作" // 操作按钮 }; } //=== 表格操作 ===// /** * 排序表格数据 * * @param columnIndex 列索引(-1表示初始状态) * @param ascending 是否升序 */ private void sortTableData(int columnIndex, boolean ascending) { // 1. 更新排序状态 if (columnIndex >= 0) { if (currentSortColumn == columnIndex) { // 同一列点击切换排序方向 sortAscending = !ascending; } else { // 不同列点击设为升序 currentSortColumn = columnIndex; sortAscending = true; } } // 2. 创建自定义比较器 Comparator<Object[]> comparator = (row1, row2) -> { if (currentSortColumn < 0) return 0; // 不排序 Object val1 = row1[currentSortColumn]; Object val2 = row2[currentSortColumn]; // 2.1 处理空值 if (val1 == null && val2 == null) return 0; if (val1 == null) return -1; if (val2 == null) return 1; try { // 2.2 数值列比较(第3、6、7列) if (currentSortColumn == 2 || currentSortColumn == 5 || currentSortColumn == 6) { double num1 = Double.parseDouble(val1.toString()); double num2 = Double.parseDouble(val2.toString()); return sortAscending ? Double.compare(num1, num2) : Double.compare(num2, num1); } // 2.3 字符串列比较 else { String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } } catch (NumberFormatException e) { // 2.4 数值解析失败时按字符串比较 String str1 = val1.toString().toLowerCase(); String str2 = val2.toString().toLowerCase(); return sortAscending ? str1.compareTo(str2) : str2.compareTo(str1); } }; // 3. 执行排序或直接复制数据 if (columnIndex == -1) { // 初始状态:复制所有数据 filteredTableRowsData.clear(); filteredTableRowsData.addAll(allTableRowsData); } else { // 执行排序 Collections.sort(filteredTableRowsData, comparator); } // 4. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 刷新表格显示 * * @param dataToShow 要显示的数据集合 */ private void refreshTableWithData(Iterable<? extends Object[]> dataToShow) { // 1. 清除旧行(保留表头) removeAllRowsSafely(); // 2. 添加新行 int addedRows = 0; for (Object[] rowData : dataToShow) { addTableRow(rowData); addedRows++; } // 3. 空数据处理 if (addedRows == 0) { addEmptyTableRow(); } } /** * 添加表格行(动态创建视图) * * @param rowData 行数据数组 */ private void addTableRow(Object[] rowData) { // 1. 创建行容器 TableRow row = new TableRow(requireContext()); TableLayout.LayoutParams rowParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT ); row.setLayoutParams(rowParams); row.setMinimumHeight(dpToPx(36)); // 设置最小高度 // 2. 遍历列数据 for (int i = 0; i < rowData.length; i++) { if (i == rowData.length - 1) { // 2.1 操作列(按钮) Button actionButton = new Button(requireContext()); actionButton.setText("操作"); actionButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); actionButton.setBackgroundResource(R.drawable.btn_selector); // 自定义按钮样式 // 按钮布局参数 TableRow.LayoutParams btnParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.WRAP_CONTENT, 0.5f // 权重 ); btnParams.setMargins(dpToPx(1), dpToPx(1), dpToPx(1), dpToPx(1)); actionButton.setLayoutParams(btnParams); // 设置点击监听 actionButton.setOnClickListener(v -> handleRowAction(rowData, v)); row.addView(actionButton); } else { // 2.2 数据列(文本) HorizontalScrollTextView textView = new HorizontalScrollTextView(requireContext()); textView.setText(String.valueOf(rowData[i])); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); // 设置内边距 int padding = dpToPx(8); textView.setPadding(padding, padding/2, padding, padding); textView.setBackgroundResource(R.drawable.cell_border); // 单元格边框 // 根据内容长度设置权重(长内容占更多空间) float weight = rowData[i].toString().length() > 10 ? 2.0f : 1.0f; TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weight ); textView.setLayoutParams(colParams); row.addView(textView); } } // 3. 将行添加到表格 table.addView(row); } //=== 用户交互处理 ===// /** * 处理行操作按钮点击 * * @param rowData 行数据 * @param anchorButton 锚点按钮(用于定位弹出菜单) */ private void handleRowAction(Object[] rowData, View anchorButton) { // 1. 创建弹出菜单 PopupMenu popupMenu = new PopupMenu(requireContext(), anchorButton); popupMenu.getMenuInflater().inflate(R.menu.row_actions_menu, popupMenu.getMenu()); popupMenu.setGravity(Gravity.BOTTOM); // 菜单显示在下方 // 2. 设置菜单项点击监听 popupMenu.setOnMenuItemClickListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.action_view_details) { // 查看详情 showDetailDialog(rowData[0].toString(), rowData[1].toString()); return true; } else if (itemId == R.id.action_edit) { // 编辑行 editRowData(rowData); return true; } else if (itemId == R.id.action_delete) { // 删除行(带确认) deleteRowWithConfirm(rowData); return true; } return false; }); // 3. 显示菜单 popupMenu.show(); } /** * 删除行数据(带确认对话框) * * @param rowData 要删除的行数据 */ private void deleteRowWithConfirm(Object[] rowData) { new AlertDialog.Builder(requireContext()) .setTitle("确认删除") .setMessage("确定要删除订单 " + rowData[0] + " 吗?") .setPositiveButton("删除", (dialog, which) -> deleteRow(rowData)) .setNegativeButton("取消", null) .show(); } /** * 实际删除逻辑 * * @param rowData 要删除的行数据 */ private void deleteRow(Object[] rowData) { // 1. 从数据集中移除 allTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); filteredTableRowsData.removeIf(row -> Arrays.equals(row, rowData)); // 2. 刷新表格 refreshTableWithData(filteredTableRowsData); // 3. 显示提示 Toast.makeText(requireContext(), "已删除订单", Toast.LENGTH_SHORT).show(); } //=== 辅助方法 ===// /** * 应用搜索过滤 */ private void applySearchFilter() { // 1. 获取搜索查询和选择的列 String query = searchView.getQuery().toString().trim().toLowerCase(); int selectedColumn = columnSelector.getSelectedItemPosition(); // 2. 清空过滤数据集 filteredTableRowsData.clear(); if (query.isEmpty()) { // 3. 无查询时显示所有数据 filteredTableRowsData.addAll(allTableRowsData); } else { // 4. 有查询时过滤数据 for (Object[] row : allTableRowsData) { if (selectedColumn == 0) { // 4.1 全局搜索(所有列) for (Object cell : row) { if (cell != null && cell.toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); break; // 匹配任一列即可 } } } else if (selectedColumn > 0 && selectedColumn <= row.length) { // 4.2 特定列搜索 int colIndex = selectedColumn - 1; // 调整索引(0是"全部") if (row[colIndex] != null && row[colIndex].toString().toLowerCase().contains(query)) { filteredTableRowsData.add(row); } } } } // 5. 刷新表格显示 refreshTableWithData(filteredTableRowsData); } /** * 添加表格表头 * * @param table 表格布局 */ private void addTableHeader(TableLayout table) { // 1. 创建表头行 TableRow headerRow = new TableRow(requireContext()); headerRow.setLayoutParams(new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.WRAP_CONTENT )); headerRow.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.purple_500)); // 紫色背景 // 2. 获取表头文本和权重 String[] headers = getResources().getStringArray(R.array.table_headers); float[] weights = {1.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f}; // 各列权重 // 3. 创建表头单元格 for (int i = 0; i < headers.length; i++) { HorizontalScrollTextView headerView = new HorizontalScrollTextView(requireContext()); headerView.setText(headers[i]); headerView.setTextColor(Color.WHITE); // 白色文字 headerView.setTypeface(null, Typeface.BOLD); // 粗体 headerView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); headerView.setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8)); // 内边距 // 单元格布局参数 TableRow.LayoutParams colParams = new TableRow.LayoutParams( 0, TableRow.LayoutParams.MATCH_PARENT, weights[i] // 使用预定义权重 ); headerView.setLayoutParams(colParams); // 设置点击排序监听 final int columnIndex = i; headerView.setOnClickListener(v -> sortTableData(columnIndex, sortAscending)); headerRow.addView(headerView); } // 4. 将表头行添加到表格 table.addView(headerRow); } /** * 安全移除所有数据行(保留表头) */ private void removeAllRowsSafely() { // 确保至少保留表头行 if (table.getChildCount() > 1) { // 从后向前移除(避免索引变化问题) for (int i = table.getChildCount() - 1; i >= 1; i--) { View child = table.getChildAt(i); if (child instanceof TableRow) { // 清理行内视图资源 cleanupRowViews((TableRow) child); } table.removeViewAt(i); } } } /** * 清理行视图资源 * * @param row 表格行 */ private void cleanupRowViews(TableRow row) { for (int i = 0; i < row.getChildCount(); i++) { View view = row.getChildAt(i); if (view instanceof Button) { // 清除按钮点击监听 ((Button) view).setOnClickListener(null); } } } /** * 添加空数据提示行 */ private void addEmptyTableRow() { TableRow row = new TableRow(requireContext()); TextView emptyView = new TextView(requireContext()); emptyView.setText("暂无数据"); emptyView.setGravity(Gravity.CENTER); emptyView.setLayoutParams(new TableRow.LayoutParams( TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT )); row.addView(emptyView); table.addView(row); } /** * 显示详情对话框 * * @param orderNumber 订单号 * @param productId 产品ID */ private void showDetailDialog(String orderNumber, String productId) { new AlertDialog.Builder(requireContext()) .setTitle("订单详情") .setMessage("订单号: " + orderNumber + "\n产品ID: " + productId) .setPositiveButton("确定", null) .show(); } /** * 编辑行数据 * * @param rowData 行数据 */ private void editRowData(Object[] rowData) { // 实际项目中应实现编辑表单 Toast.makeText(requireContext(), "编辑: " + rowData[0], Toast.LENGTH_SHORT).show(); } /** * 显示加载指示器 */ private void showLoadingIndicator() { // 实际项目中应显示进度条 Toast.makeText(requireContext(), "加载中...", Toast.LENGTH_SHORT).show(); } /** * 设置数据加载监听器 */ private void setupDataLoadListener() { if (getActivity() instanceof MainActivity) { ((MainActivity) getActivity()).setOnDataLoadListener(new MainActivity.OnDataLoadListener() { @Override public void onDataLoaded() { // 在主线程更新UI requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); isDataLoaded = true; fillTableData(); }); } @Override public void onDataError() { requireActivity().runOnUiThread(() -> { hideLoadingIndicator(); Toast.makeText(getContext(), "数据加载失败", Toast.LENGTH_SHORT).show(); }); } }); } } /** * 隐藏加载指示器 */ private void hideLoadingIndicator() { // 实际项目中应隐藏进度条 } /** * dp转px工具方法 * * @param dp 设备无关像素值 * @return 像素值 */ private int dpToPx(int dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } }package com.example.kucun2.entity.data; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import com.example.kucun2.entity.*; import com.example.kucun2.function.MyAppFnction; import com.example.kucun2.function.SafeLogger; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.*; import java.util.*; import java.util.stream.Collectors; import okhttp3.*; /** * 核心数据管理类 * 负责: * 1. 声明所有实体类的静态存储列表 * 2. 从服务器加载全量数据 * 3. 管理实体间的关联关系 * 4. 处理数据同步状态 */ public class Data { // ====================== 静态数据列表声明 ====================== public static SynchronizedList<Bancai> bancais = new SynchronizedList<>(Bancai.class); public static SynchronizedList<Caizhi> caizhis = new SynchronizedList<>(Caizhi.class); public static SynchronizedList<Mupi> mupis = new SynchronizedList<>(Mupi.class); public static SynchronizedList<Chanpin> chanpins = new SynchronizedList<>(Chanpin.class); public static SynchronizedList<Chanpin_Zujian> chanpin_zujians = new SynchronizedList<>(Chanpin_Zujian.class); public static SynchronizedList<Dingdan> dingdans = new SynchronizedList<>(Dingdan.class); public static SynchronizedList<Dingdan_Chanpin> dingdan_chanpins = new SynchronizedList<>(Dingdan_Chanpin.class); public static SynchronizedList<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians = new SynchronizedList<>(Dingdan_chanpin_zujian.class); public static SynchronizedList<Kucun> kucuns = new SynchronizedList<>(Kucun.class); public static SynchronizedList<Zujian> zujians = new SynchronizedList<>(Zujian.class); public static SynchronizedList<User> users = new SynchronizedList<>(User.class); public static SynchronizedList<Jinhuo> jinhuos = new SynchronizedList<>(Jinhuo.class); private static User user; // 实体类型与列表的映射表 <实体类, 对应的同步列表> public static final Map<Class, SynchronizedList<SynchronizableEntity>> dataCollectionMap = new HashMap<>(); private static final Gson gson = GsonFactory.createWithIdSerialization(); private static OkHttpClient client; private static final String TAG = "DataLoader"; // 静态初始化:建立实体类型与列表的映射关系 static { try { // 通过反射获取所有SynchronizedList字段 for (Field field : Data.class.getDeclaredFields()) { if (SynchronizedList.class.equals(field.getType())) { SynchronizedList<?> list = (SynchronizedList<?>) field.get(null); if (list != null) { // 将实体类型与列表关联 dataCollectionMap.put(list.getEntityType(), (SynchronizedList<SynchronizableEntity>) list); } } } } catch (IllegalAccessException e) { throw new RuntimeException("初始化dataCollectionMap失败", e); } } // ====================== 核心数据加载方法 ====================== /** * 从服务器加载全量数据 * @param context Android上下文 * @param callback 加载结果回调 */ public static void loadAllData(Context context, LoadDataCallback callback) { // 主线程检查 if (Looper.myLooper() != Looper.getMainLooper()) { throw new IllegalStateException("必须在主线程调用Data.loadAllData"); } ensurePreservedObjects(); // 确保存在预置对象 // 使用传入的 Context 获取主线程 Handler Handler mainHandler = new Handler(context.getMainLooper()); // 确保使用安全的客户端 if (client == null) { client = MyAppFnction.getClient(); } SynchronizableEntity.setSyncEnabled(false); String url = MyAppFnction.getStringResource("string", "url") + MyAppFnction.getStringResource("string", "url_all"); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, "Failed to load data", e); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); } @Override public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { Log.e(TAG, "Unexpected response code: " + response.code()); SynchronizableEntity.setSyncEnabled(true); mainHandler.post(() -> { if (callback != null) callback.onFailure(); }); return; } String responseData = response.body().string(); SynchronizableEntity.setSyncEnabled(true); ensurePreservedObjects(); // 在主线程处理解析 mainHandler.post(() -> { parseAndAssignData(responseData, context, callback); }); } }); } // ====================== 数据处理私有方法 ====================== /** * 解析JSON数据并更新到实体列表 */ private static void parseAndAssignData(String jsonData, Context context, LoadDataCallback callback) { try { // 解析顶层响应结构 Type responseType = new TypeToken<Information<AllDataResponse>>() {}.getType(); Information<AllDataResponse> info = gson.fromJson(jsonData, responseType); // 验证响应有效性 if (info == null || info.getData() == null || info.getStatus() != 200) { throw new IllegalStateException("无效服务器响应"); } AllDataResponse allData = info.getData(); SafeLogger.d("data", "原始数据: " + gson.toJson(allData)); // 更新所有数据列表 updateAllLists(allData); automaticAssociation(); // 建立实体关联 setAllEntitiesState(SynchronizableEntity.SyncState.MODIFIED); // 设置状态 safeCallback(callback, true); // 成功回调 } catch (Exception e) { Log.e(TAG, "数据处理异常: " + e.getMessage()); safeCallback(callback, false); } finally { SynchronizableEntity.setSyncEnabled(true); // 重新启用同步 } } /** * 批量更新所有实体列表 */ private static void updateAllLists(AllDataResponse allData) { updateList(bancais, allData.bancais); updateList(caizhis, allData.caizhis); updateList(mupis, allData.mupis); updateList(chanpins, allData.chanpins); updateList(chanpin_zujians, allData.chanpin_zujians); updateList(dingdans, allData.dingdans); updateList(dingdan_chanpins, allData.dingdan_chanpins); updateList(Dingdan_chanpin_zujians, allData.Dingdan_chanpin_zujians); updateList(kucuns, allData.kucuns); updateList(zujians, allData.zujians); updateList(users, allData.users); updateList(jinhuos, allData.jinhuos); } /** * 安全更新单个列表(保留预置对象) */ private static <T extends SynchronizableEntity> void updateList( List<T> existingList, List<T> newList ) { if (newList == null) return; // 保留现有列表中的预置对象 List<T> preservedItems = existingList.stream() .filter(item -> item != null && item.isPreservedObject()) .collect(Collectors.toList()); // 清空后重新添加(预置对象 + 有效新数据) existingList.clear(); existingList.addAll(preservedItems); existingList.addAll(newList.stream() .filter(item -> item != null && item.getId() != null && item.getId() != -1) .collect(Collectors.toList()) ); } /** * 确保每个列表都有预置对象(用于表示空值/默认值) */ private static void ensurePreservedObjects() { // 为每个实体类型检查并添加预置对象 addIfMissing(bancais, Bancai.class); addIfMissing(caizhis, Caizhi.class); addIfMissing(mupis, Mupi.class); addIfMissing(chanpins, Chanpin.class); addIfMissing(chanpin_zujians, Chanpin_Zujian.class); addIfMissing(dingdans, Dingdan.class); addIfMissing(kucuns, Kucun.class); addIfMissing(zujians, Zujian.class); addIfMissing(Dingdan_chanpin_zujians, Dingdan_chanpin_zujian.class); addIfMissing(dingdan_chanpins, Dingdan_Chanpin.class); addIfMissing(jinhuos, Jinhuo.class); addIfMissing(users, User.class); } private static <T extends SynchronizableEntity> void addIfMissing( List<T> list, Class<T> clazz ) { if (!containsPreservedObject(list)) { list.add(createInstance(clazz)); } } /** * 检查列表是否包含预置对象 * * @param list 目标实体列表 * @return 是否包含预置对象 */ private static <T extends SynchronizableEntity> boolean containsPreservedObject(List<T> list) { return list.stream().anyMatch(item -> item != null && item.isPreservedObject() ); } /** * 自动建立实体间关联关系(通过反射实现) */ private static void automaticAssociation() { for (Class<?> entityClass : dataCollectionMap.keySet()) { try { associateEntities(dataCollectionMap.get(entityClass)); } catch (Exception e) { Log.e(TAG, entityClass.getSimpleName() + " 关联失败", e); } } } private static <T extends SynchronizableEntity> void associateEntities( SynchronizedList<T> list ) throws IllegalAccessException, ClassNotFoundException { for (T entity : list) { if (entity == null) continue; for (Field field : entity.getClass().getDeclaredFields()) { field.setAccessible(true); Class<?> fieldType = field.getType(); // 处理实体引用字段 if (SynchronizableEntity.class.isAssignableFrom(fieldType)) { associateSingleReference(entity, field); } // 处理实体列表字段 else if (List.class.isAssignableFrom(fieldType)) { associateReferenceList(entity, field); } // 处理基础类型字段 else { setDefaultPrimitiveValue(entity, field); } } } } // ====================== 关联辅助方法 ====================== private static void associateSingleReference( SynchronizableEntity entity, Field field ) throws IllegalAccessException { SynchronizableEntity ref = (SynchronizableEntity) field.get(entity); Class<?> targetType = field.getType(); // 查找目标实体 SynchronizableEntity target = findTargetEntity(ref, targetType); field.set(entity, target); } private static void associateReferenceList( SynchronizableEntity entity, Field field ) throws IllegalAccessException, ClassNotFoundException { // 获取列表泛型类型 Type genericType = field.getGenericType(); if (!(genericType instanceof ParameterizedType)) return; Class<?> itemType = Class.forName( ((ParameterizedType) genericType).getActualTypeArguments()[0].getTypeName() ); // 只处理实体列表 if (!SynchronizableEntity.class.isAssignableFrom(itemType)) return; List<SynchronizableEntity> refList = (List<SynchronizableEntity>) field.get(entity); if (refList == null) { refList = new ArrayList<>(); field.set(entity, refList); } // 清理空值并重建引用 refList.removeAll(Collections.singleton(null)); for (int i = 0; i < refList.size(); i++) { refList.set(i, findTargetEntity(refList.get(i), itemType)); } } /** * 查找关联实体(优先匹配ID,找不到则使用预置对象) */ private static SynchronizableEntity findTargetEntity( SynchronizableEntity ref, Class<?> targetType ) { SynchronizedList<SynchronizableEntity> targetList = dataCollectionMap.get(targetType); if (targetList == null) return null; // 无效引用时返回预置对象 if (ref == null || ref.getId() == null || ref.getId() < 0) { return targetList.stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } // 按ID查找目标实体 return targetList.stream() .filter(e -> ref.getId().equals(e.getId())) .findFirst() .orElseGet(() -> targetList.stream() // 找不到时回退到预置对象 .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null) ); } // ====================== 工具方法 ====================== /** * 创建带默认值的实体实例(用作预置对象) */ public static <T> T createInstance(Class<T> clazz) { try { T instance = clazz.getDeclaredConstructor().newInstance(); // 设置基础字段默认值 for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); setDefaultFieldValue(instance, field); } // 设置特殊字段 clazz.getMethod("setId", Integer.class).invoke(instance, -1); clazz.getMethod("setState", SynchronizableEntity.SyncState.class) .invoke(instance, SynchronizableEntity.SyncState.PRESERVED); return instance; } catch (Exception e) { Log.e("Data", "创建实例失败: " + clazz.getName(), e); try { return clazz.newInstance(); // 回退创建 } catch (Exception ex) { throw new RuntimeException("无法创建实例", ex); } } } private static <T> void setDefaultFieldValue(T instance, Field field) throws IllegalAccessException { Class<?> type = field.getType(); if (type == String.class) field.set(instance, "无"); else if (type == Integer.class || type == int.class) field.set(instance, -1); else if (type == Double.class || type == double.class) field.set(instance, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(instance, false); else if (type == Date.class) field.set(instance, new Date()); else if (SynchronizableEntity.class.isAssignableFrom(type)) { field.set(instance, getPreservedEntity((Class<?>) type)); } else if (List.class.isAssignableFrom(type)) { field.set(instance, new ArrayList<>()); } } private static SynchronizableEntity getPreservedEntity(Class<?> type) { return dataCollectionMap.get(type).stream() .filter(SynchronizableEntity::isPreservedObject) .findFirst().orElse(null); } private static void setDefaultPrimitiveValue( SynchronizableEntity entity, Field field ) throws IllegalAccessException { if (field.get(entity) != null) return; Class<?> type = field.getType(); if (type == String.class) field.set(entity, "无"); else if (type == Integer.class || type == int.class) field.set(entity, -1); else if (type == Double.class || type == double.class) field.set(entity, -1.0); else if (type == Boolean.class || type == boolean.class) field.set(entity, false); else if (type == Date.class) field.set(entity, new Date()); } /** * 主线程安全回调 */ private static void safeCallback(LoadDataCallback callback, boolean success) { new Handler(Looper.getMainLooper()).post(() -> { if (callback == null) return; if (success) callback.onSuccess(); else callback.onFailure(); }); } /** * 设置所有实体同步状态 */ private static void setAllEntitiesState(SynchronizableEntity.SyncState state) { dataCollectionMap.values().forEach(list -> list.forEach(entity -> { if (entity != null) entity.setState(state); }) ); } // ====================== 内部类/接口 ====================== public interface LoadDataCallback { void onSuccess(); void onFailure(); } /** JSON响应数据结构 */ public static class AllDataResponse { public List<Bancai> bancais; public List<Caizhi> caizhis; public List<Mupi> mupis; public List<Chanpin> chanpins; public List<Chanpin_Zujian> chanpin_zujians; public List<Dingdan> dingdans; public List<Dingdan_Chanpin> dingdan_chanpins; public List<Dingdan_chanpin_zujian> Dingdan_chanpin_zujians; public List<Kucun> kucuns; public List<Zujian> zujians; public List<User> users; public List<Jinhuo> jinhuos; } }在AddInventoryFragment中新建数据,在OrderDisplayFragment中不显示
06-15
/* 公共js文件 创建时间:2025-3-21 说明:该文件包含项目中多个页面共用的功能逻辑,如登录验证、提示弹窗、全选控制、删除功能、表单验证等 */ // 定义基础接口URL,作为所有AJAX请求的前缀 let url = 'http://bas.zuniquex.cn'; // 从sessionStorage中获取登录页存储的用户账号和密码(JSON字符串转对象) let proof = JSON.parse(sessionStorage.getItem('userAccount')); let password = JSON.parse(sessionStorage.getItem('password')); // 定义标志变量,用于控制输入框事件的处理逻辑(如避免重复触发) let flag = true; // 定义全局变量editId,用于存储当前正在编辑的项的ID(如编辑表格数据时标记当前行) let editId; // 定义全局变量isValid,用于标记表单验证的合法性(true为合法) let isValid = true; // 登录状态检查:如果当前页面不是登录页,且未获取到用户信息,则提示未登录并跳转到登录页 if (!window.location.href.includes('login')) { if (proof == null) { showError("用户未登录"); setTimeout(function () { window.location.replace('./login.html'); localStorage.clear(); // 清除本地存储数据 sessionStorage.clear(); // 清除会话存储数据(避免残留登录信息) }, 1000); } } // 锁屏状态检查:如果当前页面不是锁屏页,且检测到锁屏状态(lock=1),则跳转到锁屏页 let lockVal = sessionStorage.getItem('lock'); if (!window.location.href.includes('lock')) { if (lockVal == 1) { window.location.replace('./lock.html'); } } // 错误提示函数:显示指定元素的错误信息(用于表单字段级别的错误提示) // 参数:errorId(错误提示元素的选择器)、errorMessage(错误信息内容) function errorTips(errorId, errorMessage) { $(errorId).css("display", "block"); // 显示错误提示 $(errorId).html(errorMessage); // 设置错误信息内容 } // 成功弹窗函数:显示全局成功提示弹窗,1秒后自动隐藏 function showSuccess(message) { $(".successText").html(message); // 设置成功信息内容 $("#success").css('display', 'flex'); // 显示弹窗 setTimeout(function () { $("#success").css('display', 'none'); // 1秒后隐藏 }, 1000); } // 失败弹窗函数:显示全局失败提示弹窗,1秒后自动隐藏 function showError(message) { $(".errorText").html(message); // 设置失败信息内容 $("#error").css('display', 'flex'); // 显示弹窗 setTimeout(function () { $("#error").css('display', 'none'); // 1秒后隐藏 }, 1000); } // 星星综合评分功能 let currentRating = 0; // 记录当前选中的评分(星级) $(document).ready(function () { // 鼠标悬浮星星时:临时更新星级显示(未点击确认) $('.star').on('mouseover', function () { const value = $(this).data('value'); // 获取当前星星代表的分值(如1-5) updateStars(value); // 更新星星显示状态 }); // 鼠标移出星星时:恢复为当前已确认的评分 $('.star').on('mouseout', function () { updateStars(currentRating); }); // 点击星星时:确认评分并更新当前选中状态 $('.star').on('click', function () { currentRating = $(this).data('value'); // 更新当前评分 updateStars(currentRating); // 更新星星显示 }); }); // 更新星星显示的函数:根据评分值设置星星的选中/悬浮状态 function updateStars(rating) { $('.star').each(function () { const value = $(this).data('value'); // 获取当前星星的分值 // 如果星星分值<=评分,则添加选中和悬浮类;否则移除 value <= rating ? $(this).addClass('selected').removeClass('hover') : $(this).removeClass('selected'); // 鼠标悬浮效果补充(控制hover类) value <= rating ? $(this).addClass('hover') : $(this).removeClass('hover'); }); } // 文本域计数器:实时显示输入长度,并在超过限制时变色 function updateCount() { let currentLength = $('#description').val().length; // 获取文本域当前输入长度 Count.textContent = `${currentLength}/200`; // 显示"当前长度/最大长度" // 超过200字时文字变红,否则恢复默认颜色 currentLength > 200 ? $('#Count').css('color', 'red') : $('#Count').css('color', '#505050'); } // 全局变量:存储图片链接(单图和多图) let img_url = null; // 单张图片的链接 let imgUrls = []; // 多张图片的链接数组(如轮播图) // 处理图片上传(支持单图和多图) // 参数statue:true为单图上传,false为多图上传 function upload(statue) { // 多图上传时限制最多4张 if (!statue) { if (imgUrls.length >= 4) { showError('最多只能上传4张图片'); return; // 超过数量则阻止上传 } } // 构建表单数据(根据单图/多图选择不同的表单) let pic_data = new FormData(statue ? $("#myForm")[0] : $("#myForms")[0]); // 发送AJAX上传请求 $.ajax({ url: url + '/api/common/upload', // 上传接口地址 type: 'POST', data: pic_data, dataType: 'json', cache: false, // 不缓存 processData: false, // 不对数据进行处理(FormData需保持原样) contentType: false, // 不设置Content-Type(由浏览器自动处理) success: function (res) { if (res.code == 1) { // 上传成功(假设code=1为成功) if (statue) { // 单张图片上传 img_url = res.data.url; // 存储图片链接 $("#pic").attr("src", url + img_url); // 显示上传的图片 $('#img_error').hide(); // 隐藏错误提示 isValid = true; // 标记表单验证合法 } else { // 多张图片上传 let pic_url = res.data.url; // 获取图片链接 imgUrls.push(pic_url); // 添加到数组 bannerRender(imgUrls); // 重新渲染多图展示区 // 多图至少上传2张时隐藏错误提示 if (imgUrls.length >= 2) { $('#banner_error').hide(); isValid = true; } } } else { // 上传失败处理 $('#pic').html("<p>上传失败,请重试。</p>"); } }, error: function (err) { console.error('请求失败: ', err.status, err.responseText); showError("请求失败,请稍后再试"); // 提示请求失败 } }); } // 轮播图渲染函数:根据图片数组生成HTML并显示 function bannerRender(img) { $('#imgContainer').empty(); // 清空容器 // 限制最多4张图片 if (img.length > 4) { showError('最多上传4张'); return; } let str = ''; // 拼接HTML字符串 for (let i = 0; i < img.length; i++) { // 判断图片链接是否已包含基础URL,避免重复拼接 if (img[i].includes(url)) { str += ` <div class="imgContainer"data-index="${i}"> <img src="${img[i]}" alt="" class="picture"/> <div class="closeButton" onclick="imgRemove(${i})"> <img src="./img/cha.png" alt="" class="deleteImg"/> </div> </div> `; } else { str += ` <div class="imgContainer" data-index="${i}"> <img src="${url + img[i]}" alt="" class="picture" /> <div class="closeButton" onclick="imgRemove(${i})"> <img src="./img/cha.png" alt="" class="deleteImg"/> </div> </div> `; } $('#imgContainer').append(str); // 追加到容器 str = ''; // 清空字符串,准备下一次循环 } // 清空文件输入框(避免重复上传同一张图片) $("#file").val(""); } // 移除指定索引的图片:从数组中删除并重新渲染 function imgRemove(i) { imgUrls.splice(i, 1); // 删除数组中索引为i的元素 bannerRender(imgUrls); // 重新渲染 } // 图片预览相关变量 let scale = 1; // 缩放比例(初始为1) let previewModal = document.getElementById("imagePreview"); // 预览弹窗容器 let previewImage = document.getElementById("previewImage"); // 预览图片元素 // 显示图片预览:设置图片地址并显示弹窗 function preview(imgUrl) { previewImage.src = imgUrl; // 设置预览图片的src previewModal.style.display = "flex"; // 显示弹窗 previewImage.style.transform = `scale(1)`; // 重置缩放 scale = 1; // 重置缩放比例 } // 关闭图片预览:隐藏弹窗 function previewCancel() { previewModal.style.display = "none"; } // 为预览弹窗绑定鼠标滚轮事件:控制图片缩放 if (previewModal) { previewModal.addEventListener("wheel", (event) => { event.preventDefault(); // 阻止默认行为(避免页面滚动) if (event.deltaY > 0) { scale -= 0.1; // 滚轮向下滚动:缩小 } else if (event.deltaY < 0) { scale += 0.1; // 滚轮向上滚动:放大 } // 限制缩放范围(最小0.5倍,最大3倍) scale = Math.max(0.5, Math.min(scale, 3)); // 应用缩放样式 previewImage.style.transform = `scale(${scale})`; }); } // 清空开始时间输入框 function startClear() { $('#start_time').val(''); } // 清空结束时间输入框 function endTime() { $('#end_time').val(''); } // 获取编辑器的文本内容(假设使用UE编辑器) let content; // 存储编辑器内容的变量 function getContentTxt() { content = ue.getContentTxt(); // 调用UE编辑器的方法获取纯文本内容 } // 表单验证工具:检查表单字段是否为空 var FormCheck = { // 初始化:绑定表单元素的事件 init: function () { // 获取需要验证的元素(下拉框、文本域、输入框) this.selects = document.querySelectorAll(".add_pop_select"); // 下拉框 this.textareas = document.querySelectorAll(".add_pop_textarea"); // 文本域 this.inputs = document.querySelectorAll(".add_pop_input"); // 输入框 // 绑定下拉框的change事件:值变化时验证 for (var i = 0; i < this.selects.length; i++) { this.selects[i].addEventListener("change", function () { FormCheck.checkOne(this); }); } // 绑定文本域的input事件:输入时验证 for (var j = 0; j < this.textareas.length; j++) { this.textareas[j].addEventListener("input", function () { FormCheck.checkOne(this); }); } // 绑定输入框的input事件:输入时验证 for (var k = 0; k < this.inputs.length; k++) { this.inputs[k].addEventListener("input", function () { FormCheck.checkOne(this); }); } }, // 检查单个元素:判断元素值是否为空 checkOne: function (el) { var isValid = true; // 下拉框验证:值为空则不合法 if (el.tagName === "SELECT") { isValid = el.value !== ""; } // 文本域验证:去除空格后为空则不合法 else if (el.tagName === "TEXTAREA") { isValid = el.value.trim() !== ""; } // 输入框验证:去除空格后为空则不合法 else if (el.tagName === "INPUT") { isValid = el.value.trim() !== ""; } // 显示/隐藏错误提示 this.showError(el, !isValid); return isValid; }, // 检查所有元素:验证表单中所有需要验证的字段 checkAll: function () { var allValid = true; // 获取所有需要验证的元素(下拉框、文本域、输入框) var elements = document.querySelectorAll( ".add_pop_select, .add_pop_textarea, .add_pop_input" ); // 逐个验证,只要有一个不合法则整体不合法 for (var i = 0; i < elements.length; i++) { if (!this.checkOne(elements[i])) { allValid = false; } } return allValid; }, // 显示/隐藏错误提示:根据验证结果控制错误提示的显示 showError: function (el, show) { // 查找元素的父容器(.input_pop或note_content类) var parent = el.closest(".input_pop,note_content"); if (!parent) return; // 查找父容器中的.error类元素(错误提示框) var errorBox = parent.querySelector(".error"); if (errorBox) { errorBox.style.display = show ? "block" : "none"; // 显示或隐藏 } }, }; // 页面加载完成后初始化表单验证 window.onload = function () { FormCheck.init(); }; // 清空时间输入框并触发搜索 function timeClear() { $('#start').val(''); // 清空开始时间 $('#end').val(''); // 清空结束时间 handleSearch(); // 调用搜索函数(假设该函数存在) } // 浏览量相关:初始值为1 let num = 1; // 浏览量加减函数:state为true时加1,false时减1(最小为1) function plus(state) { if (state) { num += 1; // 增加 $('#number').val(num); // 更新输入框显示 } else { if ($('#number').val() > 1) { // 确保不小于1 num -= 1; // 减少 $('#number').val(num); } } } // 库存、好评、价格的初始值(均为1) let stock = 1; // 库存 let acclaim = 1; // 好评 let priceNum = 1; // 价格 // 库存数量加减函数:state为true时加1,false时减1(最小为1) function stockChange(state) { if (state) { stock++; $("#amount").val(stock); } else { if (stock > 1) { stock--; $("#amount").val(stock); } } } // 价格数量加减函数:state为true时加1,false时减1(最小为1) function priceChange(state) { if (state) { priceNum++; $("#price").val(priceNum); } else { if (priceNum > 1) { priceNum--; $("#price").val(priceNum); } } } // 好评量数量加减函数:state为true时加1,false时减1(最小为1) function acclaimChange(state) { if (state) { acclaim++; $("#good_word").val(acclaim); } else { if (acclaim > 1) { acclaim--; $("#good_word").val(acclaim); } } } // 数量输入框事件:确保输入值不小于1 $('#price,#good_word,#number,#amount').on('input', function () { // 价格输入框:小于1则强制设为1,否则更新变量 $('#price').val() < 1 ? $('#price').val('1') : priceNum = $('#price').val(); // 好评输入框:小于1则强制设为1,否则更新变量 $('#good_word').val() < 1 ? $('#good_word').val('1') : acclaim = $('#good_word').val(); // 浏览量输入框:小于1则强制设为1,否则更新变量 $('#number').val() < 1 ? $('#number').val('1') : number = $('#number').val(); // 库存输入框:小于1则强制设为1,否则更新变量 $('#amount').val() < 1 ? $('#amount').val('1') : stock = $('#amount').val(); }) // 订单状态分类数组(按状态存储订单数据) let paymentRender = []; // 待付款订单 let accountPaid = []; // 已付款订单 let verification = []; // 已核销订单 let canceled = []; // 已取消订单 // 订单页面状态角标数量渲染:根据订单状态分类并更新角标数字 function markersRender(data) { // 清空现有分类数据 paymentRender = []; accountPaid = []; verification = []; canceled = []; // 遍历订单数据,按状态分类 for (let i in data) { if (data[i].status == 1) { // 状态1:待付款 paymentRender.push(data[i]); } else if (data[i].status == 2) { // 状态2:已付款 accountPaid.push(data[i]); } else if (data[i].status == 3) { // 状态3:已核销 verification.push(data[i]); } else if (data[i].status == 4) { // 状态4:已取消 canceled.push(data[i]); } } // 更新页面上的状态角标数字(若无数据则显示0) $(".buy_num").eq(0).html(paymentRender.length || 0); $(".payment_num").html(accountPaid.length || 0); $(".write_num").html(verification.length || 0); $(".cancelled_num").html(canceled.length || 0); } // 默认高亮"全部"状态按钮 $(".cancel").eq(0).css("background-color", "#eeeded"); // 订单状态点击事件:根据选择的状态过滤订单数据并重新渲染 function orderStatus(state) { $(".cancel").css("background-color", ""); // 重置所有按钮的背景色 // 根据状态筛选数据(使用已分类的数组) if (state == 1) { // 全部订单 $(".cancel").eq(0).css("background-color", "#eeeded"); // 高亮"全部"按钮 // 若有过滤后的数据则使用,否则使用原始分页数据 if (isFilteredDataEmpty) { return; // 数据为空时不重新渲染 } else { data = filteredData.length == 0 ? pageData : filteredData; } } else if (state == 2) { // 待付款 $(".not_buy").css("background-color", "#eeeded"); // 高亮对应按钮 data = paymentRender; } else if (state == 3) { // 已付款 $(".payment").css("background-color", "#eeeded"); data = accountPaid; } else if (state == 4) { // 已核销 $(".write_off").css("background-color", "#eeeded"); data = verification; } else { // 已取消 $(".cancelled").css("background-color", "#eeeded"); data = canceled; } // 渲染筛选后的数据并初始化分页 render(data); // 假设render函数用于渲染订单列表 pages(0); // 假设pages函数用于初始化分页 } // ====================== 公共删除模块(可直接调用) ====================== const DeleteControl = (function() { // 私有配置 const config = { deleteApi: '', findItem: () => null, afterDelete: () => {}, confirmMessage: null }; // 初始化弹窗事件(绑定asd和zxc函数) function initPopupEvents() { // 取消按钮事件(对应弹窗onclick="asd()") window.asd = () => { const modal = document.querySelector('.paddle'); if (modal) modal.style.display = 'none'; }; // 确定按钮事件(对应弹窗onclick="zxc()") window.zxc = () => { const modal = document.querySelector('.paddle'); if (modal) { const id = modal.getAttribute('data-delete-id'); if (id) executeDelete(id); modal.style.display = 'none'; } }; } // 执行删除请求 function executeDelete(id) { const targetItem = config.findItem(id); if (!targetItem) return; $.ajax({ type: "POST", url: config.deleteApi, data: { account: 'admin', password: '123456', id: id }, success: (res) => { if (res.code === 1) { showSuccess('删除成功'); removeNodeFromDataSource(id); config.afterDelete(); } else { showError('删除失败:' + (res.msg || '未知错误')); } }, error: (xhr) => { console.error('删除请求失败', xhr); showError('网络错误,删除失败'); } }); } // 从数据源移除节点 function removeNodeFromDataSource(id) { const targetId = String(id); const removeNodeById = (nodes) => { if (!Array.isArray(nodes)) return []; return nodes.filter(node => { if (node.children) node.children = removeNodeById(node.children); return String(node.id) !== targetId; }); }; // 更新缓存数据 if (window.treeDataCache) { window.treeDataCache = removeNodeById([...window.treeDataCache]); } if (window.originalTreeData) { window.originalTreeData = removeNodeById([...window.originalTreeData]); } // 刷新分页 refreshPagination(); } // 刷新分页 function refreshPagination() { if (!window.pagination) return; window.pagination = { ...window.pagination, currentPage: 1, totalRecords: window.treeDataCache?.length || 0, totalPages: Math.ceil((window.treeDataCache?.length || 0) / window.pagination.pageSize), currentData: [], cache: {} }; $('#pagination').empty(); if (window.initPaginationControl) initPaginationControl(); if (window.loadPageData) loadPageData(1); } // 公共API return { /** * 初始化删除模块 * @param {Object} options 配置参数 * @param {string} options.deleteApi 删除接口地址 * @param {Function} options.findItem 根据ID查找数据的函数 * @param {Function} options.afterDelete 删除后的回调函数 * @param {Function} options.confirmMessage 自定义确认消息函数 */ init(options) { // 合并配置 Object.assign(config, options); // 初始化弹窗事件 initPopupEvents(); console.log('删除模块初始化完成'); }, /** * 触发删除流程(直接调用此方法) * @param {string|number} id 要删除的数据ID * @param {Event} event 事件对象(用于阻止冒泡) */ deleteItem(id, event) { event?.stopPropagation(); const targetItem = config.findItem(id); if (!targetItem) { showError('目标数据不存在'); return; } // 生成确认消息 const confirmMsg = config.confirmMessage ? config.confirmMessage(targetItem) : `确定要删除「${targetItem.name}」吗?`; // 显示弹窗 const modal = document.querySelector('.paddle'); if (!modal) { console.error('未找到删除弹窗(.paddle)'); return; } const hintElement = modal.querySelector('.hint h3') || document.getElementById('deleteHintText'); if (hintElement) hintElement.textContent = confirmMsg; modal.style.display = 'flex'; modal.setAttribute('data-delete-id', id); } }; })(); // 自动暴露到全局(确保在公共文件中可直接调用) window.DeleteControl = DeleteControl; const IndustryFormPlugin = { currentMode: 'add', // add/edit currentParentId: null, currentId: null, openModal(mode, id, isSecondLevel = false) { this.currentMode = mode; this.currentId = id; this.currentParentId = isSecondLevel ? id : null; const modal = document.querySelector('.over'); const title = modal?.querySelector('.pops_title p'); const input = document.getElementById('industryNameInput'); const errorTip = document.getElementById('nameErrorTip'); // 重置表单(增加空值判断) if (input) input.value = ''; if (errorTip) errorTip.style.display = 'none'; // 设置标题 if (title) { title.textContent = mode === 'add' ? (isSecondLevel ? '新增二级行业' : '新增一级行业') : '编辑行业'; } // 编辑模式:加载数据 if (mode === 'edit' && id) { const item = findIndustryById(id); if (item && input) { input.value = item.name || ''; } } // 显示弹窗 if (modal) modal.style.display = 'flex'; // 绑定按钮事件(优化绑定方式) this.bindFormButtons(); }, bindFormButtons() { const modal = document.querySelector('.over'); if (!modal) return; // 移除旧事件(通过事件委托优化) modal.removeEventListener('click', this.handleFormClick); modal.addEventListener('click', this.handleFormClick.bind(this)); }, // 事件委托处理表单按钮点击(减少DOM操作) handleFormClick(e) { const modal = document.querySelector('.over'); if (e.target.closest('.confirm')) { this.submitForm(); // 确认按钮 } else if (e.target.closest('.cancel')) { modal.style.display = 'none'; // 取消按钮 } }, submitForm() { const input = document.getElementById('industryNameInput'); const errorTip = document.getElementById('nameErrorTip'); const name = input?.value.trim() || ''; // 验证 if (!name) { if (errorTip) { errorTip.textContent = '请输入行业名称'; errorTip.style.display = 'block'; } return; } // 检查名称是否已存在 const nodes = window.originalTreeData || []; const existing = findIndustryByName(name, nodes, this.currentMode === 'edit' ? this.currentId : null); if (existing) { if (errorTip) { errorTip.textContent = '该行业名称已存在'; errorTip.style.display = 'block'; } return; } // 提交数据 this.submitData(name); }, submitData(name) { // 构造提交数据 const data = { account: 'admin', password: '123456', name: name }; // 编辑模式需要ID if (this.currentMode === 'edit') { data.id = this.currentId; } // 新增二级行业需要父ID else if (this.currentParentId) { data.parent_id = this.currentParentId; } // 修复:接口地址变量冲突(原代码中url与全局变量重名) const apiUrl = this.currentMode === 'add' ? url + '/fastapi/trade/add' // 全局url已定义,直接使用 : url + '/fastapi/trade/edit'; $.ajax({ type: 'POST', url: apiUrl, data: data, success: (res) => { if (res.code === 1) { showSuccess(this.currentMode === 'add' ? '新增成功' : '编辑成功'); // 关闭弹窗 const modal = document.querySelector('.over'); if (modal) modal.style.display = 'none'; // 重新加载数据(增加延迟确保DOM更新) setTimeout(resetData, 300); } else { showError((this.currentMode === 'add' ? '新增失败' : '编辑失败') + ': ' + (res.msg || '未知错误')); } }, error: (xhr) => { console.error('提交失败', xhr); showError('网络错误,操作失败'); } }); } };// AJAX 请求函数 function loadData(callback) { $.ajax({ type: "POST", url: url + '/fastapi/trade/index', headers: {}, data: { account: 'admin', password: '123456' }, success: function (res) { console.log('数据加载成功', res); if (res.code === 1) { const treeData = res.data; initNodeCheckedState(treeData); window.treeDataCache = treeData; window.originalTreeData = JSON.parse(JSON.stringify(treeData)); callback(treeData); } else { console.error('请求失败', res.msg); callback(null); showError('数据加载失败: ' + res.msg); } }, error: function (xhr) { console.error('网络错误', xhr); callback(null); showError('网络错误,无法加载数据'); } }); } // 初始化节点选中状态 function initNodeCheckedState(nodes) { if (!Array.isArray(nodes)) return; nodes.forEach(node => { node.checked = false; if (node.children?.length) { initNodeCheckedState(node.children); } }); } // 分页核心变量对象 window.pagination = { currentPage: 1, pageSize: 5, totalPages: 0, totalRecords: 0, currentData: [], cache: {}, searchKeyword: '' }; // 程序核心入口 function initPaginationData() { loadData(function (treeData) { if (treeData) { initSearchEvent(); window.pagination.totalRecords = treeData.length; window.pagination.totalPages = Math.ceil(window.pagination.totalRecords / window.pagination.pageSize); initPaginationControl(); loadPageData(1); } }); // 事件委托处理按钮点击 document.addEventListener('click', (e) => { const btn = e.target.closest('[data-action]'); if (!btn) return; e.stopPropagation(); const action = btn.getAttribute('data-action'); const id = btn.getAttribute('data-id'); switch (action) { case 'add-second': IndustryFormPlugin.openModal('add', id, true); break; case 'edit': IndustryFormPlugin.openModal('edit', id); break; case 'delete': DeleteControl.deleteItem(id, e); break; } }); } // 弹窗控制变量和函数(删除相关) let currentDeleteId = null; let deleteMode = 'single'; // 重置数据(深度重置) function resetData() { window.treeDataCache = null; window.originalTreeData = null; window.pagination = { currentPage: 1, pageSize: 5, totalPages: 0, totalRecords: 0, currentData: [], cache: {}, searchKeyword: '' }; initPaginationData(); resetSearch(); } // 重置搜索(轻量重置) function resetSearch() { const refreshImg = document.querySelector('.renovate'); if (refreshImg) { refreshImg.classList.add('rotating'); refreshImg.addEventListener('animationend', () => refreshImg.classList.remove('rotating'), { once: true }); } window.pagination.searchKeyword = ''; $('.search_box').val(''); if (window.originalTreeData) { window.treeDataCache = JSON.parse(JSON.stringify(window.originalTreeData)); initNodeCheckedState(window.treeDataCache); window.pagination.totalRecords = window.treeDataCache.length; window.pagination.totalPages = Math.ceil(window.pagination.totalRecords / window.pagination.pageSize); window.pagination.currentPage = 1; window.pagination.cache = {}; $('#pagination').empty(); initPaginationControl(); loadPageData(1); } } // 从缓存的树形数据中查找指定ID的节点 function findIndustryById(targetId) { // 优先从当前分页的缓存数据找,没有再从原始全量数据找 function findRecursive(nodes) { if (!Array.isArray(nodes)) return null; for (const node of nodes) { if (String(node.id) === String(targetId)) { return node; } if (node.children && node.children.length > 0) { const foundInChildren = findRecursive(node.children); if (foundInChildren) { return foundInChildren; } } } return null; } // 先从当前分页缓存的currentData找 const foundInCurrent = findRecursive(window.pagination.currentData); if (foundInCurrent) { return foundInCurrent; } // 再从原始全量数据找 return findRecursive(window.originalTreeData); } // 根据名称递归查找行业节点 function findIndustryByName(targetName, nodes, excludeId = null) { if (!Array.isArray(nodes)) return null; for (const node of nodes) { if (node.name.toLowerCase() === targetName.toLowerCase() && node.id !== excludeId) return node; if (node.children?.length) { const result = findIndustryByName(targetName, node.children, excludeId); if (result) return result; } } return null; } // 初始化分页控件 function initPaginationControl() { $('#pagination').empty(); const totalPages = Math.max(1, window.pagination.totalPages); new Page({ id: 'pagination', pageTotal: totalPages, pageAmount: window.pagination.pageSize, dataTotal: window.pagination.totalRecords, curPage: Math.min(window.pagination.currentPage, totalPages), pageSize: 5, showSkipInputFlag: true, getPage: (page) => loadPageData(page) }); } // 初始化搜索事件 function initSearchEvent() { $('#search').on('click', performSearch); $('.search_box').on('keypress', (e) => { if (e.which === 13) { performSearch(); } }); $('#refresh').on('click', resetSearch); $('#reset').on('click', resetData); } // 执行搜索 function performSearch() { const keyword = $('.search_box').val().trim().toLowerCase(); window.pagination.searchKeyword = keyword; if (!keyword) { showError('请输入搜索关键词'); return; } const filteredData = filterTreeData(window.originalTreeData, keyword); window.treeDataCache = filteredData; window.pagination.totalRecords = filteredData.length; window.pagination.totalPages = Math.ceil(window.pagination.totalRecords / window.pagination.pageSize); window.pagination.currentPage = 1; window.pagination.cache = {}; $('#pagination').empty(); initPaginationControl(); loadPageData(1); if (filteredData.length > 0) { showSuccess(`搜索到 ${filteredData.length} 条匹配数据`); } else { showInfo('未搜索到匹配数据'); } } // 根据关键词筛选树形结构 function filterTreeData(nodes, keyword) { if (!Array.isArray(nodes)) return []; return nodes.reduce((result, node) => { const isMatch = node.name.toLowerCase().includes(keyword) || (node.id && node.id.toString().includes(keyword)); let filteredChildren = node.children?.length ? filterTreeData(node.children, keyword) : []; if (isMatch || filteredChildren.length) { const newNode = { ...node, children: filteredChildren }; if (!isMatch && filteredChildren.length) newNode._expanded = true; result.push(newNode); } return result; }, []); } // 加载指定页数据 function loadPageData(page) { const cacheKey = `page_${page}_${window.pagination.searchKeyword}`; // 如果有缓存,直接使用缓存数据 if (window.pagination.cache[cacheKey]) { window.pagination.currentPage = page; window.pagination.currentData = window.pagination.cache[cacheKey]; renderCurrentPage(); return; } if (!window.treeDataCache) return; // 计算分页数据范围 const startIndex = (page - 1) * window.pagination.pageSize; const endIndex = startIndex + window.pagination.pageSize; const pageData = window.treeDataCache.slice(startIndex, endIndex); // 初始化展开状态 initExpandedState(pageData); // 同步选中状态(关键修复:确保分页切换时选中状态正确) syncCheckedState(pageData); // 缓存当前页数据 window.pagination.cache[cacheKey] = pageData; window.pagination.currentPage = page; window.pagination.currentData = pageData; // 渲染页面 renderCurrentPage(); } // 同步选中状态(确保与全量数据一致) function syncCheckedState(nodes) { if (!Array.isArray(nodes) || !window.treeDataCache) return; nodes.forEach(node => { const fullNode = findIndustryById(node.id); if (fullNode) { node.checked = fullNode.checked; } if (node.children) { syncCheckedState(node.children); } }); } // 渲染当前页表格 function renderCurrentPage() { const tableHtml = renderTable(window.pagination.currentData); $('#dataTable').html(tableHtml || '<tr><td colspan="3" style="text-align:center">暂无数据</td></tr>'); } // 初始化节点展开状态 function initExpandedState(nodes) { if (!Array.isArray(nodes)) return; nodes.forEach(item => { if (item._expanded === undefined) item._expanded = false; if (item.children?.length) initExpandedState(item.children); }); } // 递归生成树形结构的HTML function renderTable(nodes, level = 0) { let html = ''; if (!Array.isArray(nodes)) return html; nodes.forEach(item => { const Tag = level === 0 ? 'th' : 'td'; // 二级行业增加缩进,区分层级 const indent = level > 0 ? `<span style="margin-left: ${level * 20}px"></span>` : ''; const hasRealChildren = Array.isArray(item.children) && item.children.length > 0; const rowClass = hasRealChildren ? 'parent-row' : ''; const iconSrc = hasRealChildren ? (item._expanded ? "./img/下箭头.png" : "./img/1右箭头.png") : ""; const rotateIcon = hasRealChildren && level === 0 ? ` <img src="${iconSrc}" alt="${item._expanded ? '折叠' : '展开'}" class="rotate-icon" onclick="stopEventPropagation(event); toggleChildren(${item.id}, event)" style="width:16px; height:16px; cursor:pointer; margin-right:5px;"> ` : ''; // 操作按钮(一级和二级行业不同) let actionButtons = level === 0 ? ` <div class="stair"> <button class="stair_addition" data-id="${item.id}" data-action="add-second" onclick="stopEventPropagation(event)"> <img src="./img/加号.png" alt="新增"><p>新增二级行业</p> </button> <button class="stair_compile" data-id="${item.id}" data-action="edit" onclick="stopEventPropagation(event)"> <img src="./img/编辑.png" alt="编辑"><p>编辑</p> </button> <button class="stair_delete" data-id="${item.id}" data-action="delete" onclick="stopEventPropagation(event)"> <img src="./img/删除.png" alt="删除"><p>删除</p> </button> </div> ` : ` <div class="stair"> <button class="stair_compile" data-id="${item.id}" data-action="edit" onclick="stopEventPropagation(event)"> <img src="./img/编辑.png" alt="编辑"><p>编辑</p> </button> <button class="stair_delete" data-id="${item.id}" data-action="delete" onclick="stopEventPropagation(event)"> <img src="./img/删除.png" alt="删除"><p>删除</p> </button> </div> `; // ID列内容(复选框+ID) const idColumnContent = ` <div class="middlr_left"> <input type="checkbox" class="item-check" data-id="${item.id}" onclick="stopEventPropagation(event); CheckboxControl.updateSingle(this, ${item.id})" ${item.checked ? 'checked' : ''}> <p>${rotateIcon}${item.id}</p> </div> `; // 名称列(高亮搜索关键词) let displayName = item.name; if (window.pagination.searchKeyword) { const regex = new RegExp(`(${window.pagination.searchKeyword})`, 'gi'); displayName = item.name.replace(regex, '<span class="search-highlight">$1</span>'); } // 拼接行HTML html += `<tr class="${rowClass}" onclick="handleRowClick(${item.id}, event)"> <${Tag}>${idColumnContent}</${Tag}> <${Tag}>${indent}${displayName}</${Tag}> <${Tag} class="action-column">${actionButtons}</${Tag}> </tr>`; // 递归渲染子节点(二级行业) if (hasRealChildren && item._expanded) { html += renderTable(item.children, level + 1); } }); return html; } // 行点击事件(展开/折叠子节点) function handleRowClick(id, event) { event?.stopPropagation(); const $row = event && $(event.currentTarget); if (!$row?.hasClass('parent-row') || !window.treeDataCache) return; const toggleNode = (nodes) => { for (const node of nodes) { if (node.id == id) { node._expanded = !node._expanded; return true; } if (node.children && toggleNode(node.children)) return true; } return false; }; // 切换展开状态 !toggleNode(window.pagination.currentData) && toggleNode(window.treeDataCache); // 更新缓存 window.pagination.cache[`page_${window.pagination.currentPage}_${window.pagination.searchKeyword}`] = window.pagination.currentData; // 重新渲染 renderCurrentPage(); } // 切换子节点展开/折叠 function toggleChildren(id, event) { handleRowClick(id, event); } // 阻止事件冒泡 function stopEventPropagation(event) { if (!event) return; event.stopPropagation(); event.cancelBubble = true; } // 复选框控制组件(修复分页全选问题) const CheckboxControl = { init() { // 初始化全选功能 const selectAll = document.getElementById('selectAll'); if (selectAll) { selectAll.addEventListener('click', (e) => { this.selectAll(e.target.checked); }); } }, reset() { document.querySelectorAll('.item-check').forEach(checkbox => { checkbox.checked = false; }); if (window.treeDataCache) { initNodeCheckedState(window.treeDataCache); } const selectAll = document.getElementById('selectAll'); if (selectAll) selectAll.checked = false; }, updateSingle(checkbox, id) { const isChecked = checkbox.checked; const updateNode = (nodes) => { for (const node of nodes) { if (node.id == id) { node.checked = isChecked; return true; } if (node.children && updateNode(node.children)) return true; } return false; }; updateNode(window.treeDataCache); this.updateSelectAllStatus(); }, selectAll(checked) { // 1. 更新全量数据的选中状态 const updateAllNodes = (nodes) => { if (!Array.isArray(nodes)) return; nodes.forEach(node => { node.checked = checked; if (node.children) updateAllNodes(node.children); }); }; if (window.treeDataCache) { updateAllNodes(window.treeDataCache); } // 2. 更新所有分页缓存中的数据状态(关键修复) const cache = window.pagination.cache; for (const key in cache) { if (cache.hasOwnProperty(key)) { updateAllNodes(cache[key]); } } // 3. 更新当前页DOM复选框状态 document.querySelectorAll('.item-check').forEach(checkbox => { checkbox.checked = checked; }); // 4. 更新全选框状态 const selectAll = document.getElementById('selectAll'); if (selectAll) selectAll.checked = checked; }, updateSelectAllStatus() { const selectAll = document.getElementById('selectAll'); if (!selectAll) return; const checkboxes = document.querySelectorAll('.item-check'); let allChecked = true; let hasCheckbox = false; checkboxes.forEach(checkbox => { hasCheckbox = true; if (!checkbox.checked) allChecked = false; }); selectAll.checked = hasCheckbox ? allChecked : false; }, getCheckedIds() { const ids = []; const collectIds = (nodes) => { if (!Array.isArray(nodes)) return; nodes.forEach(node => { if (node.checked) ids.push(node.id); if (node.children) collectIds(node.children); }); }; if (window.treeDataCache) collectIds(window.treeDataCache); return ids; } }; window.onload = function () { console.log('公共JS初始化'); FormCheck.init(); if (typeof initSearchEvent === 'function') initSearchEvent(); if (typeof initPaginationData === 'function') initPaginationData(); if (typeof CheckboxControl !== 'undefined' && CheckboxControl.init) CheckboxControl.init(); const batchDeleteBtn = document.getElementById('expurgate'); if (batchDeleteBtn) { batchDeleteBtn.addEventListener('click', () => { const checkedIds = CheckboxControl.getCheckedIds(); if (checkedIds.length === 0) { showError('请先选择要删除的行业'); return; } openDeleteModal('batch'); }); } const addFirstLevelBtn = document.getElementById('addition'); if (addFirstLevelBtn) { addFirstLevelBtn.addEventListener('click', () => { IndustryFormPlugin.openModal('add', null, false); }); } // 初始化删除模块 DeleteControl.init({ deleteApi: url + '/fastapi/trade/del', // 接口地址 findItem: findIndustryById, // 复用现有查找函数 afterDelete: function() { // 删除成功后执行(如刷新列表、重置选择状态) CheckboxControl.reset(); loadPageData(window.pagination.currentPage); }, confirmMessage: function(item) { // 自定义确认消息(可选) return item.children?.length ? `删除「${item.name}」将同时删除其${item.children.length}个子分类,确定继续?` : `确定删除「${item.name}」吗?`; } }); console.log('系统初始化完成'); }; 按照我的逻辑将删除添加修复好
08-08
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值