diff --git a/inventory-backend/app/__init__.py b/inventory-backend/app/__init__.py index 19cdbd6..cda2019 100644 --- a/inventory-backend/app/__init__.py +++ b/inventory-backend/app/__init__.py @@ -1,7 +1,7 @@ +# 文件路径: app/__init__.py from flask import Flask from config import Config -from app.extensions import db, ma - +from app.extensions import db, migrate, cors def create_app(): app = Flask(__name__) @@ -9,18 +9,15 @@ def create_app(): # 初始化插件 db.init_app(app) - ma.init_app(app) + migrate.init_app(app, db) + cors.init_app(app) # 允许前端访问 - # 【新增关键步骤】: 显式导入 models,让 SQLAlchemy 认识所有的表 - # 必须放在 db.init_app 之后,create_all 或 蓝图注册 之前 - from app import models + # 注册蓝图 (Blueprints) + from app.api.v1.stocks import stocks_bp + app.register_blueprint(stocks_bp, url_prefix='/api/v1/stocks') - # 注册路由蓝图 - from app.api.v1.stocks import stock_bp - app.register_blueprint(stock_bp, url_prefix='/api/v1') - - # 【可选】如果你没有用 Flask-Migrate,可以用下面这句话自动建表(开发阶段) - # with app.app_context(): - # db.create_all() + # 可以在这里打印一下路由,方便调试 + print("已注册路由:") + print(app.url_map) return app \ No newline at end of file diff --git a/inventory-backend/app/api/v1/stocks.py b/inventory-backend/app/api/v1/stocks.py index 523f307..6fe6f06 100644 --- a/inventory-backend/app/api/v1/stocks.py +++ b/inventory-backend/app/api/v1/stocks.py @@ -1,34 +1,57 @@ from flask import Blueprint, request, jsonify -from app.services.stock_service import create_inbound_stock -from app.schemas.stock_schema import StockBuySchema +from app.services.stock_service import StockService +from app.schemas.stock_schema import stock_buy_schema -stock_bp = Blueprint('stocks', __name__) +stocks_bp = Blueprint('stocks', __name__) -@stock_bp.route('/buy-inbound', methods=['POST']) -def buy_inbound(): - """ - 采购入库接口 - POST /api/v1/buy-inbound - Body: { "material_id": 1, "qty_inbound": 100, "price_unit": 10.5 ... } - """ - # 1. 接收 JSON 数据 + +@stocks_bp.route('/inbound', methods=['GET']) +def get_inbound_list(): + page = request.args.get('page', 1, type=int) + limit = request.args.get('pageSize', 10, type=int) + + result = StockService.get_list(page, limit) + + return jsonify({ + 'code': 200, + 'msg': 'success', + 'data': result + }) + + +@stocks_bp.route('/inbound', methods=['POST']) +def create_inbound(): json_data = request.get_json() - if not json_data: - return jsonify({"message": "No input data provided"}), 400 - - # 2. 数据校验 - schema = StockBuySchema() try: - # 这一步只做校验,不直接生成对象,因为我们要在 Service 里手动处理逻辑 - data = schema.load(json_data, partial=True) + # 1. 参数校验 + data = stock_buy_schema.load(json_data) + # 2. 调用业务逻辑 + new_stock = StockService.create_inbound(data) + # 3. 返回成功 + return jsonify({ + 'code': 200, + 'msg': '入库成功', + 'data': new_stock.to_dict() + }), 201 except Exception as e: - return jsonify({"message": "Validation error", "errors": e.messages}), 422 + # 捕获 ValueError 或 SQLAlchemyError + return jsonify({'code': 400, 'msg': str(e)}), 400 - # 3. 调用业务逻辑 + +@stocks_bp.route('/inbound/', methods=['PUT']) +def update_inbound(id): + json_data = request.get_json() try: - new_stock = create_inbound_stock(data) - # 4. 返回成功结果 - result = schema.dump(new_stock) - return jsonify({"message": "Inbound successful", "data": result}), 201 + StockService.update_inbound(id, json_data) + return jsonify({'code': 200, 'msg': '更新成功'}) except Exception as e: - return jsonify({"message": "Internal Server Error", "error": str(e)}), 500 \ No newline at end of file + return jsonify({'code': 400, 'msg': str(e)}), 400 + + +@stocks_bp.route('/inbound/', methods=['DELETE']) +def delete_inbound(id): + 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/extensions.py b/inventory-backend/app/extensions.py index 992bd44..3ccf6f3 100644 --- a/inventory-backend/app/extensions.py +++ b/inventory-backend/app/extensions.py @@ -1,6 +1,8 @@ +# 文件路径: app/extensions.py from flask_sqlalchemy import SQLAlchemy -from flask_marshmallow import Marshmallow +from flask_migrate import Migrate +from flask_cors import CORS # 解决前后端跨域问题 -# 初始化数据库和序列化工具 db = SQLAlchemy() -ma = Marshmallow() \ No newline at end of file +migrate = Migrate() +cors = CORS() \ No newline at end of file diff --git a/inventory-backend/app/models/material.py b/inventory-backend/app/models/material.py index 0efc7c4..5eb44b8 100644 --- a/inventory-backend/app/models/material.py +++ b/inventory-backend/app/models/material.py @@ -1,11 +1,34 @@ from app.extensions import db +from datetime import datetime + class MaterialBase(db.Model): __tablename__ = 'material_base' id = db.Column(db.Integer, primary_key=True) - sku_code = db.Column(db.String(100), unique=True, nullable=False) - name = db.Column(db.String(255), nullable=False) - spec_model = db.Column(db.String(255)) - unit = db.Column(db.String(50)) - # 其他字段按需添加,入库时主要是为了外键关联 \ No newline at end of file + # 核心字段 + sku_code = db.Column(db.String(100), unique=True, nullable=False) # 唯一编码 + name = db.Column(db.String(255), nullable=False) # 名称 + spec_model = db.Column(db.String(255)) # 规格型号 + unit = db.Column(db.String(50)) # 单位 + category = db.Column(db.String(100)) # 分类 + + # 审计字段 (自动记录时间) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + # 关联关系 (让 StockBuy 可以反向找到 Material) + # 这里的 dynamic 允许在 material.stock_buys 时进行进一步过滤 + stock_buys = db.relationship('StockBuy', back_populates='material', lazy='dynamic') + + def to_dict(self): + """将对象转换为字典,方便接口返回""" + return { + 'id': self.id, + 'sku_code': self.sku_code, + 'name': self.name, + 'spec_model': self.spec_model, + 'unit': self.unit, + 'category': self.category, + 'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None + } \ No newline at end of file diff --git a/inventory-backend/app/models/stock.py b/inventory-backend/app/models/stock.py index b1ba6c3..74cac4f 100644 --- a/inventory-backend/app/models/stock.py +++ b/inventory-backend/app/models/stock.py @@ -6,20 +6,51 @@ class StockBuy(db.Model): __tablename__ = 'stock_buy' id = db.Column(db.Integer, primary_key=True) + + # 外键:必须关联一个 MaterialBase 的 ID material_id = db.Column(db.Integer, db.ForeignKey('material_base.id'), nullable=False) - inbound_date = db.Column(db.DateTime, default=datetime.now) - barcode = db.Column(db.String(100)) - batch_no = db.Column(db.String(100)) - # 数量相关 (使用 Numeric 对应数据库的 NUMERIC) - qty_inbound = db.Column(db.Numeric(19, 4), default=0) - qty_current = db.Column(db.Numeric(19, 4), default=0) - qty_available = db.Column(db.Numeric(19, 4), default=0) + # 业务数据 + inbound_date = db.Column(db.DateTime, default=datetime.utcnow) # 入库时间 + batch_no = db.Column(db.String(100)) # 批次号 + warehouse_loc = db.Column(db.String(100)) # 库位 + supplier_name = db.Column(db.String(255)) # 供应商 - price_unit = db.Column(db.Numeric(19, 4), default=0) - price_total = db.Column(db.Numeric(19, 4), default=0) - supplier_name = db.Column(db.String(255)) - warehouse_loc = db.Column(db.String(100)) + # 数量与状态 + qty_inbound = db.Column(db.Numeric(19, 4), default=0) # 初始入库量 + qty_current = db.Column(db.Numeric(19, 4), default=0) # 当前剩余量 + qty_available = db.Column(db.Numeric(19, 4), default=0) # 当前可用量 + status = db.Column(db.String(50), default='NORMAL') - # 建立关联,方便查询物料详情 - material = db.relationship('MaterialBase', backref='buy_stocks') \ No newline at end of file + # 财务数据 + price_unit = db.Column(db.Numeric(19, 4), default=0) # 单价 + price_total = db.Column(db.Numeric(19, 4), default=0) # 总价 + + # 建立与 MaterialBase 的双向关系 + material = db.relationship('MaterialBase', back_populates='stock_buys') + + def to_dict(self): + """ + 序列化方法: + 这里做了一个扁平化处理,把关联的 material 里的 name/sku 直接拿出来, + 方便前端表格直接显示,不用前端再去拼凑。 + """ + return { + 'id': self.id, + 'material_id': self.material_id, + # 从关联对象获取基础信息 + 'sku_code': self.material.sku_code if self.material else None, + 'material_name': self.material.name if self.material else None, + 'spec_model': self.material.spec_model if self.material else None, + 'unit': self.material.unit if self.material else None, + 'category': self.material.category if self.material else None, + + # 本表信息 + 'inbound_date': self.inbound_date.strftime('%Y-%m-%d %H:%M:%S') if self.inbound_date else None, + 'batch_no': self.batch_no, + 'warehouse_loc': self.warehouse_loc, + 'supplier_name': self.supplier_name, + 'qty_inbound': float(self.qty_inbound) if self.qty_inbound else 0, + 'price_unit': float(self.price_unit) if self.price_unit else 0, + 'price_total': float(self.price_total) if self.price_total else 0, + } \ No newline at end of file diff --git a/inventory-backend/app/schemas/stock_schema.py b/inventory-backend/app/schemas/stock_schema.py index 99c2eb9..ebbd8b2 100644 --- a/inventory-backend/app/schemas/stock_schema.py +++ b/inventory-backend/app/schemas/stock_schema.py @@ -1,14 +1,42 @@ -from app.extensions import ma -from app.models.stock import StockBuy -from marshmallow import fields +from marshmallow import Schema, fields, validate, validates_schema, ValidationError -class StockBuySchema(ma.SQLAlchemyAutoSchema): - class Meta: - model = StockBuy - load_instance = True # 反序列化时自动创建模型实例 - include_fk = True # 包含外键 material_id - # 必须字段校验 - material_id = fields.Integer(required=True) - qty_inbound = fields.Decimal(required=True, as_string=True) - price_unit = fields.Decimal(missing=0, as_string=True) \ No newline at end of file +class StockBuySchema(Schema): + # 只用于输出的字段 + id = fields.Int(dump_only=True) + + # --- 输入字段 --- + + # 1. 核心识别字段 + material_id = fields.Int(missing=None) # 如果是老物料,可能传ID + sku_code = fields.Str(required=True, error_messages={"required": "SKU编码是必填项"}) # 必填 + + # 2. 新物料自动建档字段 (如果是新SKU,这些需要校验) + material_name = fields.Str(missing=None) + spec_model = fields.Str(missing=None) + unit = fields.Str(missing=None) + category = fields.Str(missing=None) + + # 3. 入库业务字段 + qty_inbound = fields.Float(required=True, validate=validate.Range(min=0.0001, error="入库数量必须大于0")) + price_unit = fields.Float(missing=0) + + inbound_date = fields.DateTime(format='%Y-%m-%d %H:%M:%S') + batch_no = fields.Str(missing='') + warehouse_loc = fields.Str(missing='') + supplier_name = fields.Str(missing='') + + @validates_schema + def validate_material_logic(self, data, **kwargs): + """ + 自定义校验逻辑: + 如果用户没传 material_id,说明可能想新建物料。 + 虽然最终是否新建由 Service 层判断数据库决定, + 但这里可以做一个弱校验:尽量让用户填上名字。 + """ + pass + # 这里暂时不强制抛出错误,交给 Service 层处理 "SKU不存在且无名字" 的情况 + + +# 实例化 Schema +stock_buy_schema = StockBuySchema() \ No newline at end of file diff --git a/inventory-backend/app/services/stock_service.py b/inventory-backend/app/services/stock_service.py index 894059c..0bf1043 100644 --- a/inventory-backend/app/services/stock_service.py +++ b/inventory-backend/app/services/stock_service.py @@ -1,39 +1,119 @@ from app.extensions import db from app.models.stock import StockBuy +from app.models.material import MaterialBase from sqlalchemy.exc import SQLAlchemyError -def create_inbound_stock(data): - """ - 处理采购入库逻辑 - """ - try: - # 1. 计算总价 - qty = data.get('qty_inbound') - price = data.get('price_unit', 0) - total = float(qty) * float(price) +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') - # 2. 创建库存记录 - # 注意:入库时,当前库存(current)和可用库存(available)通常等于入库数量 - new_stock = StockBuy( - material_id=data['material_id'], - barcode=data.get('barcode'), - batch_no=data.get('batch_no'), - qty_inbound=qty, - qty_current=qty, # 初始:当前=入库 - qty_available=qty, # 初始:可用=入库 - price_unit=price, - price_total=total, - supplier_name=data.get('supplier_name'), - warehouse_loc=data.get('warehouse_loc'), - inbound_date=data.get('inbound_date') # 如果前端没传,Model会默认用当前时间 + # --- 第一步:确定 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.add(new_stock) db.session.commit() + return stock - return new_stock - - except SQLAlchemyError as e: - db.session.rollback() - raise e \ No newline at end of file + @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/config.py b/inventory-backend/config.py index 1df7e5e..51ea870 100644 --- a/inventory-backend/config.py +++ b/inventory-backend/config.py @@ -5,7 +5,7 @@ class Config: # 数据库连接配置 # 请务必将 '你的密码' 替换为你 PostgreSQL 的真实密码 # 如果数据库不在本地,请将 localhost 替换为 IP 地址 - SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:1234@localhost:5432/inventory_system' + SQLALCHEMY_DATABASE_URI = 'postgresql://test:1234@localhost:5432/inventory_system' # 关闭 SQLAlchemy 的事件追踪,减少内存消耗 SQLALCHEMY_TRACK_MODIFICATIONS = False diff --git a/inventory-backend/run.py b/inventory-backend/run.py index 7425f4c..6532ae2 100644 --- a/inventory-backend/run.py +++ b/inventory-backend/run.py @@ -1,6 +1,8 @@ +# 文件路径: run.py (在项目根目录下,与 config.py 同级) from app import create_app app = create_app() if __name__ == '__main__': + # debug=True 修改代码后会自动重启 app.run(host='0.0.0.0', port=5000, debug=True) \ No newline at end of file diff --git a/inventory-web/package.json b/inventory-web/package.json index 68a7300..323e254 100644 --- a/inventory-web/package.json +++ b/inventory-web/package.json @@ -4,7 +4,7 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "vue-tsc -b && vite build", "preview": "vite preview" }, diff --git a/inventory-web/src/App.vue b/inventory-web/src/App.vue index 58b0f21..719d1ca 100644 --- a/inventory-web/src/App.vue +++ b/inventory-web/src/App.vue @@ -1,30 +1,115 @@ - + +/* 新增:底部栏样式 */ +.app-footer { + height: 30px; /* 固定高度 */ + background-color: #e9e9eb; /* 稍微深一点的灰色,区分内容区 */ + border-top: 1px solid #dcdfe6; + display: flex; + align-items: center; + justify-content: center; /* 文字居中 */ + flex-shrink: 0; /* 防止被压缩 */ + font-size: 12px; + color: #909399; +} + +.version-tag { + display: flex; + align-items: center; + font-weight: 500; + color: #e6a23c; /* 使用橙色,表示“测试/警告”意味 */ +} + \ No newline at end of file diff --git a/inventory-web/src/api/auth.ts b/inventory-web/src/api/auth.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/api/material.ts b/inventory-web/src/api/material.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/api/stock.ts b/inventory-web/src/api/stock.ts new file mode 100644 index 0000000..b341760 --- /dev/null +++ b/inventory-web/src/api/stock.ts @@ -0,0 +1,39 @@ +import request from '@/utils/request' + +// 注意:baseURL 已经是 '/api/v1' 了,所以这里只需要写剩下的部分 + +// 获取入库列表 +// 最终请求: /api/v1 + /stocks/inbound = /api/v1/stocks/inbound +export function getInboundList(params: any) { + return request({ + url: '/stocks/inbound', // <--- 修改点:去掉了 /api/v1 + method: 'get', + params + }) +} + +// 新增入库 +export function createInbound(data: any) { + return request({ + url: '/stocks/inbound', // <--- 修改点 + method: 'post', + data + }) +} + +// 修改入库 +export function updateInbound(id: number, data: any) { + return request({ + url: `/stocks/inbound/${id}`, // <--- 修改点 + method: 'put', + data + }) +} + +// 删除入库 +export function deleteInbound(id: number) { + return request({ + url: `/stocks/inbound/${id}`, // <--- 修改点 + method: 'delete' + }) +} \ No newline at end of file diff --git a/inventory-web/src/api/transaction.ts b/inventory-web/src/api/transaction.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/assets/iris.png b/inventory-web/src/assets/iris.png new file mode 100644 index 0000000..1ec12c2 Binary files /dev/null and b/inventory-web/src/assets/iris.png differ diff --git a/inventory-web/src/assets/vue.svg b/inventory-web/src/assets/vue.svg deleted file mode 100644 index 770e9d3..0000000 --- a/inventory-web/src/assets/vue.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/inventory-web/src/layout/index.vue b/inventory-web/src/layout/index.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/layout/index.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/main.ts b/inventory-web/src/main.ts index 2425c0f..4faa621 100644 --- a/inventory-web/src/main.ts +++ b/inventory-web/src/main.ts @@ -1,5 +1,29 @@ import { createApp } from 'vue' -import './style.css' import App from './App.vue' -createApp(App).mount('#app') +// 1. 引入路由配置 (确保你已经创建了 src/router/index.ts) +import router from './router' + +// 2. 引入 Element Plus (UI组件库) +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +// 引入中文包 +import zhCn from 'element-plus/es/locale/lang/zh-cn' + +// 3. 引入图标 +import * as ElementPlusIconsVue from '@element-plus/icons-vue' + +const app = createApp(App) + +// 注册所有图标 +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +// 使用插件 +app.use(router) +app.use(ElementPlus, { + locale: zhCn, // 设置为中文 +}) + +app.mount('#app') \ No newline at end of file diff --git a/inventory-web/src/router/index.ts b/inventory-web/src/router/index.ts new file mode 100644 index 0000000..b336abd --- /dev/null +++ b/inventory-web/src/router/index.ts @@ -0,0 +1,24 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const routes = [ + // --- 修改点:根路径不再重定向,而是显示 Dashboard 首页 --- + { + path: '/', + name: 'Dashboard', + component: () => import('@/views/dashboard/index.vue') + }, + + // --- 保持原有的入库页路由不变 --- + { + path: '/stock/inbound', + name: 'StockInbound', + component: () => import('@/views/stock/inbound.vue') + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router \ No newline at end of file diff --git a/inventory-web/src/router/permission.ts b/inventory-web/src/router/permission.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/stores/tagsView.ts b/inventory-web/src/stores/tagsView.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/stores/user.vue b/inventory-web/src/stores/user.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/stores/user.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/utils/format.ts b/inventory-web/src/utils/format.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/utils/request.ts b/inventory-web/src/utils/request.ts new file mode 100644 index 0000000..98b4a23 --- /dev/null +++ b/inventory-web/src/utils/request.ts @@ -0,0 +1,47 @@ +import axios from 'axios' +import { ElMessage } from 'element-plus' + +// 1. 创建 axios 实例 +const service = axios.create({ + // 这里的 '/api' 配合 vite.config.ts 的 proxy 使用 + baseURL: '/api/v1', + timeout: 5000 // 请求超时时间 +}) + +// 2. 请求拦截器 (可以在这里加 Token) +service.interceptors.request.use( + (config) => { + // 如果以后有登录 token,就在这里加 + // const token = localStorage.getItem('token') + // if (token) { + // config.headers['Authorization'] = 'Bearer ' + token + // } + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 3. 响应拦截器 (统一处理错误) +service.interceptors.response.use( + (response) => { + const res = response.data + // 这里可以根据后端的 code 来判断 + // 假设你的后端成功返回 code: 200 + if (res.code && res.code !== 200) { + ElMessage.error(res.msg || 'Error') + return Promise.reject(new Error(res.msg || 'Error')) + } else { + return res // 直接返回数据部分 + } + }, + (error) => { + console.log('err' + error) + ElMessage.error(error.message || '请求失败') + return Promise.reject(error) + } +) + +// 4. 【关键】必须默认导出 service +export default service \ No newline at end of file diff --git a/inventory-web/src/utils/validate.ts b/inventory-web/src/utils/validate.ts new file mode 100644 index 0000000..e69de29 diff --git a/inventory-web/src/views/dashboard/index.vue b/inventory-web/src/views/dashboard/index.vue new file mode 100644 index 0000000..d516a5a --- /dev/null +++ b/inventory-web/src/views/dashboard/index.vue @@ -0,0 +1,61 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/login/index.vue b/inventory-web/src/views/login/index.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/login/index.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/material/list.vue b/inventory-web/src/views/material/list.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/material/list.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/stock/inbound.vue b/inventory-web/src/views/stock/inbound.vue new file mode 100644 index 0000000..9a07ad3 --- /dev/null +++ b/inventory-web/src/views/stock/inbound.vue @@ -0,0 +1,237 @@ + + + \ No newline at end of file diff --git a/inventory-web/src/views/stock/query.vue b/inventory-web/src/views/stock/query.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/stock/query.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/stock/semi.vue b/inventory-web/src/views/stock/semi.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/stock/semi.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/transaction/borrow.vue b/inventory-web/src/views/transaction/borrow.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/transaction/borrow.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/transaction/return.vue b/inventory-web/src/views/transaction/return.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/transaction/return.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/src/views/transaction/scrap.vue b/inventory-web/src/views/transaction/scrap.vue new file mode 100644 index 0000000..96c0baf --- /dev/null +++ b/inventory-web/src/views/transaction/scrap.vue @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/inventory-web/vite.config.ts b/inventory-web/vite.config.ts index bbcf80c..9aa989d 100644 --- a/inventory-web/vite.config.ts +++ b/inventory-web/vite.config.ts @@ -1,7 +1,23 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import { fileURLToPath, URL } from 'node:url' -// https://vite.dev/config/ export default defineConfig({ plugins: [vue()], -}) + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + }, + // --- 新增下面这一段 server 配置 --- + server: { + proxy: { + '/api': { + target: 'http://127.0.0.1:5000', // 后端的地址 + changeOrigin: true, + // 如果你的后端路径本身就包含 /api,通常不需要 rewrite + // rewrite: (path) => path.replace(/^\/api/, '') + } + } + } +}) \ No newline at end of file