From 7a4ea8acfbee159bdc70ff79c258a10449209d1f Mon Sep 17 00:00:00 2001 From: dxc Date: Wed, 28 Jan 2026 08:54:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E7=A1=80=E4=BF=A1=E6=81=AF=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E9=94=99=E8=AF=AF=EF=BC=8C=E6=9C=AA=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inventory-backend/app/api/v1/inbound/base.py | 96 ++++++ inventory-backend/app/api/v1/stocks.py | 106 ------ inventory-backend/app/models/material.py | 60 +++- .../app/services/inbound/base_service.py | 139 ++++++++ .../app/services/stock_service.py | 119 ------- inventory-backend/run.py | 4 + inventory-web/src/api/material.ts | 0 inventory-web/src/api/material_base.ts | 44 +++ inventory-web/src/router/index.ts | 6 +- inventory-web/src/views/dashboard/index.vue | 2 +- inventory-web/src/views/material/list.vue | 312 ++++++++++++++++-- 11 files changed, 624 insertions(+), 264 deletions(-) create mode 100644 inventory-backend/app/api/v1/inbound/base.py delete mode 100644 inventory-backend/app/api/v1/stocks.py create mode 100644 inventory-backend/app/services/inbound/base_service.py delete mode 100644 inventory-backend/app/services/stock_service.py delete mode 100644 inventory-web/src/api/material.ts create mode 100644 inventory-web/src/api/material_base.ts diff --git a/inventory-backend/app/api/v1/inbound/base.py b/inventory-backend/app/api/v1/inbound/base.py new file mode 100644 index 0000000..6eba4d9 --- /dev/null +++ b/inventory-backend/app/api/v1/inbound/base.py @@ -0,0 +1,96 @@ +from flask import Blueprint, request, jsonify +# 修改为这一行,指向 app/services/inbound/base_service.py +from app.services.inbound.base_service import MaterialBaseService +import traceback + +# 定义蓝图 +# name='inbound_base' 确保全局唯一,防止和其他蓝图重名 +inbound_base_bp = Blueprint('inbound_base', __name__) + + +# ------------------------------------------------------------------ +# 1. 获取基础信息列表 (GET) +# 路由: /api/v1/inbound/base/list +# ------------------------------------------------------------------ +@inbound_base_bp.route('/list', methods=['GET']) +def get_list(): + try: + # 获取分页参数 + page = request.args.get('pageNum', 1, type=int) + limit = request.args.get('pageSize', 10, type=int) + + # 获取筛选参数 + filters = { + "keyword": request.args.get('keyword'), + "category": request.args.get('category'), + "type": request.args.get('type'), + "isEnabled": request.args.get('isEnabled') + } + + # 调用 Service 层逻辑 + result = MaterialBaseService.get_list(page, limit, filters) + + return jsonify({ + "code": 200, + "msg": "success", + "data": result + }) + except Exception as e: + traceback.print_exc() + return jsonify({"code": 500, "msg": str(e)}), 500 + + +# ------------------------------------------------------------------ +# 2. 新增基础信息 (POST) +# 路由: /api/v1/inbound/base/ +# ------------------------------------------------------------------ +@inbound_base_bp.route('/', methods=['POST']) +def add_material(): + try: + data = request.get_json() + if not data: + return jsonify({"code": 400, "msg": "No data provided"}), 400 + + MaterialBaseService.create_material(data) + return jsonify({"code": 200, "msg": "新增成功"}) + except ValueError as ve: + # 捕获业务逻辑验证错误(如名称重复) + return jsonify({"code": 400, "msg": str(ve)}), 400 + except Exception as e: + traceback.print_exc() + return jsonify({"code": 500, "msg": "系统错误"}), 500 + + +# ------------------------------------------------------------------ +# 3. 修改基础信息 (PUT) +# 路由: /api/v1/inbound/base/ +# ------------------------------------------------------------------ +@inbound_base_bp.route('/', methods=['PUT']) +def update_material(): + try: + data = request.get_json() + if not data or not data.get('id'): + return jsonify({"code": 400, "msg": "ID不能为空"}), 400 + + MaterialBaseService.update_material(data.get('id'), data) + return jsonify({"code": 200, "msg": "更新成功"}) + except Exception as e: + traceback.print_exc() + return jsonify({"code": 500, "msg": str(e)}), 500 + + +# ------------------------------------------------------------------ +# 4. 删除基础信息 (DELETE) +# 路由: /api/v1/inbound/base/ +# ------------------------------------------------------------------ +@inbound_base_bp.route('/', methods=['DELETE']) +def delete_material(id): + try: + MaterialBaseService.delete_material(id) + return jsonify({"code": 200, "msg": "删除成功"}) + except ValueError as ve: + # 捕获依赖检查错误(如已被库存引用) + return jsonify({"code": 400, "msg": str(ve)}), 400 + except Exception as e: + traceback.print_exc() + return jsonify({"code": 500, "msg": str(e)}), 500 \ No newline at end of file diff --git a/inventory-backend/app/api/v1/stocks.py b/inventory-backend/app/api/v1/stocks.py deleted file mode 100644 index 939fa75..0000000 --- a/inventory-backend/app/api/v1/stocks.py +++ /dev/null @@ -1,106 +0,0 @@ -from flask import Blueprint, request, jsonify - -# 确保这两个引用路径是存在的,如果报错说明文件没建好 -try: - from app.services.stock_service import StockService - from app.schemas.stock_schema import stock_buy_schema -except ImportError as e: - # 如果服务还没写好,这里会打印错误,防止整个后端起不来 - print(f"❌ 导入服务出错: {e}") - StockService = None - stock_buy_schema = None - -stocks_bp = Blueprint('stocks', __name__) - - -# ------------------------------------------------------------------ -# 1. 获取入库列表 -# URL: /api/v1/stocks/inbound (GET) -# ------------------------------------------------------------------ -@stocks_bp.route('/inbound', methods=['GET']) -def get_inbound_list(): - if not StockService: - return jsonify({'code': 500, 'msg': '后端服务未初始化'}), 500 - - try: - page = request.args.get('page', 1, type=int) - limit = request.args.get('pageSize', 10, type=int) - - # 调用 Service 层获取数据 - result = StockService.get_list(page, limit) - - return jsonify({ - 'code': 200, - 'msg': 'success', - 'data': result - }) - except Exception as e: - print(f"获取列表报错: {e}") - return jsonify({'code': 500, 'msg': '服务器内部错误'}), 500 - - -# ------------------------------------------------------------------ -# 2. 新增入库单 -# URL: /api/v1/stocks/inbound (POST) -# ------------------------------------------------------------------ -@stocks_bp.route('/inbound', methods=['POST']) -def create_inbound(): - if not StockService: - return jsonify({'code': 500, 'msg': '后端服务未初始化'}), 500 - - json_data = request.get_json() - if not json_data: - return jsonify({'code': 400, 'msg': '没有接收到数据'}), 400 - - try: - # 1. 参数校验 (Marshmallow Schema) - data = stock_buy_schema.load(json_data) - - # 2. 调用业务逻辑 - new_stock = StockService.create_inbound(data) - - # 3. 返回成功 - # 注意:确保 new_stock 对象有 to_dict() 方法,否则这里会报错 - return jsonify({ - 'code': 200, - 'msg': '入库成功', - 'data': new_stock.to_dict() if hasattr(new_stock, 'to_dict') else str(new_stock) - }), 201 - - except Exception as e: - # 捕获校验错误或数据库错误 - print(f"入库报错: {e}") - return jsonify({'code': 400, 'msg': str(e)}), 400 - - -# ------------------------------------------------------------------ -# 3. 更新入库单 -# URL: /api/v1/stocks/inbound/ (PUT) -# ------------------------------------------------------------------ -@stocks_bp.route('/inbound/', methods=['PUT']) -def update_inbound(id): - if not StockService: - return jsonify({'code': 500, 'msg': '后端服务未初始化'}), 500 - - json_data = request.get_json() - try: - StockService.update_inbound(id, json_data) - return jsonify({'code': 200, 'msg': '更新成功'}) - except Exception as e: - return jsonify({'code': 400, 'msg': str(e)}), 400 - - -# ------------------------------------------------------------------ -# 4. 删除入库单 -# URL: /api/v1/stocks/inbound/ (DELETE) -# ------------------------------------------------------------------ -@stocks_bp.route('/inbound/', methods=['DELETE']) -def delete_inbound(id): - if not StockService: - return jsonify({'code': 500, 'msg': '后端服务未初始化'}), 500 - - try: - StockService.delete_inbound(id) - return jsonify({'code': 200, 'msg': '删除成功'}) - except Exception as e: - return jsonify({'code': 400, 'msg': str(e)}), 400 \ No newline at end of file diff --git a/inventory-backend/app/models/material.py b/inventory-backend/app/models/material.py index 07f7a63..b1cd976 100644 --- a/inventory-backend/app/models/material.py +++ b/inventory-backend/app/models/material.py @@ -1,32 +1,62 @@ -#material.py +# app/models/material.py from app.extensions import db from datetime import datetime + class MaterialBase(db.Model): + """ + 基础信息表模型 + 对应数据库表: material_base + """ __tablename__ = 'material_base' id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(255), nullable=False) # 名称 - category = db.Column(db.String(100)) # 类别 - material_type = db.Column(db.String(100)) # 类型 - spec_model = db.Column(db.String(255)) # 规格型号 - unit = db.Column(db.String(50)) # 计量单位 - visibility_level = db.Column(db.Integer, default=0) # 信息可见等级 - manual_link = db.Column(db.Text) # 通用说明书 - product_image = db.Column(db.Text) # 通用产品图 - is_enabled = db.Column(db.Boolean, default=True) # 是否启用 + name = db.Column(db.String(255), nullable=False, comment='基础信息名称') + category = db.Column(db.String(100), comment='类别') # 例如: 采购件, 自制件 + material_type = db.Column(db.String(100), comment='类型') # 例如: 电子料, 结构件 (对应前端 type) + spec_model = db.Column(db.String(255), comment='规格型号') # (对应前端 spec) + unit = db.Column(db.String(50), comment='计量单位') + + # 根据你提供的代码,可见等级设为 Integer,默认为 0 + visibility_level = db.Column(db.Integer, default=0, comment='信息可见等级') + + manual_link = db.Column(db.Text, comment='通用说明书链接') # (对应前端 generalManual) + product_image = db.Column(db.Text, comment='通用产品图链接') # (对应前端 generalImage) + + is_enabled = db.Column(db.Boolean, default=True, comment='是否启用') + + # 时间字段(建议加上,用于排序或记录) + create_time = db.Column(db.DateTime, default=datetime.utcnow) + update_time = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # 【核心关联】 - # 这里定义了反向关系,lazy='dynamic' 允许我们后续做 count() 查询 - # cascade='all, delete-orphan' 并不是在这里用的,因为我们是手动控制逻辑 + # 关联采购库存表,lazy='dynamic' 允许使用 .count() 等查询方法 stock_buys = db.relationship('StockBuy', back_populates='material', lazy='dynamic') def to_dict(self): + """ + 序列化方法:将模型转换为字典,供API返回JSON使用 + 此处进行了字段名的映射,以适配 list.vue 前端的 prop 属性 + """ return { 'id': self.id, 'name': self.name, 'category': self.category, - 'material_type': self.material_type, - 'spec_model': self.spec_model, - 'unit': self.unit + + # --- 字段映射区域 (后端字段 -> 前端字段) --- + 'type': self.material_type, # 前端 prop="type" + 'spec': self.spec_model, # 前端 prop="spec" + 'unit': self.unit, + + # 转为驼峰命名,适配前端习惯 + 'visibilityLevel': self.visibility_level, + 'generalManual': self.manual_link, + 'generalImage': self.product_image, + + # Element Plus Switch 组件通常接受 1/0 或 true/false + # 这里转为 1/0 方便前端 el-switch :active-value="1" :inactive-value="0" + 'isEnabled': 1 if self.is_enabled else 0, + + # 补充时间信息 + 'createTime': self.create_time.strftime('%Y-%m-%d %H:%M:%S') if self.create_time else None } \ No newline at end of file diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py new file mode 100644 index 0000000..2711cca --- /dev/null +++ b/inventory-backend/app/services/inbound/base_service.py @@ -0,0 +1,139 @@ +from app.extensions import db +from app.models.material import MaterialBase +from app.models.stock import StockBuy # 需要引入库存表做删除时的依赖检查 +from sqlalchemy import or_ +import traceback + + +class MaterialBaseService: + @staticmethod + def get_list(page, limit, filters=None): + """ + 获取基础信息列表 + :param page: 页码 + :param limit: 每页条数 + :param filters: 筛选条件字典 {keyword, category, type, isEnabled} + """ + try: + query = MaterialBase.query + + if filters: + # 1. 关键词模糊搜索 (名称 或 规格型号) + if filters.get('keyword'): + kw = f"%{filters['keyword']}%" + query = query.filter(or_( + MaterialBase.name.like(kw), + MaterialBase.spec_model.like(kw) + )) + + # 2. 精确筛选 + if filters.get('category'): + query = query.filter_by(category=filters['category']) + + if filters.get('type'): + query = query.filter_by(material_type=filters['type']) + + if filters.get('isEnabled') is not None: + # 前端传 1/0,转为 Boolean + is_active = bool(int(filters['isEnabled'])) + query = query.filter_by(is_enabled=is_active) + + # 按 ID 倒序排列 + pagination = query.order_by(MaterialBase.id.desc()).paginate(page=page, per_page=limit, error_out=False) + + items = [item.to_dict() for item in pagination.items] + return {"total": pagination.total, "items": items} + + except Exception as e: + print(f"查询基础信息列表失败: {e}") + # 生产环境建议记录日志 + return {"total": 0, "items": []} + + @staticmethod + def create_material(data): + """新增基础信息""" + try: + # 0. 基础校验 + if not data.get('name') or not data.get('spec'): + raise ValueError("名称和规格型号不能为空") + + # 1. 查重 (名称+规格型号 唯一) + exist = MaterialBase.query.filter_by( + name=data['name'], + spec_model=data['spec'] + ).first() + if exist: + raise ValueError(f"已存在相同名称和规格的数据 (ID: {exist.id})") + + # 2. 创建对象 (注意前端驼峰 -> 后端下划线映射) + new_material = MaterialBase( + name=data['name'], + spec_model=data['spec'], # 映射 + category=data.get('category'), + material_type=data.get('type'), # 映射 + unit=data.get('unit'), + visibility_level=data.get('visibilityLevel'), # 映射 + manual_link=data.get('generalManual'), # 映射 + product_image=data.get('generalImage'), # 映射 + is_enabled=True if data.get('isEnabled', 1) == 1 else False + ) + + db.session.add(new_material) + db.session.commit() + return new_material + + except Exception as e: + db.session.rollback() + raise e + + @staticmethod + def update_material(m_id, data): + """修改基础信息""" + try: + material = MaterialBase.query.get(m_id) + if not material: + raise ValueError("数据不存在") + + # 更新字段 (仅更新传入的字段) + if 'name' in data: material.name = data['name'] + if 'spec' in data: material.spec_model = data['spec'] + if 'category' in data: material.category = data['category'] + if 'type' in data: material.material_type = data['type'] + if 'unit' in data: material.unit = data['unit'] + if 'visibilityLevel' in data: material.visibility_level = data['visibilityLevel'] + if 'generalManual' in data: material.manual_link = data['generalManual'] + if 'generalImage' in data: material.product_image = data['generalImage'] + + if 'isEnabled' in data: + material.is_enabled = bool(int(data['isEnabled'])) + + db.session.commit() + return material + + except Exception as e: + db.session.rollback() + raise e + + @staticmethod + def delete_material(m_id): + """删除基础信息 (带依赖检查)""" + try: + material = MaterialBase.query.get(m_id) + if not material: + raise ValueError("数据不存在") + + # 1. 依赖检查:如果该基础信息已经在库存表(StockBuy)中使用,禁止物理删除 + # 这里假设 StockBuy 表有一个外键或字段指向 MaterialBase (e.g., base_id) + usage_count = StockBuy.query.filter_by(base_id=m_id).count() + if usage_count > 0: + raise ValueError(f"无法删除:该基础信息已被 {usage_count} 条库存记录引用,请先清理库存或仅禁用此条目。") + + # 2. 执行删除 + db.session.delete(material) + db.session.commit() + return True + + except Exception as e: + db.session.rollback() + print(f"删除基础信息失败: {e}") + raise e \ No newline at end of file diff --git a/inventory-backend/app/services/stock_service.py b/inventory-backend/app/services/stock_service.py deleted file mode 100644 index 0bf1043..0000000 --- a/inventory-backend/app/services/stock_service.py +++ /dev/null @@ -1,119 +0,0 @@ -from app.extensions import db -from app.models.stock import StockBuy -from app.models.material import MaterialBase -from sqlalchemy.exc import SQLAlchemyError - - -class StockService: - @staticmethod - def create_inbound(data): - """ - 处理入库逻辑: - 1. 根据 SKU 查找物料。 - 2. 如果没找到,创建新物料 (MaterialBase)。 - 3. 创建入库单 (StockBuy)。 - """ - try: - sku = data.get('sku_code') - material_id = data.get('material_id') - - # --- 第一步:确定 material_id --- - - # 如果前端没传 ID,或者传了但我们想二次确认,都通过 SKU 查一遍 - existing_material = MaterialBase.query.filter_by(sku_code=sku).first() - - if existing_material: - # 场景 A: 物料已存在 -> 直接使用其 ID - material_id = existing_material.id - else: - # 场景 B: 物料不存在 -> 自动创建新物料 - if not data.get('material_name'): - raise ValueError(f"SKU [{sku}] 是新物料,必须填写【物料名称】才能入库。") - - new_material = MaterialBase( - sku_code=sku, - name=data.get('material_name'), - spec_model=data.get('spec_model'), - unit=data.get('unit'), - category=data.get('category') - ) - db.session.add(new_material) - db.session.flush() # 关键:将对象刷入暂存区,以获取自增的 ID - material_id = new_material.id - - # --- 第二步:创建入库单 --- - - qty = data.get('qty_inbound') - price = data.get('price_unit', 0) - - new_stock = StockBuy( - material_id=material_id, - inbound_date=data.get('inbound_date'), - batch_no=data.get('batch_no'), - warehouse_loc=data.get('warehouse_loc'), - supplier_name=data.get('supplier_name'), - - # 数量逻辑:初始时,当前量 = 可用量 = 入库量 - qty_inbound=qty, - qty_current=qty, - qty_available=qty, - - # 财务逻辑 - price_unit=price, - price_total=float(qty) * float(price) if qty else 0 - ) - - db.session.add(new_stock) - db.session.commit() # 统一提交事务 - - return new_stock - - except SQLAlchemyError as e: - db.session.rollback() # 数据库报错回滚 - raise e - except ValueError as e: - db.session.rollback() # 业务报错回滚 - raise e - - @staticmethod - def get_list(page, per_page): - """获取分页列表""" - pagination = StockBuy.query.order_by(StockBuy.inbound_date.desc()).paginate( - page=page, per_page=per_page, error_out=False - ) - return { - 'items': [item.to_dict() for item in pagination.items], - 'total': pagination.total, - 'pages': pagination.pages, - 'current_page': pagination.page - } - - @staticmethod - def update_inbound(stock_id, data): - """更新入库单信息 (通常不允许改物料本身,只改入库相关)""" - stock = StockBuy.query.get_or_404(stock_id) - - if 'warehouse_loc' in data: stock.warehouse_loc = data['warehouse_loc'] - if 'supplier_name' in data: stock.supplier_name = data['supplier_name'] - if 'batch_no' in data: stock.batch_no = data['batch_no'] - if 'price_unit' in data: stock.price_unit = data['price_unit'] - - # 如果修改了数量,需要级联更新当前库存 - if 'qty_inbound' in data: - old_qty = float(stock.qty_inbound) - new_qty = float(data['qty_inbound']) - diff = new_qty - old_qty - - stock.qty_inbound = new_qty - stock.qty_current = float(stock.qty_current) + diff - stock.qty_available = float(stock.qty_available) + diff - - db.session.commit() - return stock - - @staticmethod - def delete_inbound(stock_id): - """删除入库单""" - stock = StockBuy.query.get_or_404(stock_id) - db.session.delete(stock) - db.session.commit() \ No newline at end of file diff --git a/inventory-backend/run.py b/inventory-backend/run.py index 6532ae2..0b70a0c 100644 --- a/inventory-backend/run.py +++ b/inventory-backend/run.py @@ -5,4 +5,8 @@ app = create_app() if __name__ == '__main__': # debug=True 修改代码后会自动重启 + print("\n====== 当前所有注册路由 ======") + for rule in app.url_map.iter_rules(): + print(f"{rule} -> {rule.endpoint}") + print("==============================\n") app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/inventory-web/src/api/material.ts b/inventory-web/src/api/material.ts deleted file mode 100644 index e69de29..0000000 diff --git a/inventory-web/src/api/material_base.ts b/inventory-web/src/api/material_base.ts new file mode 100644 index 0000000..bc35c5f --- /dev/null +++ b/inventory-web/src/api/material_base.ts @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 1. 获取基础信息列表 +export function listMaterialBase(params: any) { + return request({ + url: '/inbound/base/list', + method: 'get', + params + }) +} + +// 2. 新增基础信息 +export function addMaterialBase(data: any) { + return request({ + url: '/inbound/base/', + method: 'post', + data + }) +} + +// 3. 修改基础信息 (包含状态启用/禁用) +export function updateMaterialBase(data: any) { + return request({ + url: '/inbound/base/', + method: 'put', + data + }) +} + +// 4. 删除基础信息 +export function delMaterialBase(id: number) { + return request({ + url: `/inbound/base/${id}`, // 注意这里是反引号,用于拼接 URL + method: 'delete' + }) +} + +// 5. 获取详情 (可选,用于编辑回显) +export function getMaterialBase(id: number) { + return request({ + url: `/inbound/base/${id}`, + method: 'get' + }) +} \ No newline at end of file diff --git a/inventory-web/src/router/index.ts b/inventory-web/src/router/index.ts index 7b307a3..8cb537b 100644 --- a/inventory-web/src/router/index.ts +++ b/inventory-web/src/router/index.ts @@ -19,7 +19,7 @@ const routes: Array = [ ] }, - // 2. 基础物料 (对应 views/material/list.vue) + // 2. 基础信息 (对应 views/material/list.vue) { path: '/material', component: Layout, @@ -28,9 +28,9 @@ const routes: Array = [ { path: 'index', name: 'MaterialBase', - // 基础物料列表 + // 基础信息列表 component: () => import('@/views/material/list.vue'), - meta: { title: '基础物料', icon: 'Box' } + meta: { title: '基础信息', icon: 'Box' } } ] }, diff --git a/inventory-web/src/views/dashboard/index.vue b/inventory-web/src/views/dashboard/index.vue index 5a73df0..ed9200b 100644 --- a/inventory-web/src/views/dashboard/index.vue +++ b/inventory-web/src/views/dashboard/index.vue @@ -20,7 +20,7 @@ - 基础物料 + 基础信息 diff --git a/inventory-web/src/views/material/list.vue b/inventory-web/src/views/material/list.vue index 82973be..9e49887 100644 --- a/inventory-web/src/views/material/list.vue +++ b/inventory-web/src/views/material/list.vue @@ -3,39 +3,311 @@
- - - - - - + + + + + - 搜索 + + + + + + + + + + + + 搜索 + 重置
- - - - - - - - \ No newline at end of file +import { ref, reactive, onMounted } from 'vue'; +import { Plus, Picture, Document } from '@element-plus/icons-vue'; +import { ElMessage, ElMessageBox } from 'element-plus'; + +// 【关键修改】引入刚才定义的 API 文件 +import { + listMaterialBase, + delMaterialBase, + updateMaterialBase +} from '@/api/material_base'; + +// --- 类型定义 --- +interface MaterialBaseVO { + id: number; + name: string; + category: string; + type: string; + spec: string; + unit: string; + visibilityLevel: number; + generalManual?: string; + generalImage?: string; + isEnabled: number; // 1 or 0 + statusLoading?: boolean; // 辅助字段 +} + +interface QueryParams { + pageNum: number; + pageSize: number; + keyword: string; + category: string; + type: string; + isEnabled?: number; +} + +// --- 响应式数据 --- +const loading = ref(false); +const total = ref(0); +const tableData = ref([]); + +const queryParams = reactive({ + pageNum: 1, + pageSize: 10, + keyword: '', + category: '', + type: '', + isEnabled: undefined +}); + +// --- 业务逻辑方法 --- + +// 获取数据 +const getList = () => { + loading.value = true; + // 调用 API 文件中的 listMaterialBase + listMaterialBase(queryParams) + .then((response: any) => { + // 我们的 request.ts 已经处理了 code!=200,这里直接拿 data + if (response && response.data) { + tableData.value = response.data.items; + total.value = response.data.total; + } else { + tableData.value = []; + total.value = 0; + } + }) + .catch((err) => { + console.error(err); + tableData.value = []; + }) + .finally(() => { + loading.value = false; + }); +}; + +// 搜索 +const handleQuery = () => { + queryParams.pageNum = 1; + getList(); +}; + +// 重置 +const resetQuery = () => { + queryParams.keyword = ''; + queryParams.category = ''; + queryParams.type = ''; + queryParams.isEnabled = undefined; + handleQuery(); +}; + +// 新增 +const handleAdd = () => { + ElMessage.info("请实现新增弹窗逻辑"); + // 逻辑:dialogVisible.value = true +}; + +// 编辑 +const handleEdit = (row: MaterialBaseVO) => { + console.log("点击编辑", row); + ElMessage.info(`准备编辑 ID: ${row.id}`); + // 逻辑:调用 getMaterialBase(row.id) 回显数据 +}; + +// 状态切换 (实时保存) +const handleStatusChange = (row: MaterialBaseVO) => { + row.statusLoading = true; + const text = row.isEnabled === 1 ? "启用" : "停用"; + + const updateData = { + id: row.id, + isEnabled: row.isEnabled + }; + + // 调用 API 文件中的 updateMaterialBase + updateMaterialBase(updateData) + .then(() => { + ElMessage.success(`已${text} "${row.name}"`); + }) + .catch(() => { + // 失败回滚 + row.isEnabled = row.isEnabled === 1 ? 0 : 1; + }) + .finally(() => { + row.statusLoading = false; + }); +}; + +// 删除 +const handleDelete = (row: MaterialBaseVO) => { + ElMessageBox.confirm( + `是否确认删除名称为 "${row.name}" 的数据项? \n如果该物料已有库存记录,删除将会被拒绝。`, + "警告", + { + confirmButtonText: "确定", + cancelButtonText: "取消", + type: "warning" + } + ).then(() => { + // 调用 API 文件中的 delMaterialBase + delMaterialBase(row.id) + .then(() => { + ElMessage.success("删除成功"); + if (tableData.value.length === 1 && queryParams.pageNum > 1) { + queryParams.pageNum--; + } + getList(); + }); + }).catch(() => { + // 取消 + }); +}; + +// 打开链接 +const openLink = (url: string) => { + if (!url) return; + window.open(url, '_blank'); +} + +// 初始化 +onMounted(() => { + getList(); +}); + + + \ No newline at end of file