2026-06-15-market-material-my-usage-plan.md 12 KB

市场物料库「我的物料领用记录」实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 为市场物料库新增「我的物料领用记录」功能,参照生产物料库的实现模式

Architecture: 采用复用模式 - 页面 = 标题 + Tab组件(mineOnly prop),复用现有 MaterialUsageTab 组件,新增后端 API /web/admin/my-material-usages

Tech Stack: Vue 3 + Element Plus (前端) | FastAPI + SQLAlchemy (后端)


任务清单

Task 1: 后端 - 新增 Pydantic Schema

Files:

  • Modify: backend/app/schemas/material.py

Steps:

  • Step 1: 读取现有 schema 文件了解结构

读取 backend/app/schemas/material.py 查看现有的 MaterialUsage 相关 schema 定义。

  • Step 2: 添加查询参数 Schema

在 material.py 中添加:

from pydantic import BaseModel, Field
from typing import Optional

class MyMaterialUsageQuery(BaseModel):
    keyword: Optional[str] = Field(None, description="关键词搜索")
    material_id: Optional[str] = Field(None, description="物料ID")
    material_type: Optional[str] = Field(None, description="物料类型")
    change_type: Optional[str] = Field(None, description="变动类型")
    start_time: Optional[str] = Field(None, description="开始日期 YYYY-MM-DD")
    end_time: Optional[str] = Field(None, description="结束日期 YYYY-MM-DD")
  • Step 3: 添加响应字段

在 MaterialUsage 或创建新的 MaterialUsageResponse 添加 requisition_status 字段:

class MaterialUsageResponse(BaseModel):
    # ... 现有字段 ...
    requisition_status: Optional[str] = Field(None, description="领用状态: open/closed")
  • Step 4: Commit
git add backend/app/schemas/material.py
git commit -m "feat(market): 新增 MyMaterialUsageQuery schema"

Task 2: 后端 - 新增 CRUD 方法

Files:

  • Modify: backend/app/crud/material.py

Steps:

  • Step 1: 读取现有 CRUD 文件

读取 backend/app/crud/material.py 查看现有的 material_usage 相关 CRUD 方法。

  • Step 2: 添加 get_my_usages 方法

在 material.py CRUD 文件中添加:

async def get_my_usages(
    db: AsyncSession,
    current_user_id: str,
    tenant_no: str,
    page: int = 1,
    page_size: int = 10,
    keyword: Optional[str] = None,
    material_id: Optional[str] = None,
    material_type: Optional[str] = None,
    change_type: Optional[str] = None,
    start_time: Optional[str] = None,
    end_time: Optional[str] = None,
) -> Tuple[List[dict], int]:
    """
    获取当前用户的物料领用记录(手动出库记录)

    Args:
        current_user_id: 当前用户ID(用于筛选领用人)
        其他筛选参数...
    Returns:
        (记录列表, 总数)
    """
    # 参考现有 material_usage 的查询逻辑
    # 额外添加条件:
    # - source_type = 'manual_out'
    # - receiver = current_user_name 或与 current_user_id 关联的领用人
    # 按 create_time DESC 排序
  • Step 3: Commit
git add backend/app/crud/material.py
git commit -m "feat(market): 新增 get_my_usages CRUD 方法"

Task 3: 后端 - 新增 API 路由

Files:

  • Create: backend/app/api/market.py
  • Modify: backend/app/api/__init__.py
  • Modify: backend/app/main.py

Steps:

  • Step 1: 创建 market.py 路由文件

创建 backend/app/api/market.py:

from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from typing import Optional

from app.core.security import get_current_user
from app.database import get_db
from app.schemas.material import MyMaterialUsageQuery
from app.crud.material import get_my_usages

router = APIRouter(prefix="/market", tags=["market"])


