From e13104e792f9b5860bf85eb07a99368d1367cad2 Mon Sep 17 00:00:00 2001 From: cjw <792430652@qq.com> Date: Sun, 15 Mar 2026 13:50:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=89=8D=E7=AB=AF=E5=A2=9E?= =?UTF-8?q?=E5=88=A0=E6=94=B9=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/inventory/customer_routes.py | 42 ++- src/api/inventory/supplier_routes.py | 42 ++- static/style.css | 252 ++++++++++++- static/vue-app.js | 542 ++++++++++++++++++++++----- 4 files changed, 777 insertions(+), 101 deletions(-) diff --git a/src/api/inventory/customer_routes.py b/src/api/inventory/customer_routes.py index 084562c..e58db87 100644 --- a/src/api/inventory/customer_routes.py +++ b/src/api/inventory/customer_routes.py @@ -4,17 +4,19 @@ 提供客户信息的管理功能,包括: - 客户列表查询(支持分页、搜索) - 创建新客户(自动生成客户编码) +- 更新客户信息 +- 删除客户(软删除) 路由前缀: /api/customers """ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Query, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from typing import Optional, List from datetime import datetime from database.database import get_db_session -from services.auth_service import get_current_active_user +from services.auth_service import get_current_active_user, get_current_admin_user from models.database import User, Customer from .schemas import CustomerCreate, CustomerResponse @@ -52,3 +54,39 @@ async def create_customer( await db_session.commit() await db_session.refresh(customer) return CustomerResponse.from_orm(customer) + + +@router.put("/{customer_id}", response_model=CustomerResponse) +async def update_customer( + customer_id: int, + customer_data: CustomerCreate, + db_session: AsyncSession = Depends(get_db_session), + current_user: User = Depends(get_current_active_user) +): + result = await db_session.execute(select(Customer).where(Customer.id == customer_id)) + customer = result.scalar_one_or_none() + if not customer: + raise HTTPException(status_code=404, detail="客户不存在") + + for key, value in customer_data.dict().items(): + setattr(customer, key, value) + + await db_session.commit() + await db_session.refresh(customer) + return CustomerResponse.from_orm(customer) + + +@router.delete("/{customer_id}") +async def delete_customer( + customer_id: int, + db_session: AsyncSession = Depends(get_db_session), + current_user: User = Depends(get_current_admin_user) +): + result = await db_session.execute(select(Customer).where(Customer.id == customer_id)) + customer = result.scalar_one_or_none() + if not customer: + raise HTTPException(status_code=404, detail="客户不存在") + + customer.is_active = False + await db_session.commit() + return {"message": "客户已删除"} diff --git a/src/api/inventory/supplier_routes.py b/src/api/inventory/supplier_routes.py index 60c0f14..9070216 100644 --- a/src/api/inventory/supplier_routes.py +++ b/src/api/inventory/supplier_routes.py @@ -4,17 +4,19 @@ 提供供应商信息的管理功能,包括: - 供应商列表查询(支持分页、搜索) - 创建新供应商(自动生成供应商编码) +- 更新供应商信息 +- 删除供应商(软删除) 路由前缀: /api/suppliers """ -from fastapi import APIRouter, Depends, Query +from fastapi import APIRouter, Depends, Query, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from typing import Optional, List from datetime import datetime from database.database import get_db_session -from services.auth_service import get_current_active_user +from services.auth_service import get_current_active_user, get_current_admin_user from models.database import User, Supplier from .schemas import SupplierCreate, SupplierResponse @@ -52,3 +54,39 @@ async def create_supplier( await db_session.commit() await db_session.refresh(supplier) return SupplierResponse.from_orm(supplier) + + +@router.put("/{supplier_id}", response_model=SupplierResponse) +async def update_supplier( + supplier_id: int, + supplier_data: SupplierCreate, + db_session: AsyncSession = Depends(get_db_session), + current_user: User = Depends(get_current_active_user) +): + result = await db_session.execute(select(Supplier).where(Supplier.id == supplier_id)) + supplier = result.scalar_one_or_none() + if not supplier: + raise HTTPException(status_code=404, detail="供应商不存在") + + for key, value in supplier_data.dict().items(): + setattr(supplier, key, value) + + await db_session.commit() + await db_session.refresh(supplier) + return SupplierResponse.from_orm(supplier) + + +@router.delete("/{supplier_id}") +async def delete_supplier( + supplier_id: int, + db_session: AsyncSession = Depends(get_db_session), + current_user: User = Depends(get_current_admin_user) +): + result = await db_session.execute(select(Supplier).where(Supplier.id == supplier_id)) + supplier = result.scalar_one_or_none() + if not supplier: + raise HTTPException(status_code=404, detail="供应商不存在") + + supplier.is_active = False + await db_session.commit() + return {"message": "供应商已删除"} diff --git a/static/style.css b/static/style.css index f0bd374..530b72b 100644 --- a/static/style.css +++ b/static/style.css @@ -1477,13 +1477,40 @@ select.form-input { } /* ================================ - 标签页 + 标签页 - 现代风格 ================================ */ .tabs { display: flex; - gap: var(--space-1); - border-bottom: 1px solid var(--border-light); + gap: var(--space-2); + background: var(--bg-secondary); + padding: var(--space-1); + border-radius: var(--radius-xl); margin-bottom: var(--space-6); + overflow-x: auto; +} + +.tab { + padding: var(--space-3) var(--space-5); + font-size: var(--text-sm); + font-weight: var(--font-medium); + color: var(--text-secondary); + background: transparent; + border: none; + border-radius: var(--radius-lg); + cursor: pointer; + transition: all var(--duration-fast) var(--ease-default); + white-space: nowrap; +} + +.tab:hover { + color: var(--text-primary); + background: var(--bg-primary); +} + +.tab.active { + color: var(--primary-600); + background: var(--bg-primary); + box-shadow: var(--shadow-sm); } .tab-item { @@ -1534,7 +1561,7 @@ select.form-input { .hidden { display: none; } /* ================================ - 统计卡片 + 统计卡片 - 现代风格 ================================ */ .stats-grid { display: grid; @@ -1543,6 +1570,13 @@ select.form-input { margin-bottom: var(--space-6); } +.dashboard-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: var(--space-5); + margin-bottom: var(--space-8); +} + .stat-card { background: var(--bg-primary); border: 1px solid var(--border-light); @@ -1550,21 +1584,33 @@ select.form-input { padding: var(--space-5); cursor: pointer; transition: all var(--duration-fast) var(--ease-default); + display: flex; + align-items: center; + gap: var(--space-4); } .stat-card:hover { - border-color: var(--primary-300); + border-color: var(--primary-200); box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.stat-card .stat-icon { + font-size: var(--text-3xl); + line-height: 1; + flex-shrink: 0; } .stat-content { - text-align: center; + flex: 1; + min-width: 0; } .stat-value { - font-size: var(--text-3xl); + font-size: var(--text-2xl); font-weight: var(--font-bold); color: var(--text-primary); + line-height: 1.2; margin-bottom: var(--space-1); } @@ -1573,6 +1619,198 @@ select.form-input { color: var(--text-tertiary); } +.stat-icon { + font-size: var(--text-2xl); + margin-bottom: var(--space-3); +} + +/* ================================ + 快速操作 - 现代风格 + ================================ */ +.quick-actions { + margin-top: var(--space-8); +} + +.section-title { + font-size: var(--text-lg); + font-weight: var(--font-semibold); + color: var(--text-primary); + margin-bottom: var(--space-4); +} + +.action-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: var(--space-4); +} + +.action-card { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-3); + padding: var(--space-6) var(--space-4); + background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 100%); + border: 1px solid var(--border-light); + border-radius: var(--radius-xl); + cursor: pointer; + transition: all var(--duration-normal) var(--ease-default); + position: relative; + overflow: hidden; +} + +.action-card::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient(90deg, var(--primary-400), var(--primary-600)); + opacity: 0; + transition: opacity var(--duration-fast) var(--ease-default); +} + +.action-card:hover { + border-color: var(--primary-200); + box-shadow: var(--shadow-lg); + transform: translateY(-2px); + background: linear-gradient(135deg, var(--primary-50) 0%, var(--bg-primary) 100%); +} + +.action-card:hover::before { + opacity: 1; +} + +.action-card:active { + transform: translateY(0); + box-shadow: var(--shadow-sm); +} + +.action-icon { + font-size: var(--text-3xl); + line-height: 1; +} + +.action-label { + font-size: var(--text-sm); + font-weight: var(--font-medium); + color: var(--text-primary); +} + +/* 不同操作按钮的颜色变体 */ +.action-card:nth-child(1):hover { + background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 100%); + border-color: var(--primary-200); +} + +.action-card:nth-child(2):hover { + background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%); + border-color: #86efac; +} + +.action-card:nth-child(3):hover { + background: linear-gradient(135deg, #fefce8 0%, #fef9c3 100%); + border-color: #fde047; +} + +.action-card:nth-child(4):hover { + background: linear-gradient(135deg, #fdf2f8 0%, #fce7f3 100%); + border-color: #f9a8d4; +} + +/* ================================ + 表格操作区 + ================================ */ +.table-header { + display: flex; + align-items: center; + justify-content: flex-end; + margin-bottom: var(--space-4); + gap: var(--space-2); +} + +.action-btns { + display: flex; + gap: var(--space-2); +} + +/* ================================ + 模态框 + ================================ */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: var(--space-4); +} + +.modal-content { + background: var(--bg-primary); + border-radius: var(--radius-xl); + width: 100%; + max-width: 480px; + max-height: 90vh; + overflow-y: auto; + box-shadow: var(--shadow-xl); +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-5) var(--space-6); + border-bottom: 1px solid var(--border-light); +} + +.modal-header h3 { + font-size: var(--text-lg); + font-weight: var(--font-semibold); + color: var(--text-primary); + margin: 0; +} + +.modal-close { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: none; + font-size: var(--text-xl); + color: var(--text-tertiary); + cursor: pointer; + border-radius: var(--radius-md); + transition: all var(--duration-fast) var(--ease-default); +} + +.modal-close:hover { + background: var(--bg-secondary); + color: var(--text-primary); +} + +.modal-body { + padding: var(--space-6); +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: var(--space-3); + padding-top: var(--space-4); + margin-top: var(--space-4); + border-top: 1px solid var(--border-light); +} + /* ================================ 用户区域 ================================ */ diff --git a/static/vue-app.js b/static/vue-app.js index 5515709..22e30e2 100644 --- a/static/vue-app.js +++ b/static/vue-app.js @@ -448,7 +448,7 @@ const HomeView = {
| SKU | -名称 | -分类 | -单位 | -成本价 | -销售价 | -
|---|---|---|---|---|---|
| {{ product.sku }} | -{{ product.name }} | -{{ product.category || '-' }} | -{{ product.unit }} | -{{ formatCurrency(product.cost_price) }} | -{{ formatCurrency(product.sale_price) }} | -
| SKU | +名称 | +分类 | +单位 | +成本价 | +销售价 | +操作 | +
|---|---|---|---|---|---|---|
| {{ product.sku }} | +{{ product.name }} | +{{ product.category || '-' }} | +{{ product.unit }} | +{{ formatCurrency(product.cost_price) }} | +{{ formatCurrency(product.sale_price) }} | +
+
+
+
+
+ |
+
| SKU | -产品 | -仓库 | -数量 | -可用 | -
|---|---|---|---|---|
| {{ item.product_sku }} | -{{ item.product_name }} | -{{ item.warehouse_name }} | -{{ item.quantity }} | -{{ item.available_quantity }} | -
| SKU | +产品 | +仓库 | +数量 | +可用 | +
|---|---|---|---|---|
| {{ item.product_sku }} | +{{ item.product_name }} | +{{ item.warehouse_name }} | +{{ item.quantity }} | +{{ item.available_quantity }} | +
| 编码 | -名称 | -联系人 | -电话 | -邮箱 | -
|---|---|---|---|---|
| {{ supplier.code }} | -{{ supplier.name }} | -{{ supplier.contact_person || '-' }} | -{{ supplier.phone || '-' }} | -{{ supplier.email || '-' }} | -
| 编码 | +名称 | +联系人 | +电话 | +邮箱 | +操作 | +
|---|---|---|---|---|---|
| {{ supplier.code }} | +{{ supplier.name }} | +{{ supplier.contact_person || '-' }} | +{{ supplier.phone || '-' }} | +{{ supplier.email || '-' }} | +
+
+
+
+
+ |
+
| 编码 | -名称 | -联系人 | -电话 | -邮箱 | -
|---|---|---|---|---|
| {{ customer.code }} | -{{ customer.name }} | -{{ customer.contact_person || '-' }} | -{{ customer.phone || '-' }} | -{{ customer.email || '-' }} | -
| 编码 | +名称 | +联系人 | +电话 | +邮箱 | +操作 | +
|---|---|---|---|---|---|
| {{ customer.code }} | +{{ customer.name }} | +{{ customer.contact_person || '-' }} | +{{ customer.phone || '-' }} | +{{ customer.email || '-' }} | +
+
+
+
+
+ |
+