@router.get("/my-material-usages")
async def get_my_material_usage_list(
    page: int = Query(1, ge=1),
    page_size: int = Query(10, ge=1, le=100),
    keyword: Optional[str] = Query(None),
    material_id: Optional[str] = Query(None),
    material_type: Optional[str] = Query(None),
    change_type: Optional[str] = Query(None),
    start_time: Optional[str] = Query(None),
    end_time: Optional[str] = Query(None),
    current_user: dict = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """获取当前用户的物料领用记录"""
    # 获取当前用户信息
    user_id = current_user.get("user_id")
    user_name = current_user.get("user_name")
    tenant_no = current_user.get("tenant_no")

    records, total = await get_my_usages(
        db=db,
        current_user_id=user_id,
        user_name=user_name,
        tenant_no=tenant_no,
        page=page,
        page_size=page_size,
        keyword=keyword,
        material_id=material_id,
        material_type=material_type,
        change_type=change_type,
        start_time=start_time,
        end_time=end_time,
    )

    return {
        "list": records,
        "total": total,
        "page": page,
        "pageSize": page_size,
    }
  • Step 2: 注册路由到 init.py

在 backend/app/api/__init__.py 中添加:

from app.api.market import router as market_router

# 在 api_router 列表中添加
api_router.include_router(market_router)
  • Step 3: 确保 main.py 中调用 init.py

确认 backend/app/main.py 中的 include_router(api_router) 包含了 __init__.py 中注册的路由。

  • Step 4: Commit
git add backend/app/api/market.py backend/app/api/__init__.py backend/app/main.py
git commit -m "feat(market): 新增市场模块 API 路由"

Task 4: 前端 - 新增 API 函数

Files:

  • Modify: web-admin/src/api/material-usage.ts

Steps:

  • Step 1: 读取现有 API 文件

读取 web-admin/src/api/material-usage.ts 了解现有结构和 MaterialUsage 类型定义。

  • Step 2: 添加 fetchMyMaterialUsageList 函数

在 material-usage.ts 中添加:

export async function fetchMyMaterialUsageList(params: {
  page: number
  pageSize: number
  keyword?: string
  materialId?: string
  materialType?: string
  changeType?: string
  startTime?: string
  endTime?: string
}): Promise<{ list: MaterialUsage[]; total: number }> {
  return request.get('/web/admin/my-material-usages', { params })
}
  • Step 3: 确保 MaterialUsage 类型包含 requisitionStatus

检查 MaterialUsage 类型定义,如需要则添加:

export interface MaterialUsage {
  // ... 现有字段 ...
  requisitionStatus?: 'open' | 'closed'
}
  • Step 4: Commit
git add web-admin/src/api/material-usage.ts
git commit -m "feat(market): 新增 fetchMyMaterialUsageList API"

Task 5: 前端 - 修改 MaterialUsageTab 支持 mineOnly

Files:

  • Modify: web-admin/src/views/cell-storage/material/MaterialUsageTab.vue

Steps:

  • Step 1: 读取现有 MaterialUsageTab.vue

读取 web-admin/src/views/cell-storage/material/MaterialUsageTab.vue 了解当前实现。

  • Step 2: 添加 mineOnly prop

在 <script setup> 中添加:

const props = withDefaults(
  defineProps<{
    mineOnly?: boolean
  }>(),
  {
    mineOnly: false
  }
)
  • Step 3: 修改 loadData 函数支持 mineOnly
async function loadData() {
  loading.value = true
  try {
    const params: Record<string, any> = {
      page: pagination.page,
      pageSize: pagination.pageSize
    }
    // ... 现有参数构建 ...

    const res = props.mineOnly
      ? await fetchMyMaterialUsageList(params)
      : await fetchMaterialUsageList(params)
    tableData.value = res.list || []
    pagination.total = res.total ?? 0
  } finally {
    loading.value = false
  }
}
  • Step 4: 导入 fetchMyMaterialUsageList
import {
  fetchMaterialUsageList,
  fetchMaterialUsage,
  fetchMarkMaterialUsageFullyUsed,
  fetchMyMaterialUsageList  // 新增
} from '@/api/material-usage'
  • Step 5: 修改变动类型下拉选项(mineOnly 时不显示入库)

在模板中找到变动类型选择器:

<el-select v-model="searchForm.changeType" placeholder="全部" clearable>
  <el-option v-if="!mineOnly" label="入库" value="in" />  <!-- 修改:添加 v-if="!mineOnly" -->
  <el-option label="出库" value="out" />
  <el-option label="任务使用" value="use" />
  <el-option label="废弃" value="discard" />
</el-select>
  • Step 6: 添加使用状态列

在表格中(变动类型列之后或适当位置)添加:

<el-table-column
  v-if="mineOnly"
  label="使用状态"
  min-width="100"
  align="center"
  header-align="center"
>
  <template #default="{ row }">
    <template v-if="isManualOutRow(row)">
      <el-tag v-if="row.requisitionStatus === 'closed'" type="success" size="small">
        已使用完
      </el-tag>
      <el-tag v-else type="info" size="small">
        使用中
      </el-tag>
    </template>
    <span v-else>—</span>
  </template>
</el-table-column>
  • Step 7: 隐藏领用人列(mineOnly 时不显示)

找到领用人列,添加 v-if:

<el-table-column
  v-if="!mineOnly"  <!-- 添加此条件 -->
  label="领用人"
  ...
>
  • Step 8: Commit
git add web-admin/src/views/cell-storage/material/MaterialUsageTab.vue
git commit -m "feat(market): MaterialUsageTab 支持 mineOnly 模式"

Task 6: 前端 - 新建页面

Files:

  • Create: web-admin/src/views/cell-storage/my-material-usage/index.vue

Steps:

  • Step 1: 参考生产物料库页面创建

参考 web-admin/src/views/production/my-production-material-usage/index.vue 创建:

<!-- 市场物料库 — 我的物料领用记录 -->
<template>
  <div class="page-container">
    <section class="panel">
      <div class="panel-top">
        <h2 class="title-bar-heading">我的物料领用记录</h2>
      </div>
      <MaterialUsageTab mine-only />
    </section>
  </div>
</template>

<script setup lang="ts">
  import MaterialUsageTab from '../material/MaterialUsageTab.vue'

  defineOptions({ name: 'MarketMaterialMyUsage' })
</script>
  • Step 2: Commit
git add web-admin/src/views/cell-storage/my-material-usage/index.vue
git commit -m "feat(market): 新增我的物料领用记录页面"

Task 7: 前端 - 添加路由配置

Files:

  • Modify: web-admin/src/router/modules/sqxp.ts

Steps:

  • Step 1: 读取路由文件找到市场基础信息菜单位置

在 sqxp.ts 中找到市场物料库的路由配置,添加同级菜单项。

  • Step 2: 添加路由配置

在市场物料库路由的同级添加:

{
  path: 'my-material-usage',
  component: '/cell-storage/my-material-usage/index',
  meta: {
    title: '我的物料领用记录',
    icon: 'List',
    menuCode: 'biz:material:myUsage'
  }
}

注意:需要添加到市场基础信息菜单分组下。

  • Step 3: Commit
git add web-admin/src/router/modules/sqxp.ts
git commit -m "feat(market): 添加我的物料领用记录路由"

Task 8: 验证测试

Steps:

  • Step 1: 启动后端服务
cd backend && ./start-local.sh

或确认后端服务已运行。

  • Step 2: 启动前端服务
cd web-admin && pnpm dev
  • Step 3: 手动测试
  1. 登录系统
  2. 访问「市场基础信息 → 我的物料领用记录」
  3. 验证列表正确加载当前用户的出库记录
  4. 点击「标记已使用完」按钮
  5. 验证状态变为"已使用完"
  • Step 4: Commit 最终改动

如果测试通过,提交所有剩余改动。


验收清单

  • 后端 API /web/admin/my-material-usages 返回正确数据
  • 菜单「市场基础信息 → 我的物料领用记录」可正常访问
  • 列表仅显示当前用户的物料出库记录
  • 变动类型筛选正常(mineOnly 模式不显示入库选项)
  • 「使用状态」列正确显示"已使用完"/"使用中"
  • 点击「标记已使用完」后,状态正确更新
  • 查看详情弹窗正常显示